Migrating a Web App to AWS Lambda with Lambda Web Adapter

Home Blog Migrating a Web App to AWS Lambda with Lambda Web Adapter

As developers, we all seek to build web applications that can scale seamlessly, adapt to changing needs and do so without incurring excessive costs. One way to achieve this is by migrating web applications to AWS Lambda, which can provide scalability, flexibility, and cost savings. 

To make this process even easier, AWS provides the Lambda web adapter, a simple and efficient tool that enables you to migrate your web apps quickly. The AWS Lambda Web Adapter is an open-source project that simplifies migrating your web applications to Lambda, allowing you to use familiar frameworks like Express.js, Flask, SpringBoot and more

In this blog post, we’ll discuss the purpose of the adapter, how it can save time in migrating applications, provide a technical overview of how it works, and offer code examples to help you get started.

Hello Adapter

The AWS Lambda Web Adapter is designed to make it easier for developers to run web applications on AWS Lambda using their preferred web frameworks. The adapter supports Amazon API Gateway Rest API and HTTP  API endpoints, Lambda Function URLs, and Application Load Balancer. It works with Lambda managed runtimes, custom runtimes, and Docker OCI images, and supports web applications running over both HTTP and HTTPS. The Lambda Web Adapter requires no additional code dependencies, making it a seamless addition to your existing web applications.

Saving Time in Migrating Applications

Migrating a web application to AWS Lambda can be a time-consuming process, especially when using unfamiliar tools and frameworks. The Lambda Web Adapter allows developers to leverage their existing knowledge of popular web frameworks, resulting in a smoother and faster migration process. By using the Lambda Web Adapter, developers can continue to use their preferred tools and practices while benefiting from the scalability and cost savings of running their application on AWS Lambda.

Technical Overview

image source: https://github.com/awslabs/aws-lambda-web-adapter

The AWS Lambda Web Adapter (LWA) works by acting as a bridge between your web application and the AWS Lambda environment. It listens for incoming requests, processes them, and forwards them to your web application. The adapter uses the AWS Lambda extensions to create an independent Rust process within your Lambda container. It then listens for incoming events from the Lambda URL or API Gateway and forwards them to your internal HTTP server (e.g. Express or Python Django), with the appropriate path and the HTTP context from the external AWS service.

We’ve developed a functional demonstration of the extension using a Django app, which is the same one you create when following the official Django tutorial. This example includes PostgreSQL access, an admin panel for creating poll questions, and a website for users to takepart in the polls.

aws lambda web adapter

LWA -> Gunicorn -> Django

Gunicorn, short for Green Unicorn, is a Python Web Server Gateway Interface (WSGI) HTTP server that serves Python web applications by managing the communication between the web server and the application.

Django does not include a production-ready web server by default. The built-in development server provided by Django is intended for local development and testing, but it is not designed to handle the performance requirements and security considerations of production environments. That’s where Gunicorn comes in. By integrating Gunicorn with a Django application, developers can deploy their web applications in production.

AWS Lambda does not provide a direct web application interface; instead, services like API Gateway and Lambda URL convert HTTP requests into Lambda event payloads. Gunicorn, however, is not designed to communicate in this format. This is where LWA proves valuable, as it acts as a bridge, translating Lambda events received from API Gateway and Lambda URL into HTTP requests compatible with Gunicorn.

Due to AWS Lambda’s design, which allows processing of only one request at a time, it is necessary to configure Gunicorn with a single listener when setting it up for use with Lambda.

CMD ["gunicorn", "polls.wsgi:application", "-w=1", "-b=0.0.0.0:8000"]

Consuming AWS Services

When migrating an application to the cloud, it presents an excellent opportunity to leverage other cloud services. In this example, we use the AWS Secret Manager service to store the database password and the Django secret key. To retrieve the secret values and integrate them into the Django app, we employ Lambda Power Tools , simplifying the process of securely accessing these sensitive pieces of information.

One challenge when using traditional web applications that rely on SQL databases is the requirement to be within a VPC. By default, a Lambda function within a private subnet cannot access the internet and, therefore, cannot access AWS services. There are two ways to address this issue:

  1. Use an Internet Gateway and NAT to enable compute resources in the private subnet to access the internet.
  2. Use VPC endpoints, as Secret Manager supports VPC endpoints

The current solution opts for the first option because of its simplicity; however, it is less secure because it exposes the Lambda function to the internet rather than limiting access to specific AWS services.

The code snippet below is from the settings.py file. To accommodate both local development and cloud deployment, we attempt to load the secret using the Power Tools parameters utility and the Secret’s ARN. If this fails, we fall back to loading the password from the environment variable.

settings.py


try :
    password =  parameters.get_secret(os.environ.get("SSM_PASSWORD_NAME"))
except Exception:
    password = os.environ.get("DB_PASSWORD")
docker-compose.yml

app:
    build: .
    ports:
      - "8000:8000"
    depends_on:
      - db
    environment:
      DB_USER: myuser
      DB_PASSWORD: mypassword
      DB_HOST: db
      DJANGO_DEBUG: "True"

Serving Static Files

Django is an all-inclusive framework that not only handles backend processes but also supports frontend server-side rendering. This means that we can render web pages using the framework, even when developing Single Page Applications (SPAs) and using Django as a REST backend. One issue that requires attention is static file handling, such as serving JS or CSS files.

AWS recommends managing static files using S3 and CloudFront. One approach is to use django-storages, define S3 as a storage backend, and then set the S3 as the source for the CloudFront distribution. Alternatively, you can serve static files directly through the web server (using Gunicorn and the AWS Lambda Adapter), which is simpler and, in cases where only the admin panel needs support, is often preferable. To serve static files directly through the web server, simply use Django WhiteNoise and add it as middleware.

Take it for a Spin

The AWS Lambda Web Adapter offers a powerful solution for migrating web applications to AWS Lambda using familiar web frameworks like Django. By acting as a bridge between your web application and the AWS Lambda environment, the adapter enables a seamless transition while retaining the benefits of scalability and cost savings. 

Furthermore, by embracing a cloud-based solution, you can take advantage of improved security measures and more efficient static file serving. With the Lambda Web Adapter, developers can continue to use their preferred tools and practices, making migrating web applications to AWS Lambda more efficient and enjoyable.

Learn more about other serverless services and more in Lumigo’s guide to the AWS Serverless Ecosystem.