Building a Serverless Flask Application in Python: Deploying a Docker Image on AWS Lambda with API Gateway

Afonso Alcantara
5 min readOct 2, 2024

--

Our goal

This article continues from my previous piece, “A Step-by-step guide to running a Python Docker Image on AWS Lambda”, In this article, we’ll deploy a Flask application as a Docker image within a Lambda function, making it accessible through an API Gateway. This architecture is particularly useful for delivering a robust backend for applications.

Want to get notified about my future posts? Subscribe here

First things first

To follow this article you’ll need:

  • Docker installed and configured
  • AWS CLI installed and configured
  • AWS CDK installed and configured
  • Basic knowledge of Python and Flask
  • Be able to create an API in AWS API Gateway

Once pre-requirements have been checked, let's dive in.

Let’s code

Let’s begin by creating a folder for our project:

 mkdir flask-python-lambda

In the folder you created, type the following command.

cdk init app --language typescript

The command “cdk init app — language typescript” initializes a new AWS CDK application using TypeScript as the programming language. Here’s a breakdown of its purpose:

  • cdk init: This is the command to create a new CDK project.
  • app: Specifies that you want to create a new CDK application.
  • language typescript: Indicates that the application should be set up using TypeScript, allowing you to write your infrastructure code in this specific language. We’ll explore more details about this later in the article.

All in all, this command sets up the necessary files and directory structure for a TypeScript-based AWS CDK project, enabling you to define and manage your cloud resources programmatically.

As mentioned before, this article continues my previous piece, so I won’t cover all the configuration steps here. For a complete guide, please refer to my earlier article.

Dependencies

It’s always a good practice to create a virtual environment for installing the project dependencies. If you’d like to create one, please do so within image/src folder.

We will need two dependencies in this article:

Flask and aws-wsgi

I believe you know what Flask is so I won’t dive into this dependency, However, it’s worth explaining aws-wsgi: A wrapper that helps deploy WSGI-compatible applications (like those built with Flask) on AWS services such as Lambda, facilitating serverless deployment.

pip install Flask aws-wsgi

Once the dependencies are installed, we need to create a requirements.txt file. This file will be used during the Docker image build process for Lambda deployment. To create the file, run the command below in the image folder.

pip freeze > requirements.txt

Dockerfile

Our Dockerfile will look like this:

FROM public.ecr.aws/lambda/python:3.11

# 2 - Copy requirements.txt
COPY requirements.txt ${LAMBDA_TASK_ROOT}

# 3 - Install all the packages
RUN pip install -r requirements.txt

# 4 - Copy all files in ./src to the LAMBDA_TASK_ROOT
COPY src/ ${LAMBDA_TASK_ROOT}

# 5 - Set the CMD to the handler.
CMD [ "main.handler" ]

Just out of curiosity, I’ve explained in a previous article why we need to use ${LAMBDA_TASK_ROOT}.

Checking the progress

After all configuration’s been done, the project will have this structure:

flask-python-lambda/
├── README.md
├── bin
│ └── flask-python-lambda.ts
├── cdk.json
├── image
│ ├── Dockerfile
| ├── requirements.txt
│ └── src
│ └── main.py
├── jest.config.js
├── lib
│ └── flask-python-lambda-stack.ts
├── node_modules
├── package-lock.json
├── package.json
├── test
│ └── flask-python-lambda.test.ts
└── tsconfig.json

To develop our Lambda function, we’ll need to create a file named main.py. The code will look like this:

import awsgi
from flask import (Flask)
from webhooks import webhook


def create_app():
app = Flask(__name__)

# Import and register blueprints, if any
app.register_blueprint(webhook)

return app

def handler(event, context):
print("Flask app started")
app = create_app()

return awsgi.response(app, event, context)

To organize our code, we’ll create another file namedwebhook.py. This file declares all the routes we want to have.

from flask import (
Blueprint,
jsonify
)

webhook = Blueprint("webhooks", __name__)

@webhook.route("/welcome", methods=["GET"])
def webhook_welcome():
print('testing log from welcome function')
return jsonify(status=200, message='Welcome to the webhook!')

@webhook.route("/", methods=["GET"])
def webhook_root():
print('testing log from root function')
return jsonify(status=200, message='OK')

Deploy

It’s time to deploy our docker image. To do this, use the command below:

cdk deploy

The command will produce a result like this:

Tests

The easiest way to test the Lambda function is by using the test functionality provided by the AWS Console. You can create a new test by choosing the api-gateway-aws-proxy.

Change the path and httpMethod variables to test the desired route. In this example, the route to be tested is /welcome, and the httpMethod is GET.

The test result will look like this:

API Gateway

Create a REST API and add a resource called welcome, which matches the route we used.

The resource will be set up as a proxy integration, sending requests directly to the Lambda function. Be sure to select ANY as the method type and enable Lambda proxy integration toggle.

Let’s deploy the API and test it using the URL provided by API Gateway. The result will look like this:

Well, we did it!

Pros and cons

You may be wondering why you need to do all this work and whether this architecture is effective. That’s a valid question, and I had similar thoughts when I first encountered it. To help clarify, I’ll list some pros and cons of this architecture.

Pros

  • Cost: This architecture is more cost-effective compared to an architecture that uses EKS/ECS, ECR, Load Balancer, and VPC.
  • Reusable: This architecture is based on Docker Image, so you can deploy the same image in an ECS or EKS cluster without changing your code.

Cons

  • Performance: Due to Lambda’s cold start, this architecture may take a bit of time to respond to its initial requests.
  • API Gateway Proxy integration: All response transformations must be configured directly in your code.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Afonso Alcantara
Afonso Alcantara

Written by Afonso Alcantara

I talk about Solution Architecture, Cloud providers, and other stuff. Feel free to come along.

No responses yet

Write a response