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.22, build 3a2c30b
$ docker-compose --version
Docker Compose version v2.15.1
Dockerfile
Add a Dockerfile to the "server" directory:
# server/Dockerfile
# pull official base image
FROM python:3.11
# 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.11
command tells Docker to start with a Linux base image that has Python 3.11 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" directory, 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