Dockerizing Your FastAPI Project
Overview
In this post, I briefly break down the dockerization of a FastAPI project and how to upload it to Docker Hub. The concepts outlined here are expanded upon in a book I recently published, “Building Serverless Python Apps Using FastAPI and AWS.” Without further ado, let’s begin!
Defining the Image
Installing dependencies
We will be using the Python 3.9-slim base image to help in our definition and installing uvicorn, wheel, and poetry along with a few other dependencies. Create a Dockerfile and add the following:
FROM python:3.9.12-slim RUN pip install fastapi uvicorn poetry wheel EXPOSE 8000 WORKDIR /usr/src/projectname ENV PORT 8000 ENV HOST "0.0.0.0"
Here you can change the port and anything else you may need for your app to be properly configured.
Copying Project Files
This next part will be running under a few assumptions. First, it assumes that everything except the main.py
and dependencies file is under a folder called src
within the repo. The assumption is made that your app’s port and host are being pulled from environment variables instead of needing to be passed via the startup command. It also assumes that the end-user is leveraging poetry for their dependencies.
Either way, you’ll need to essentially copy the minimum files required to run your application to avoid adding extra scripts, the tests folder, etc. Add the following to your Dockerfile:
COPY ./src/ /projectname/src
COPY ./main.py /projectname
COPY ./pyproject.toml /projectname
WORKDIR /projectname
Now we just need to install project dependencies and add the entry point. Add the following to your file:
RUN poetry config virtualenvs.create false \
&& poetry install
CMD ["uvicorn", "main:app"]
Review Dockerfile
Your final Dockerfile should appear similar to the following, give or take a change in ports and project file listing:
FROM python:3.9.12-slim
RUN pip install fastapi uvicorn poetry wheel virtualenv
EXPOSE 8000
WORKDIR /usr/src/projectname
ENV PORT 8000
ENV HOST "0.0.0.0"
COPY ./src/ /projectname/src
COPY ./main.py /projectname
COPY ./pyproject.toml /projectname
WORKDIR /projectname
RUN poetry config virtualenvs.create false \
&& poetry install
CMD ["uvicorn", "main:app"]
You may need to swap out the dependency install that is using poetry
for the install using pip
if you are using a requirements.txt
file in your project. Don’t forget to copy it over as well!
Build and Test
Building
To build locally, run the following with the desired image name for your project:
docker build -t imagename -f Dockerfile .
If your machine is running with the Apple Silicon chip, run the following instead to prevent issues with AWS Fargate and Heroku (to name a few):
docker build --platform linux/amd64 -t imagename -f Dockerfile .
Check the generated item by running the docker images
command. Once you confirm the listing, we can move on to the next step.
Running Locally
We are set to attempt an initial project startup. If your app requires environment variables, it won’t be ready to use until you configure your build to have it, but you will be able to know if the setup overall is close to working in the next step.
From the terminal, run the following:
docker run --name imagename -p 8000:8000 imagename
You’ll see the output from the terminal unless you pass in the option to run in the background. You can also see the logs from the Docker Dashboard when you click on the container. Assuming you are running on port 8000
and no further changes were done to the project startup, your output should appear similar to the following:
Uploading to Docker Hub
For this part, you’ll need to have an account with Docker. Once you do that, click on the option to create a new repository and give it a name.
You can choose either private or public for this example. Note that you can delete it after the exercise. Click on create to get the final repository and image name. You can change the way you are tagging your image in your builds to include the repository name like so:
docker build -t repositoryname/imagename -f Dockerfile .
Once you are all set with your newly tagged image, authenticate into your DockerHub account and push your image up with the following command (replace repository, image, and tag with your entries, e.g., viratecinteractive/bookapi:latest
):
docker push repositoryname/imagename:tag
Once complete, you’ll see your new upload alongside some additional details and options available to you like vulnerability scanning.
That’s it! This was just a brief example of using Docker Hub as our registry but you can apply most of the steps discussed for uploading to AWS ECR and Heroku container registry. There is still plenty one can do to double down on security efforts or automate some of these processes but this is just a guide to get your team started. If you want to learn more about using AWS ECR and Fargate with a sample FastAPI project, check out my new book, Building Serverless Python Apps Using FastAPI and AWS. Thanks for reading!