6.1. Write a Dockerfile#

A Dockerfile packages your application to be run as a container. This lab will show you how to create a Dockerfile that runs your application.

Ignoring Files#

Just like git, Docker should ignore some of the files in your project. In the root directory of your repo, create a new file called .dockerignore. The contents of .dockerignore should be:

**/*.sqlite3
**/__pycache__

The database does not belong in the image.

Python Version#

Applications should run in production the same way they run in development environments. Docker makes that possible by having different versions of containers available. Use the following command to discover the version of Python you use in your dev environment:

$ python --version 
Python 3.9.2

If you see output like that you should use the 3.9.2 tag of the official python container.

Starter Dockerfile#

In the root directory of your repo, create a new file called Dockerfile. The initial contents of Dockerfile should be:

# Start with the base Python container
# FIXME: Update the version
FROM docker.io/python:PUT-PYTHON-VERSION-HERE

# Install packages that are required. 
RUN pip install Django==4.2.9 

# Copy the Python code into the container
COPY mysite /mysite

# Set environment variables 
ENV PORT=8000 

# Set the working directory
WORKDIR /mysite 

# Default command to execute in the container
CMD python ./manage.py runserver 0.0.0.0:$PORT

The initial Dockerfile expects that the mysite directory exists in the root of your repo. Note that it runs the pip install Django==4.2.9 command that we ran in the 4.1. Start a Django Application lab.

Container Build and Run#

Build your application by pointing Docker to the Dockerfile. It’s typical to run the command from the directory with the Dockerfile:

$ docker build -t mysite .

The build will take a few minutes. Once it’s done verify that the container has been built:

$ docker image ls 
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
mysite       latest    812b4be50628   6 seconds ago   927MB

Now let’s run it:

$ docker run -it --rm -p 8000:8000 mysite 

Error

There’s an error in startup! What did we forget?

Debugging a Container That Won’t Start#

Sometimes a container builds but won’t start. Problems like this arise when there are problems with the WORKDIR or CMD lines in the Dockerfile or when a COPY moved files to the wrong place. When a container won’t start it can be hard to figure out what went wrong. When you run a container you can override the CMD in the Dockerfile to get a shell. The shell lets you look around and try to see what’s wrong.

$ docker run -it --rm -p 8000:8000 mysite /bin/bash
root@083afb59e29e:/mysite#

Inside the container try to start the Django server:

$ python manage.py runserver 0.0.0.0:$PORT 

Notice the error? We forgot to put psutil in the Dockerfile. We can test to make sure that’s the only fix needed:

root@083afb59e29e:/mysite# pip install psutil

With bash running you can look around the container with cd and ls and also try to start your application manually:

root@9e30cbcaa3af:/mysite# python manage.py runserver 0.0.0.0:$PORT
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
February 15, 2023 - 01:14:12
Django version 4.1.7, using settings 'mysite.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.

Note

The changes you make inside of a container are not permanent! Update your Dockerfile and re-run docker build to permanently fix this issue.

Initialize the Database#

Error

If you did everything correctly you will receive and OperationalError while trying to login.

Your container should not contain a database to start with, so it won’t let you login if you just run it as shown above. The following commands have to be run inside the container to get it to initialize the database:

root@28b4b0cf16b6:/mysite# python manage.py migrate 
root@28b4b0cf16b6:/mysite# python manage.py createsuperuser

Remember these commands. You need them every time you start a new container.