Adding flexibility to your deployments with Lambda Web Adapter
Explore how to use Lambda Web Adapter and CDK to simplify the deployment of your Web apps in Lambda and how to easily transition to ECS Fargate.
Published Apr 19, 2024
Last Modified May 10, 2024
Lambda Web Adapter (LWA) is an open-source project that enables running Web apps on Lambda functions without the need to change or adapt the code.
In my opinion, LWA opens up interesting pathways for architecture evolution: While it’s an interesting tool that helps lift & shift Web apps and APIs to Lambda functions without a lot of effort, it can also enable another migration path : Start deploying your application in Lambda as a Lambdalith and then transition to a classical container deployment when needed (e.g. ECS Fargate). In some scenarios, it may happen that you don’t have enough data to decide whether it’s better to host on Lambda or on Fargate. LWA contributes by adding portability to your deployments.
In this article, we’ll explore how to use LWA with CDK to simplify the deployment of your Web apps in Lambda and how to easily transition to ECS Fargate.
LWA is a Lambda extension. It creates an independent process within the Lambda execution environment that listens for incoming events and forwards them to your HTTP server.
LWA can integrate with invocations from Lambda function URLs (FURL), ALB, and API Gateway, converting invocation JSON payloads into HTTP requests that web frameworks like fastify or ASP.NET can handle. LWA also supports non-HTTP triggers (e.g. SQS, S3 notifications), but in these cases, it acts as a pass-through without converting the invocation payload.
LWA supports functions packaged as zip as well as Docker or OCI images.
Let’s have a look on how we’ll create our flexible deployment using CDK. In this example we’ll be focusing on deploying a public Web application using fastify as a Web framework:
My objective is to create a CDK construct supporting two deployment strategies:
Lambda
or ECSFargate
. Depending on the selected strategy, only the required components will be deployed:- When in
Lambda
mode, we’ll configure the function to use LWA extension. We’ll also configure a REST API Gateway with lambda proxy integration. - When the deployment mode is
ECSFargate
, We will deploy our application in an ECS Fargate service that is exposed via an ALB.
In both of these deployment strategies, users access the Web app through CloudFront. We will associate a WAF Web ACL to restrict access to both the API Gateway and the Application Load Balancer. These origins will only respond to requests that include a custom verification header added by the CloudFront distribution. This approach prevents bypassing the CloudFront distribution to access the origin directly.
☝️Some notes:
When deploying in Lambda, I ruled-out the use of FURL or HTTP API Gateway:
- With FURL, while you can setup Origin Access Control (OAC) with CloudFront, at the time of writing,
PUT
andPOST
operations require the client to sign the request payload. - HTTP API Gateway does not support WAF. An alternative solution would involve creating a Lambda@Edge to verify the presence of the custom header in the request.
To improve security, the custom verification header can be stored in secrets manager with rotation enabled so that the header can be updated as well as the origin WAF and the CloudFront distribution configurations.
Here are the relevant parts of the solution:
Creating a new fastify project is a breeze, I generally go with typescript; for the purpose of this article, I will create one super basic api:
I will deploy the Lambda function using zip archive and in order to use LWA as a Lambda extension, we’ll need to:
- Attach the LWA layer to the function
- Set the handler to the startup command
run.sh
script. This script starts the fastify Web app. It will be added to the zip package after the code bundling. - And lastly, define the
AWS_LAMBDA_EXEC_WRAPPER
environment variable to/opt/bootstrap
The
RestApi
CDK construct simplifies exposition of the Lambda function:After deployment, you will be able to view in the console the layer associated with the function:
CDK offers an L3 construct to deploy a load balanced ECS service. What I find interesting about this construct is that it hides all the complexity and verbosity of defining such a deployment, while allowing a level of flexibility. Another neat feature is that it can build and push our container image.
We’ll make sure to enable HTTPS, for that we’ll create a certificate and associated to the load balancer:
Here, I am using the default configuration, but you will want to adapt it to your own requirements.
Once deployed, the ECS service looks like this on the AWS console
You can find the full definition of the construct here.
The important bit, the CDK construct that enables flexible deployments:
This construct handles two deployment strategies
Lambda
or ECSFargate
. For each strategy, we’ll need to provide a factory function that creates the required resources. These two functions need to be injected from a parent construct and they are lazily evaluated given the selected strategy.We’ll also make sure that the distribution cache policy is disabled for both of these two origins.
As an example, the origin of the distribution, should end up looking like this when you select
Lambda
deployment strategyAnd finally, let’s see how to define the WAF WebACL with a rule that checks the
x-origin-header
verification header:You can follow this link for the complete
WebAppDeployment
construct definitionBefore wapping up, let’s call
where-are-you-deployed
endpoint defined in the sample web app for each strategy:Lambda web adapter is certainly not the only tool that helps running full-fledged web apps on Lambda functions, but it simplifies their deployment while supporting architectural evolution.
In this article we have seen how to build a CDK construct that offers a way to deploy the same web app on two distinct platforms, we can choose Lambda function or ECS Fargate by specifying a configuration during design time. We can extend this further by enabling the system to automatically redeploy itself, during runtime, on a specific target based on some events or CloudWatch alarms !
As always, you can find the full code source, ready to be adapted and deployed here:
Thanks for reading and hope you enjoyed it !