Logo

Simplified Dockerization of Python Applications: Part 1 - Dockerizing Django with Multi-Stage Alpine Image

dockerpython

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 --from=generator /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 --chown=appuser:appgroup . . 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:

  1. FROM python:3.10-alpine as base: This sets the base image for the Dockerfile, using the python:3.10-alpine image as a starting point. It will be referred to as base for future stages.

  2. Environment setup:

    • ENV LANG C.UTF-8 and ENV 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.
  3. FROM base as generator: This creates a new stage named generator, inheriting from the base stage. It allows for logical separation within the Docker build process.

  4. WORKDIR /app: Sets the working directory inside the container to /app.

  5. Copying project files:

    • COPY Pipfile . and COPY Pipfile.lock .: Copies the Pipfile and Pipfile.lock files from the host machine to the current working directory in the container.
  6. 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 a requirements.txt file containing all the project's dependencies.
  7. FROM generator as runtime: Creates a new stage named runtime, inheriting from the generator stage.

  8. WORKDIR /app: Sets the working directory inside the container to /app.

  9. Copying dependencies:

    • COPY --from=generator /app/requirements.txt ./: Copies the requirements.txt file generated in the generator stage to the current working directory in the runtime stage.
  10. Dependency installation:

    • RUN pip install -r requirements.txt: Installs the project's dependencies listed in the requirements.txt file.
  11. User setup:

    • RUN addgroup --system --gid 1001 appgroup and RUN 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.
  12. 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 the appuser and appgroup created earlier.
  13. RUN chmod u+x ./entrypoint.sh: Makes the entrypoint.sh file executable for the user appuser.

  14. User switch:

    • USER appuser:appgroup: Sets the user and group to appuser and appgroup for subsequent commands. This enhances security by running the application with limited privileges.
  15. EXPOSE 8000: Informs Docker that the container will listen on port 8000, allowing for external access to the application if port mapping is configured.

  16. ENTRYPOINT [ "/app/entrypoint.sh" ]: Specifies the entrypoint command to execute when the container starts. In this case, it runs the entrypoint.sh script located in the /app directory.

Any chanllenges comment below.

Comments