Django-Docker Containerized Deployment: Django-Docker Local Deployment

Keywords: Linux Docker Django Python pip

This chapter will build a containerized Django project locally to experience how Docker works.

preparation in advance

development environment

Although there is a Windows-based version of Docker, all aspects of compatibility are not well done (installation is more troublesome), so it is recommended that readers install their own Linux or Mac system before learning. Of course, if you're willing to toss about, you can do it on Windows.

Don't worry, Django projects can still be developed under Windows, just without Docker.

Software Installation

  • Docker: To learn Docker, of course, you need to install Docker software (free community edition). See how to install it. Official Documents.
  • Docker-compose: This is Docker's official tool for arranging and running multiple containers. See the installation method. Official Documents This is the case. Most of the content of this tutorial is related to it.
  • Python 3: The tutorial deploys the Django project, so Python 3 is certainly needed (including python's package management tool pip).

When you're ready, go on with the next step.

Create a Django project

Open the Linux/Mac terminal and install the Django library:

$ pip install django==2.2

Create a new Django project in a location you like (for example, / home /):

$ django-admin startproject django_app

Enter the project root directory:

$ cd django_app

The rest of the tutorial is in this directory. For ease of reading, the command prompt $represents the current project root directory django_app/, and MySQL $represents the current directory django_app/mysql/. Please pay attention to the current working directory when you operate.

Then migrate the data:

$ python migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying sessions.0001_initial... OK

The preparations are done.

Building projects with Docker

Initial knowledge of Docker

The whole life cycle of Docker consists of three parts: image + container + repository.

Containers are instantiated from mirrors, which is a bit like object-oriented concepts: mirrors are classes, and containers are objects after class instantiation.

The mirror is a read-only template that contains the data needed to run the container. Mirrors can include a complete Linux operating environment with only Python or other programs that users need.

Containers are instances created by mirrors, similar to virtual machines, where specific applications can be run, and containers and containers are isolated from each other.

Warehouse concepts are similar to Git and Github, which are very easy to understand if you have used them. The default warehouse used by Docker is the Docker hub public warehouse officially maintained, from which uploading and pulling operations are similar to Git.

So much needs to be known at present. Here's how to understand it through practice.


To confirm that Docker is installed correctly, run the following instructions:

$ docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete 

Hello from Docker!
This message shows that your installation appears to be working correctly.

If everything is all right, the terminal will print out the welcome statement as shown in the figure above. The meaning of the docker run hello-world instruction is to build a container with a mirror named hello-world and run it. If the hello-world image is not available locally, Docker automatically searches from the warehouse and downloads the image with the same name.

We can use docker images to view existing local images:

$ docker images

REPOSITORY      TAG        IMAGE ID           CREATED           SIZE
hello-world     latest     fce289e99eb9       9 months ago      1.84kB

The tables and columns are mirror name, version, ID number, creation time and size.

You can also view existing local containers:

$ docker ps -a

CONTAINER ID   IMAGE        ..   CREATED         ..
38cb03a96dca   hello-world  ..   2 minutes ago   ..

In addition, there are some very useful basic instructions:

docker rmi [images ID]  # Delete the mirror of this ID
docker container stop [container ID]  # Containers that stop this ID
docker container start [container ID]  # Container that starts this ID
docker container rm [container ID]  # Delete the container for this ID

Because images are generated frequently during the test, you will definitely use the above instructions to view and delete useless images and containers.

After a small trial, the Django container is formally constructed.


Docker allows you to build images from text-formatted configuration files, with the default name of Docker file. So create a new Docker file in the project root directory and write:

# Pull Linux environment with python 3.7 from warehouse
FROM python:3.7

# Setting python environment variables

# Create the code folder and set it to the working directory
RUN mkdir /code
# Update pip
RUN pip install pip -U
# Copy requirements.txt to the code directory of the container
ADD requirements.txt /code/
# Installation Library
RUN pip install -r requirements.txt
# Copy the current directory to the code directory of the container
ADD . /code/

The key to understanding these Docker instructions is to keep in mind that the environment in the container is isolated from the outside (host) and that they are completely different. In other words, we need to figure out which operations are for the host and which operations are for the container.

The FROM python:3.7 instruction pulls a Linux operating system environment containing python 3.7 from the repository (the Linux version is Debian).

Both RUN and WORKDIR instructions are directed at containers. The function is to create directories in containers and set them to working directories. Note that the host does not have this directory.

The ADD instruction appears twice. ADD requirements.txt/code/means to copy the requirements.txt file of the host's current directory (that is, the directory where the Dockerfile is located) into the container's / code directory. ADD. / code / means to copy all contents of the current directory to the container / code / directory, paying attention to the middle point.

At present, the only library on which the project depends is Django, so create requirements.txt in the project root directory and write:


Didn't Django have been installed before? Why should it be installed here? The reason is that the Django is not installed in the container.

So the current document structure is as follows:

  - Dockerfile
  - requirements.txt
  - django_app
  - db.sqlite3

Now that the configuration file is ready, let's see how Docker-compose works.


In an online environment, it is not always possible to put all components of a project in the same container; it is better to put each individual function in a separate container for easy reuse. For example, put Django code in container A, Mysql database in container B, and so on.

So there may be multiple containers running on the same server, and it would be too cumbersome to start with one instruction at a time. Docker-compose is a solution to this problem. It is used to arrange multiple containers and write the commands of starting containers to the docker-compose.yml file. Every time this group of containers is started, it only needs docker-compose up. So the tutorial also uses docker-compose to manage containers.

First, confirm whether docker-compose was installed successfully:

$ docker-compose -v
docker-compose version 1.24.1, build 4667896b

After confirmation, create docker-compose.yml in the project root directory and write:

version: "3"
    restart: always
    build: .  # 'Point'represents the current directory
    command: "python3 runserver"
      - .:/code
      - "8000:8000"

Let's break down the meanings.

Version represents the version of docker-compose.yml. The latest version is currently 3, and there is no need to change it.

Then a container named app is defined. The following is the configuration of the app container:

  • Restart: In addition to working properly, the container will restart at any time, such as encountering bug s, process crashes, docker restart and so on.
  • Build: Specify a path containing Dockerfile and use it to build the container image. Notice that "..." represents the current directory.
  • Command: The command that the container needs to execute when it runs. Here is the running development server we are familiar with.
  • volumes: Volume, that's a very important concept. As mentioned earlier, containers are completely isolated from the host, but sometimes they need to be connected; for example, the Django project code we develop is often updated, and the update depends on programs such as Git, which makes it inconvenient to operate in containers. So there is a volume, which defines the mapping between the host and the container: "..." denotes the current directory of the host, ":" as a delimiter, "/code" denotes the directory in the container. That is to say, the current directory of the host and the / code directory of the container are connected. When the Django code of the current directory of the host is updated, the code in the / code directory of the container is updated accordingly. It's a bit like punching a hole in a container, and to some extent it's a compromise between practicality and isolation.

Strictly speaking, the.:/ code used here is not a volume, but a mount. It differs from each other, except that docker-compose allows the mount to be written to the volume configuration. Later chapters will talk about it.

  • Ports: Port mapping of the host and container is defined. Containers are isolated not only from the environment, but even from the ports. But web applications can't communicate with the outside world without ports, so we define that the 8000 port of the host is mapped to the 8000 port of the container. That is to say, the 8000 port of the host is the 8000 port of the container, but we need to make sure that the port is not occupied by other programs.

The configuration is ready. Now the catalogue structure of the project is as follows:

  - docker-compose.yml
  - Dockerfile
  - requirements.txt
  - django_app
  - db.sqlite3


Input instruction docker-compose up starts container service:

$ docker-compose up

Creating network "django_app_default" with the default driver
Building app
Step 1/8 : FROM python:3.7
3.7: Pulling from library/python
4a56a430b2ba: Pull complete
6933d3d46042: Pull complete
Digest: sha256:0f0e991a97426db345ca7ec59fa911c8ed27ced27c88ae9966b452bcc6438c2f
Status: Downloaded newer image for python:3.7
 ---> 02d2bb146b3b
Step 1/8 : FROM python:3.7
 ---> 02d2bb146b3b
Step 7/8 : RUN pip install -r requirements.txt
 ---> Running in 62a60a3003fe
Looking in indexes:
Collecting django==2.2 (from -r requirements.txt (line 1))
  Downloading (7.4MB)
Installing collected packages: sqlparse, pytz, django
Successfully installed django-2.2 pytz-2019.2 sqlparse-0.3.0
Step 8/8 : ADD . /code/
 ---> cb23f483ffb6
Successfully built cb23f483ffb6
Successfully tagged django_app_app:latest
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating django_app_app_1 ... done
Attaching to django_app_app_1
app_1  | Watching for file changes with StatReloader
app_1  | Performing system checks...
app_1  | 
app_1  | System check identified no issues (0 silenced).
app_1  | October 05, 2019 - 15:03:15
app_1  | Django version 2.2, using settings 'django_app.settings'
app_1  | Starting development server at
app_1  | Quit the server with CONTROL-C.

You can see that Docker successfully constructed the image and container according to the requirements of the configuration file, and started the container.

Open the browser and enter local IP port

Seeing Django's small rocket, the project is running successfully. Press Ctrl + C to stop the development server running.

After stopping the server, the container actually exists, just stops running. Input:

$ docker-compose down

You can delete containers.

If you want to run the container in the background, enter:

$ docker-compose up -d

In addition, if you need to rebuild the image, enter the command:

$ docker-compose build

Start and stop existing containers:

$ docker-compose start
$ docker-compose stop

It's very simple.

Too slow to download?

Because of the well-known reasons, the domestic network environment is very complex. When building a mirror, it is often necessary to pull resources from remote warehouses abroad. The speed of downloading without moving is really a headache.

The solution is to change the resource pull link to a domestic mirror source, such as Tsinghua mirror source.

Modify Dockerfile as follows:

FROM python:3.7

# Adding Debian Tsinghua Mirror Source
RUN echo \
deb buster main contrib non-free\
deb buster-updates main contrib non-free\
deb buster-backports main contrib non-free\
deb buster/updates main contrib non-free\
    > /etc/apt/sources.list

RUN mkdir /code
# Add pip Tsinghua Mirror Source
RUN pip install pip -U -i
ADD requirements.txt /code/
# Add pip Tsinghua Mirror Source
RUN pip install -r requirements.txt -i
ADD . /code/

Reconstruct the image and download it as fast as you can.

Tsinghua Source is used in the later part of the tutorial, but in order to facilitate reading, the code for replacing this part of the source is omitted and the reader understands it.


This chapter has a preliminary experience of Docker's workflow, and it is easy to build a container Django project.

The next chapter also adds MySQL database to container orchestration.

Posted by hl_tan on Tue, 08 Oct 2019 10:02:05 -0700