In the previous part you learned how to deploy FastAPI on Lambda. This time we will use containers, but still in a serverless fashion. Read on to learn how to deploy a FastAPI application on AWS Fargate behind an Application Load Balancer using AWS CDK.
Introduction
FastAPI is a Python framework for creating production-ready APIs, made by Sebastián Ramírez AKA tiangolo. It's fast (both when it comes to performance and developer speed), easy to get started with, and an absolute joy to work with.
In this two-part series you will learn how to create and deploy a minimal FastAPI application on AWS in a serverless fashion.
In part 1, you learned how to deploy FastAPI on Lambda using SAM.
In part 2, you will instead learn how to package FastAPI inside a container and deploy it on AWS Fargate, the serverless container platform. Here we will use an Application Load Balancer to front the application instead of API Gateway, and this time we will define and deploy the application using AWS Cloud Development Kit (CDK).
Are you ready to test out serverless containers? Keep reading!
Requirements
- Python
- AWS CDK installed and bootstrapped
- Docker
- Docker Compose (for running locally)
Tutorial
1. Create a directory for your application
To start, create a new directory and cd
into it.
$ mkdir fastapi-on-fargate
$ cd fastapi-on-fargate
2. Install AWS CDK for Python
In this example I have used Python, so to follow along the tutorial you must ensure that aws-cdk-lib
is installed. This is preferably done in a virtualenv, to avoid polluting the global python installation.
i. Create a virtualenv
$ python3 -m venv .venv
ii. Activate the virtualenv
$ source .venv/bin/activate
iii. Install aws-cdk-lib
$ pip3 install aws-cdk-lib
3. Create a simple FastAPI application
The sample application we will use is similar to the one used in part 1. However, this time we will not use mangum
as an adapter.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def get_root():
return {"message": "FastAPI running in a Docker container"}
Save the above in a file with the path src/app/__init__.py
.
4. Specify runtime dependencies
To run our function we will need to install a couple of third-party dependencies. We define these in a requirements.txt
file.
fastapi
uvicorn
Save the above in a file with the path src/requirements.txt
.
5. Create a Dockerfile
Now it is time to dockerize our FastAPI application. In the src/
directory, create a Dockerfile
with the following contents:
FROM python:3.9-alpine
WORKDIR /src
COPY ./requirements.txt /src/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /src/requirements.txt
COPY ./app /src/app
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"]
Save the above in a file with the path src/Dockerfile
.
6. Test your API locally with Docker Compose
Now that we have an application and a Dockerfile, let's try running it locally using Docker Compose. First, create a Compose file with the following contents:
services:
app:
build: src
ports:
- "3000:80"
Save the above as docker-compose.yml
in the root of the directory you created in step 1.
You should now be able to start your API locally:
$ docker-compose up
The above command should build a Docker image from your Dockerfile, and start the API locally on port 3000. Let's try calling it:
$ curl localhost:3000
{"message":"FastAPI running in a Docker container"}
7. Define your application using CDK
Now that we have verified that our dockerized API works as expected, it is time to get building with AWS CDK. The current directory structure should look like the following:
fastapi-on-fargate/
docker-compose.yml
src/
requirements.txt
Dockerfile
app/
__init__.py
Next to the src/
directory, create a new directory named cdk/
.
In the cdk/
directory, add a file cdk.json
with the following contents:
{
"app": "python3 cdk.py"
}
This tells cdk that our application is defined in cdk.py
, so let's create that file:
from aws_cdk import App
from fastapi_stack import FastAPIStack
app = App()
FastAPIStack(app, "FastAPIStack")
app.synth()
In the above file, we have imported FastAPIStack
from fastapi_stack
, but that file does not exist (yet). Save the following in fastapi_stack.py
(next to cdk.py
):
from aws_cdk import Stack
from constructs import Construct
import aws_cdk.aws_ec2 as ec2
import aws_cdk.aws_ecs as ecs
import aws_cdk.aws_ecs_patterns as ecs_patterns
class FastAPIStack(Stack):
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# (i) Create VPC
self.vpc = ec2.Vpc(self, "MyVPC", max_azs=3)
# (ii) Create Fargate Cluster
self.ecs_cluster = ecs.Cluster(
self,
"MyECSCluster",
vpc=self.vpc,
)
# (iii) Define Docker image for the Service
image = ecs_patterns.ApplicationLoadBalancedTaskImageOptions(
image=ecs.ContainerImage.from_asset(
directory="../src",
)
)
# (iv) Create Fargate Service and ALB
self.ecs_service = ecs_patterns.ApplicationLoadBalancedFargateService(
self,
"FastAPIService",
cluster=self.ecs_cluster,
cpu=256,
memory_limit_mib=512,
desired_count=2,
task_image_options=image,
)
This is where the magic happens. Lets go through the resource one by one.
i. Create VPC
This statement creates a VPC for us to use. Under the hood, CDK will generate a lot of resources (Subnets, Route Tables, Security Groups, etc.)
To see the power of CDK and how much it abstracts away from us, try executing cdk synth
and check out the generated CloudFormation template.
ii. Create Fargate Cluster
This statement creates a Fargate Cluster inside the VPC defined above.
iii. Define Docker image for the Service
Here we define which Docker image the Service should use. In a real world scenario, this should preferably reference an image from a registry such as DockerHub. But for the sake of this tutorial, here we build the image locally by specifying the path to the Dockerfile (which is ../src
).
iv. Create Fargate Service and ALB
This statement illustrates a high-level construct in CDK. Here we specify which cluster our service should run in, how many instances of the service we want, which image to use, and how much resources to allocate. The construct also sets up an Application Load Balancer in front of the Fargate Service and sets up the connection between them. There are more configuration options available for this particular construct than shown here.
8. Deploy your API to AWS
To deploy the API to AWS you need the following:
- An AWS account (duh!).
- Credentials configured for said account.
- Your account needs to be bootstrapped for CDK.
With the above in place we can now use CDK to deploy our FastAPI application. Issue the following command inside the cdk/
directory:
$ cdk deploy
...
✅ FastAPIStack
Outputs:
FastAPIStack.FastAPIServiceLoadBalancerDNS12341234 = FastA-FastA-XXXXXXXXXXXX-1111111111.eu-west-1.elb.amazonaws.com
FastAPIStack.FastAPIServiceServiceURL12341234 = http://FastA-FastA-XXXXXXXXXXXX-1111111111.eu-west-1.elb.amazonaws.com
Stack ARN:
arn:aws:cloudformation:eu-west-1:1234567890:stack/FastAPIStack/11111111-aaaa-2222-bbbb-333333333333
If everything works, and hopefully it does, you should see an output similar to the one above. This means that we have successfully deployed the API.
9. Call your newly deployed API
From the cdk deploy
output, you should be able to locate the URL for the Application Load Balancer. Let's try it out in the browser, or from the command line:
$ curl http://FastA-FastA-XXXXXXXXXXXX-1111111111.eu-west-1.elb.amazonaws.com
{"message":"FastAPI running in a Docker container"}
Cleaning up
To clean up, simply issue the following CDK command:
$ cdk destroy
Conclusion
You have now learned how to run a FastAPI application on Fargate, and how to configure and deploy it using AWS CDK. You have also seen how to use high-level constructs in CDK for common use-cases, such as deploying a Fargate Service behind an Application Load Balancer.
This wraps up this short two-part series, I hope you have learned a thing or two.
Now go build something awesome!
Top comments (0)