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
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
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
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
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.
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.
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
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
--rmoption tells docker to delete the container after you exit the bash shell.
✓ Mark as Completed