DEV Community

Cover image for Django development using Docker as host - Part 1: Dockerfile
Anuj Sharma
Anuj Sharma

Posted on

Django development using Docker as host - Part 1: Dockerfile

Django

Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of Web development, so you can focus on writing your app without needing to reinvent the wheel.

Docker

Docker is a set of platform as a service products that use OS-level virtualization to deliver software in packages called containers.

Recently while developing a Django project, I faced multiple issues installing the system dependencies (mysqlclient, etc) to install the required package for the working of my Django application in the local development environment.

This is always a hassle installing the system dependencies in the development machine, when

  • The host is formatted
  • You change your development machine
  • Change of the operating system (say windows to Linux)
  • Upgrading of operating system (removes support for the earlier version of libraries, always comes with the new version of libraries which may not have support for the application)

And again you start the race with the system setup, killing lots of time.

Even though we use Dockerfile to create production-ready images to run the production server, why not use the same as host and remove the system dependencies with all phases of the development?

Using the Docker as host, the release servers will always be in sync with the development machine and you can always look over the dependencies required for your application.

TOC

  1. Create Dockerfile
  2. Add non-root user
  3. Install runtime dependencies
  4. Copy files to the container
  5. Install python dependencies
  6. Add ENTRYPOINT

Let's jump in

If you have not already installed Docker and docker-compose on your machine, first install both by following the official guide.

1. Create Dockerfile

We need to first create a Dockerfile in the project directory, where we will be creating the Django project.
To keep the image size lower, we will be using the latest slim python image python:3.9.1-slim.
Add the following to the first line of the Dockerfile

FROM python:3.9.1-slim
Enter fullscreen mode Exit fullscreen mode

2. Add non-root user

It is always recommended to create a non-root user instead of using the root user in the Dockerfile. So let's create a user by the name of the project (myapp).

ARG APP_USER=myapp
RUN groupadd -r ${APP_USER} && useradd --no-log-init -r -m -g ${APP_USER} ${APP_USER}
Enter fullscreen mode Exit fullscreen mode

The ARG APP_USER=myapp creates an argument variable so that the value myapp can be referenced using the variable throughout the Dockerfile.
The next command adds a group and creates a user with the myapp, creates home directory /home/myapp for the user and assigns the permission to the home directory.

3. Install runtime dependencies

With Django, we are going to use the latest version of MySQL, so let's add the required system dependencies to the Dockerfile. Also, we will clean the Linux apt list as it is no longer required.

# default-libmysqlclient-dev -- Required for mysql database support
RUN set -ex \
    # Runtime dependencies
    && RUN_DEPS=" \
    default-libmysqlclient-dev \
    " \
    && seq 1 8 | xargs -I{} mkdir -p /usr/share/man/man{} \
    && apt-get update && apt-get install -y --no-install-recommends $RUN_DEPS \
    # Remove package list
    && rm -rf /var/lib/apt/lists/* \
    && mkdir /static_my_project
Enter fullscreen mode Exit fullscreen mode

What above command does is

  • Installs the mysqlclient system dependency
  • Removes the apt lists

4. Copy files to the container

Now, change the working directory to the /app/ directory

WORKDIR /app/
Enter fullscreen mode Exit fullscreen mode

Let's copy the requirements.txt file for the dependency instalment. (We will create it in further steps)

ADD requirements.txt /requirements.txt
Enter fullscreen mode Exit fullscreen mode

Let's copy the application source code and the scripts to the container

COPY ./src /app/
COPY scripts/ /scripts/
Enter fullscreen mode Exit fullscreen mode

5. Install python dependencies

Now we will install the required dependencies from the requirements.txt file

RUN set -ex \
    # Define build dependencies, they will be removed after build completes and libraries has been installed
    && BUILD_DEPS=" \
    build-essential \
    " \
    && apt-get update && apt-get install -y --no-install-recommends $BUILD_DEPS \
    && pip install -r /requirements.txt  \
    && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $BUILD_DEPS \
    && rm -rf /var/lib/apt/lists/*
Enter fullscreen mode Exit fullscreen mode

The above command does the following

  • Installs the build dependencies
  • Installs the python dependencies
  • Removes the build dependencies
  • Deletes the repository lists

Now we need to expose the port 8000 to access the running server

EXPOSE 8000
Enter fullscreen mode Exit fullscreen mode

6. Add ENTRYPOINT

Let's add an ENTRYPOINT which allows running manage.py commands without running the server directly. We will run the server by executing a command on the image. (I personally use this pattern because it gives you the flexibility to directly run Django shell without running the server).

ENTRYPOINT ["/scripts/docker/entrypoint.sh"]
Enter fullscreen mode Exit fullscreen mode

That's it for the Dockerfile. Here is the final content

FROM python:3.9.1-slim

ARG APP_USER=myapp
RUN groupadd -r ${APP_USER} && useradd --no-log-init -r -m -g ${APP_USER} ${APP_USER}

# default-libmysqlclient-dev -- Required for mysql database support
RUN set -ex \
    # Runtime dependencies
    && RUN_DEPS=" \
    default-libmysqlclient-dev \
    " \
    && seq 1 8 | xargs -I{} mkdir -p /usr/share/man/man{} \
    && apt-get update && apt-get install -y --no-install-recommends $RUN_DEPS \
    # Remove package list
    && rm -rf /var/lib/apt/lists/* \
    && mkdir /static_my_project

WORKDIR /app/

ADD requirements.txt /requirements.txt
COPY ./src /app/
COPY scripts/ /scripts/

# build-essential -- Required to build python mysqlclient library. https://packages.debian.org/sid/build-essential
RUN set -ex \
    # Define build dependencies, they will be removed after build completes and libraries has been installed
    && BUILD_DEPS=" \
    build-essential \
    " \
    && apt-get update && apt-get install -y --no-install-recommends $BUILD_DEPS \
    && pip install -r /requirements.txt  \
    && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $BUILD_DEPS \
    && rm -rf /var/lib/apt/lists/*

ENTRYPOINT ["/scripts/docker/entrypoint.sh"]
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
happygrizzly profile image
Aleksey Filippov

Can you tell what is the difference between the RUN_DEPS RUN command and BUILD_DEPS RUN command technically? Could default-libmysqlclient-dev be used in the BUILD_DEPS RUN command as well?