Simplified Dockerization of Python Applications: Part 1 - Dockerizing Django with Multi-Stage Alpine Image
Sat Jun 24 2023
Dickson Afful
Introduction
Docker has emerged as one of the most effective methods for distributing applications, offering unparalleled convenience. With a multitude of Python frameworks available, including Flask, FastAPI, Django, and more, developers have a wide array of options at their disposal. However, it has come to my attention that many beginners and even intermediate engineers face challenges when it comes to dockerizing their applications.
In this series, I aim to alleviate those struggles by providing valuable code snippets and comprehensive guidance on how to dockerize Python applications effectively.
To achieve optimized results, we will utilize the python:3.10-alpine image. This choice allows us to create smaller image sizes, despite a slightly longer build time. By leveraging this image, we can achieve the smallest image size possible, optimizing efficiency and resource utilization. Moreover, we will employ a multistage build approach, further reducing the image size.
sqlite
FROM python:3.10-alpine as base # Setup env ENV LANG C.UTF-8 ENV LC_ALL C.UTF-8 ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONFAULTHANDLER 1 FROM base as generator WORKDIR /app # Install python dependencies in /.venv COPY Pipfile . COPY Pipfile.lock . # Install pipenv RUN pip install pipenv RUN pipenv requirements > requirements.txt FROM generator as runtime WORKDIR /app COPY /app/requirements.txt ./ RUN pip install -r requirements.txt # Create and switch to a new user RUN addgroup --system --gid 1001 appgroup RUN adduser --system --uid 1001 appuser COPY . . RUN chmod u+x ./entrypoint.sh USER appuser:appgroup EXPOSE 8000 ENTRYPOINT [ "/app/entrypoint.sh" ]
entrypoint.sh
#!/bin/sh set -e python manage.py collectstatic --noinput python manage.py makemigrations python manage.py migrate gunicorn app_project.wsgi -b 0.0.0.0:8000
The provided code can be used as a base for all Python applications. To customize it for your
specific application, you only need to modify the entrypoint.sh file and the line
RUN pip install -r requirements.txt
.
Additionally, depending on your application's requirements and the database engine used, you may need to install specific dependencies. You can modify the RUN pip install -r requirements.txt line accordingly to include any additional dependencies required by your chosen database engine
Mysql
for the mysqlclient
RUN apk update \ && apk add --virtual build-deps gcc python3-dev musl-dev \ && apk add --no-cache mariadb-dev \ && pip install -r requirements.txt \ && apk del build-deps
Postgres
for psycopg2-binary
RUN apk update \ && apk add --virtual build-deps gcc python3-dev musl-dev \ && apk add --no-cache mariadb-dev \ && pip install -r requirements.txt \ && apk del build-deps
If there are other dependencies this is where you modify the code.
This Dockerfile is used to build a Docker image for a Python application. Let's go through the different sections and understand their purpose:
-
FROM python:3.10-alpine as base
: This sets the base image for the Dockerfile, using thepython:3.10-alpine
image as a starting point. It will be referred to asbase
for future stages. -
Environment setup:
ENV LANG C.UTF-8
andENV LC_ALL C.UTF-8
: These environment variables ensure that the system uses UTF-8 encoding, which is important for Python applications.ENV PYTHONDONTWRITEBYTECODE 1
: This prevents Python from writing bytecode files (.pyc) to disk, which can improve startup performance.ENV PYTHONFAULTHANDLER 1
: This enables the Python fault handler, which provides detailed tracebacks for unhandled exceptions.
-
FROM base as generator
: This creates a new stage namedgenerator
, inheriting from thebase
stage. It allows for logical separation within the Docker build process. -
WORKDIR /app
: Sets the working directory inside the container to/app
. -
Copying project files:
COPY Pipfile .
andCOPY Pipfile.lock .
: Copies thePipfile
andPipfile.lock
files from the host machine to the current working directory in the container.
-
Dependency installation:
RUN pip install pipenv
: Installs Pipenv, a tool for managing Python dependencies and virtual environments.RUN pipenv requirements > requirements.txt
: Uses Pipenv to generate arequirements.txt
file containing all the project's dependencies.
-
FROM generator as runtime
: Creates a new stage namedruntime
, inheriting from thegenerator
stage. -
WORKDIR /app
: Sets the working directory inside the container to/app
. -
Copying dependencies:
COPY --from=generator /app/requirements.txt ./
: Copies therequirements.txt
file generated in thegenerator
stage to the current working directory in theruntime
stage.
-
Dependency installation:
RUN pip install -r requirements.txt
: Installs the project's dependencies listed in therequirements.txt
file.
-
User setup:
RUN addgroup --system --gid 1001 appgroup
andRUN adduser --system --uid 1001 appuser
: Creates a system group (appgroup
) and a system user (appuser
) with specific user and group IDs (UID and GID) to run the application, ensuring better security and isolation.
-
Copying project files:
COPY --chown=appuser:appgroup . .
: Copies the project files from the host machine to the current working directory in the container, assigning ownership to theappuser
andappgroup
created earlier.
-
RUN chmod u+x ./entrypoint.sh
: Makes theentrypoint.sh
file executable for the userappuser
. -
User switch:
USER appuser:appgroup
: Sets the user and group toappuser
andappgroup
for subsequent commands. This enhances security by running the application with limited privileges.
-
EXPOSE 8000
: Informs Docker that the container will listen on port 8000, allowing for external access to the application if port mapping is configured. -
ENTRYPOINT [ "/app/entrypoint.sh" ]
: Specifies the entrypoint command to execute when the container starts. In this case, it runs theentrypoint.sh
script located in the/app
directory.
Any chanllenges comment below.