Writing an API using FastAPI, part 2: Containerizing the app

Introduction

In order to deploy our app, we need to containerize it. I found that containerizing Python apps is not as straightforward as one might think. In this post I will discuss the Dockerfile I used to generate the image. In the repository, you will find the yaml files in order to deploy this app on a Kubernetes cluster.

The full code can be found in this repository.

The Dockerfile

We will start by setting up our working environment:

FROM python:3.11-slim-buster

RUN mkdir -p /home/app

RUN addgroup --system app && adduser --system --group app   


ENV HOME=/home/app
ENV APP_HOME=/home/app/code
RUN mkdir $APP_HOME
WORKDIR $APP_HOME

A couple of points:

  • For now I am sticking to Python 3.11. As of the time of writing (7th of October 2023), Python 3.12 was released, I have not tried this out yet.
  • Always make sure that your code does not run under a user with administrative rights.

We go on:

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

Line by line:

  1. The PYTHONDONTWRITEBYTECODE environment variable is to ensure that on import of any source modules, no pyc files are written. Information on that can be found here.
  2. The PYTHONUNBUFFERED environment variable forces all stdout and stderr streams to be unbuffered.

Next we update the system by installing the necessary libraries, especially the postgres library, the libpq-dev is a library that makes it possible to interface with the Postgres database.

Next we upgrade pip, install the setup tools, and install all the dependencies:

RUN pip install --upgrade pip
RUN pip install -U setuptools
COPY ./requirements.txt .
RUN pip install -r requirements.txt

Then we copy the sources, make sure the directory has the right owner, that is an owner without administrative rights and switch to that owner:

COPY . .

RUN chown -R app:app $APP_HOME

USER app

Then we simple expose our port, 8000 in our case, and start the webserver. I chose for gunicorn for this example. I also made sure enough workers and threads were started:

EXPOSE 8000
CMD gunicorn --bind :8000 --workers 10 --threads 10 --timeout 90 app:app -k uvicorn.workers.UvicornWorker

That is it, to deploy this to Kubernetes, please look in the kubernetes directory in the repository.

Conclusion

As mentioned in the introduction, writing a Dockerfile for a Python app is not straightforward. However, I used the Dockerfile I used to containerize a Django Rest Framework application as a sort of template.

This Dockerfile could probably be further optimized by using a two layer approach, but that is someting for another post.

Leave a Reply

Your email address will not be published. Required fields are marked *