Django Dockerize

Part 1, Chapter 4


Docker in 30 seconds or less.

Docker allows you to package software in a container that includes everything it needs to run. Remember how we used a virtual environment on our local machine to hold all of our Python dependencies? Docker containers are like virtual environments that include code, the Python runtime, system tools, and libraries. Your Docker container is a machine within your local machine. Everything you need to run your software exists within the boundaries of the container. Learn more here.

Start by downloading and installing Docker if you haven't already done so.

If you're on a Mac or Windows machine, Docker Desktop will install both Docker and Docker Compose. Linux users will have to download and install them separately.

$ docker --version
Docker version 20.10.2, build 2291f61

$ docker-compose --version
docker-compose version 1.27.4, build 40524192

Dockerfile

Add a Dockerfile to the "server" directory:

# server/Dockerfile

# pull official base image
FROM python:3.9

# set working directory
WORKDIR /usr/src/app

# set environment variables
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1

# install dependencies
COPY ./requirements.txt .
RUN pip install --upgrade pip
RUN pip install -r requirements.txt

The Dockerfile tells Docker how to build the image that your container will invoke during runtime. The FROM python:3.9 command tells Docker to start with a Linux base image that has Python 3.8 installed on it.

Next, add a .dockerignore file to the "server" directory with the following:

# server/.dockerignore

.dockerignore
*.pyc
__pycache__
env

The .dockerignore file tells Docker to ignore all of the files and directories listed within. These files will not be copied into the Docker container. For example, we don't need the env directory in the Docker container because we install the Python dependencies directly onto the container's machine.

From your "server" directory, create a new start.sh file with the following code.

# server/start.sh

#!/bin/bash

python ./manage.py migrate
python ./manage.py runserver 0.0.0.0:8000

Now we can run the migrate and runserver commands serially with a single script. Try it out in your terminal:

(env)$ chmod +x start.sh
(env)$ ./start.sh

Quit the server with CONTROL-C.

Docker Compose File

Change directories up one level, so that you are at your project's root. Create a new docker-compose.yml file with the following code:

# docker-compose.yml

version: "3.8"

services:

  server:
    build:
      context: ./server
    container_name: perusable-server
    command: [ "bash", "start.sh" ]
    ports:
      - 8003:8000
    volumes:
      - ./server:/usr/src/app

While the Dockerfile defines how to build an image that a single container will use, the docker-compose.yml file describes how multiple containers will interact with each other.

Learn more about Docker Compose here.

Our docker-compose.yml file defines a single server service with a container name of perusable-server. We can give the service and the container any names we want, but I chose these to make their intentions clear. The server service will run the Django app server. Django will run on port 8000 within the Docker container and will expose port 8003 to your local machine to access the service. When the container starts, it will execute our start.sh file, which will run the migrate and runserver Django management commands. One more thing -- the "server" directory on your local machine is shared with the /usr/src/app directory in the Docker container. That means every time you add or change a file on your machine inside the "server" directort, it will be automatically updated in the Docker container.

Test

Now we're ready to start our Docker container. Run the following code in your terminal from your project's root directory, to build the image:

$ docker-compose build

Once the image is built, spin up the containers in detached mode:

$ docker-compose up -d

Once the container is up, the start.sh script will execute. The Django migrations will be applied and the development server will run. The Django app should then be available.

Visit http://localhost:8003 in your browser. You should see the confirmation screen.

Run docker-compose down to bring the container down.

Since we'll be using Docker from this point on, you can go ahead and deactivate from the virtual environment and remove the "env" directory.

At this point, your directory structure should look like this:

├── docker-compose.yml
└── server
    ├── .dockerignore
    ├── Dockerfile
    ├── catalog
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    ├── db.sqlite3
    ├── manage.py
    ├── perusable
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── requirements.txt
    └── start.sh

Docker Commands

If you run into problems, you can view the Docker logs at:

$ docker-compose logs -f

Try to fix the issue, and then re-build the images and spin up the containers again.

To enter the shell of a specific container that's up and running, run the following command:

$ docker-compose exec <service-name> bash

# for example:
# docker-compose exec server bash

If you want to run a command against a new container that's not currently running, run:

$ docker-compose run --rm server bash

The --rm option tells docker to delete the container after you exit the bash shell.




Mark as Completed