Deploy FastAPI on AWS Part 2: Fargate & ALB
Tags: aws, python, serverless, tutorial
Published: 2022-03-09 10:25:55

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](https://aws.amazon.com/fargate/) behind an Application Load Balancer using [AWS CDK](https://aws.amazon.com/cdk/).

## Introduction

[FastAPI](https://fastapi.tiangolo.com/) is a Python framework for creating production-ready APIs, made by Sebastián Ramírez AKA [tiangolo](https://github.com/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](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html)
- Docker
- Docker Compose (for running locally)

## Tutorial

### 1. Create a directory for your application

To start, create a new directory and `cd` into it.

```bash
$ 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

```bash
$ python3 -m venv .venv
```

#### ii. Activate the virtualenv

```bash
$ source .venv/bin/activate
```

#### iii. Install aws-cdk-lib

```bash
$ 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.

```python
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:

```docker
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](https://docs.docker.com/compose/). First, create a Compose file with the following contents:

```yaml
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:

```bash
$ 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:

```bash
$ 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:

```json
{
  "app": "python3 cdk.py"
}

```

This tells cdk that our application is defined in `cdk.py`, so let's create that file:

```python
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`):

```python
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](https://docs.aws.amazon.com/cdk/api/v1/python/aws_cdk.aws_ecs_patterns/ApplicationLoadBalancedFargateService.html) available for this particular construct than shown here.


### 8. Deploy your API to AWS

To deploy the API to AWS you need the following:
1. An AWS account (duh!).
2. Credentials configured for said account.
3. Your account needs to be [bootstrapped](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html) 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:

```bash
$ 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:

```bash
$ 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:

```bash
$ 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!