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:
- 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.
- 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.