Deploying a GraphQL Docker Container to AWS Lambda with Waypoint

I finally got around to ticking off a few items on my todo list. 1. Shoving an express.js app into a lambda function. 2. Deploying a Docker container to lambda. 3. Using a new HashiCorp product. ...And it all required very little code. Sort of.

Kevin Wang


Just want a TL;DR? I used waypoint to build, deploy, and release a containerized GraphQL server to Lambda. waypoint builds the image with Docker, publishes it to ECR, and points an ALB at the lambda function.

Just here to see some code? Skip ahead ↓


Changelog:

Backstory

When I hear about new tech that piques my interest or old patterns that are simply new to me, I take note and add them to my list of things to try out. However, this list has grown large and I have a bad habit of not actually getting around to many of those things, because you know... work, life, and everything else.

Somedays I do get lucky and am blessed with a streak of diligence, discipline, or just that one thing that helps triggers the domino effect in checking items off.

express.js inside lambda

Ever since listening to “JavaScript Deployments” (March 2020) and learning that you can put an express.js app inside a lambda function, I’ve been curious about exactly what that looked like... I just never got around to figuring it out for myself.

Docker container inside lambda

Ever since AWS announced container image support for Lambda back in December 2020, I’ve been curious about it as well, but never got around to using it since Docker wasn’t really a tool that I used regularly.

My method to date for deploying something to lambda has been using the aws-cdk-lib/aws_lambda_nodejs package, which uploads some compiled JavaScript to a Node.js lambda function. But this is still just JavaScript and not a containerized app.

Beyond terraform...

And... Ever since joining HashiCorp in August 2021, I’ve telling myself to go learn more about all the core products. Terraform has been the only product I’ve really used and not even very in-depth. I don’t have significant infrastructure needs, and when I have, the AWS TypeScript CDK has been both sufficient as well as educational with its compiler hinting and documentation support.

The tipping point — waypoint

A week ago, I decided to hunker down and see how far I could get with trying out waypoint.

Waypoint allows developers to deploy, manage, and observe their applications through a consistent abstraction of underlying infrastructure. Waypoint works with Kubernetes, ECS and many other platforms.

I referenced the AWS Lambda Tutorial, worked around some hiccups (like image and function architecture mismatch 1) and to my surprise, I ended up ticking off the previous 3 items on my todo list. Thank you, waypoint!

The following section is essentially the whole project.

The code

The amount of code needed to get this project up and running is pretty light.

Containerized Application

The secret to running express.js on lambda is @vendia/serverless-express, which handles the mapping of a few supported AWS event objects (API Gateway, ALB, and Lambda@Edge) to and from the express.js Request and Response objects.

The GraphQL portion is handled by apollo-server-lambda, which simply wraps @vendia/serverless-express.

Dockerfile

It’s worth noting the special /var/task directory, which is where Lambda expects container code to exist, by default. 2

Application code

This is a minimal GraphQL server implementation, with one RESTDataSource to prove out external data fetching.

waypoint.hcl

waypoint, similar to terraform, uses HCL to define the resources you want.

The highlighted lines were required for me since I’m using an M1 Macbook. 1

Deploying

Deploying was more or less simple.

I did go down some rabbit holes afterwards:

  • How to resolve a DuplicateListener error 3
  • How to enable HTTPS on an Application Load Balancer

Install waypoint v0.7.1

Run the Waypoint server and run waypoint up

This requires an AWS account and credentials in ~/.aws/credentials

Waypoint should output a load balancer URL which is where the public can view the containerized app. It takes a few minutes for a brand new load balancer to be fully provisioned.

After doing some additional digging into Target Groups, Security Groups, SSL Certificates, I was able to add a pretty URL to the waypoint deployment. (I’ll try write about this endeavor in another blog post.)

...This URL may or may not work as I may tear down the deployment in the future.

Grok’ing the infrastructure

Here’s a brain dump of notes I took on the infrastucture that Waypoint created.

ECR

Waypoint builds a Docker image, and publishes it to your image registry.

Lambda

Waypoint creates or publishes a new lambda function version that uses the previous image.

EC2

Waypoint creates...

  • a Security Group with a default inbound rule, like “allow all traffic on port 80.
  • a Target Group that targets the previously created lambda function.
  • an Application Load Balancer in some VPC (?) and the previously created security group.
  • a Listener on port 80 that forwards traffic to the previously created Target Group.

Final thoughts

Waypoint is slick but has tons of room for polishing. As a developer, I feel like Waypoint resonates with me. I like being hands-on (...mostly) with every part of my stack, especially the cloud infrastructure portion. So I appreciate that Waypoint doesn’t rely on a 3rd party platform or abstraction over AWS, GCP, Azure, etc. — it’s still just you and your cloud infra.

I’ve been building and running Waypoint from source to get a feel for the codebase, as well its general architecture.

I’m also hoping to chip away at the following issue on my free time I opened a pull request, #3032, for: arm64 function support for aws-lambda deployments #3026,

I’ve written a go-script to workaround I opened another pull request, #3035, for another existing issue: Lambda DuplicateListener error when releasing a 2nd time #2066.

...⚠️ This is a hack! Don’t do this ⚠️...

Footnotes

  1. See open issue: arm64 function support for aws-lambda deployments #3026 2

  2. See WORKDIR under “Container image overrides”: https://docs.aws.amazon.com/lambda/latest/dg/configuration-images.html

  3. See open issue: Lambda DuplicateListener error when releasing a 2nd time #2066