Deploy a FastAPI App on AWS ECS
FastAPI has become Pythonβs de-facto API framework; many new apps and prototypes are written with it due to itβs flexibility, βfreebiesβ such as built-in docs, and lack of boiler plate code.
However, deploying these apps on AWS is not so straightforward. There are several challenges including using a different production server, deploying the app as a container, and getting it to run on an EC2 instance or Elastic Container Service (ECS) cluster.
In this tutorial I am going to walk through setting up a FastAPI app on AWS ECS using Fargate. The code will create all the necessary infrastucture using Terraform, including setting up the Elastic Container Registry (ECR) repository, deploying the app in a Docker container, deploying the network infrastructure (VPC, subnets, route table, elastic IP, NAT gateway, etc.), and finally deploying the ECS cluster, task, service, and all other necessary components.
If youβd like to skip the step-by-step and just deploy your own FastAPI app right away, feel free to check out the repo on my GitHub.
FastAPI App
Letβs create a simple API app that we can deploy. Nothing fancy here; we can modify this later.
main.py:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {"message": "Welcome to the API"}
In order to get this on ECS weβll need to containerize the app using Docker. We will write our Dockerfile to install dependencies, expose port 80, and run the FastAPI app. To run our app, we will use uvicorn
instead of the pre-installed flask
development server, since our application is going into production. More on that here.
Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY . /app
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 80
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
requirements.txt
fastapi==0.109.2
uvicorn==0.27.1
Now we just need a way to deploy this code. We can write a generic deployment script that takes in a few environment variables and uses those to log into ECR, build the Docker image, tags the image for AWS, and pushes the image to ECR.
deploy.sh
#!/bin/bash
echo "Logging in to ECR"
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
echo "Building image"
docker build --no-cache --platform=linux/amd64 -t $REGISTRY_NAME .
echo "Tagging image"
docker tag $REGISTRY_NAME:$TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REGISTRY_NAME:$TAG
echo "Pushing image to ECR"
docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$REGISTRY_NAME:$TAG
And our FastAPI app is complete. Hereβs an overview of what our repo looks like so far.
fastapi-on-ecs/
ββ app/
β ββ Dockerfile
β ββ deploy.sh
β ββ main.py
β ββ requirements.txt
Infrastructure
We will use Terraform to manage all of our infrastructure. We can break the infrastructure into two parts: setup and app. The setup will be a one time creation of our ECR repository. The app will deploy all of the network components needed plus all of the ECS components needed.
Letβs take a look at the breakdown of the infra directory.
fastapi-on-ecs/
ββ ...
ββ infra/
β ββ setup/
β β ββ main.tf
β β ββ output.tf
β β ββ variable.tf
β ββ app/
β β ββ ecs
β β β ββ main.tf
β β β ββ output.tf
β β β ββ variable.tf
β β ββ network
β β β ββ main.tf
β β β ββ output.tf
β β β ββ variable.tf
β β ββ main.tf
β β ββ variable.tf
Optionally, if we want to store our Terraform state in an S3 bucket we need to create a backend.tf
file for each of the setup and app infrastructure.
Examples of these are below:
infra/setup/backend.tf
terraform {
backend "s3" {
region = "<AWS_REGION>"
bucket = "<BUCKET_NAME>"
key = "<APP_NAME>/terraform.tfstate"
}
}
/infra/app/backend.tf
terraform {
backend "s3" {
region = "<AWS_REGION>"
bucket = "<BUCKET_NAME>"
key = "<APP_NAME>/terraform.tfstate"
}
}
You can forgo this option and Terraform will store the state locally in each directory.
Using a Makefile
To make deploying our app easier, we will use a Makefile. We can then call short make commands to preform each deployment task.
To do so, we will create a .env file that will store our AWS Account ID and AWS region we wish to deploy to. The Makefile will then pick up those variables using the include
keyword.
.env
AWS_ACCOUNT_ID=<YOUR_AWS_ACCOUNT_ID>
AWS_REGION=<YOUR_AWS_REGION>
Makefile
include .env
.EXPORT_ALL_VARIABLES:
APP_NAME=my-app-name
TAG=latest
TF_VAR_app_name=${APP_NAME}
REGISTRY_NAME=${APP_NAME}
TF_VAR_image=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REGISTRY_NAME}:${TAG}
TF_VAR_region=${AWS_REGION}
setup-ecr:
cd infra/setup && terraform init && terraform apply -auto-approve
deploy-container:
cd app && sh deploy.sh
deploy-service:
cd infra/app && terraform init && terraform apply -auto-approve
destroy-service:
cd infra/app && terraform init && terraform destroy -auto-approve
Letβs take a look at our final file structure:
fastapi-on-ecs/
ββ app/
β ββ ...
ββ infra/
β ββ ...
ββ .env
ββ Makefile
Deploying the App
Letβs deploy our app!
WARNING: When you are doing using your API service, be sure to tear down all of your infrastructure. Leaving the API running can incur a large AWS bill.
- Setup your ECR Repository (one time setup)
make setup-ecr
2. Build and deploy your container
make deploy-container
3. Deploy your API Service on ECS
make deploy-service
Note: The URL for your endpoint will be printed by Terraform once the above command is done executing. Example: alb_dns_name = "<APP_NAME>-alb-123456789.<AWS_REGION>.elb.amazonaws.com"
. Navigate to that URL in your browser to ensure the API is working. You can also check out the API docs at the <URL>/docs
endpoint.
4. Destroy (Tear Down) your Infrastructure
make destroy-service
Conclusion
Using this starter code you can easily deploy a FastAPI app to your AWS account without having to write any infrastructure or click through the console. Simply adapt the main.py
to your needs and youβll be up and running with your own app.
The code for this post can be found on my GitHub page.
If you liked this post or have any questions/comments, please leave a comment below!
Want updates on what Iβm writing, side projects Iβm building, or articles I find interesting? Feel free to follow me or checkout my publication, Aspiring Data Scientist.
Thanks for reading! π¨πΌβπ»