SoFunction
Updated on 2024-10-29

Django Docker containerized deployment of Django-Docker local deployment

This chapter will build a containerized Django project locally to get a feel for how Docker works.

preliminary

development environment (computer)

There is a Windows-based version of Docker, but it's not very compatible (and can be tricky to install), so we recommend that readers install Linux or Mac before learning. Of course, if you're up for it, you can do it on Windows.

Don't worry, you can still develop Django projects under Windows, you just won't be using Docker.

software installation

  • Docker: To learn Docker, of course, you need to install the Docker software (free community edition), see the official documentation for installation methods.
  • Docker-compose: This is the official Docker tool for orchestrating and running multiple containers, see the official documentation for installation. Most of this tutorial is related to it.
  • Python3: The tutorial deploys a Django project, so Python3 is a must (including pip, the python package management tool).

When you're ready, move on to the next step.

Creating a Django Project

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

$ pip install django==2.2

Create a new Django project in a location you like (e.g. /home/):

$ django-admin startproject django_app

Go to the project root directory:

$ cd django_app

The rest of the tutorial will be in this directory. For ease of reading, the command prompt $ means that you are currently in the project root directory django_app/, and mysql $ means that you are currently in the directory django_app/mysql/, so readers are encouraged to keep an eye on the current working directory as they proceed.

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

Preparation is taken care of.

Building Projects with Docker

Getting to know Docker for the first time

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

Containers are instantiated from mirrors, which is a bit of an object-oriented concept: the mirror is the class, and the container is the object after the class is instantiated.

An image is a read-only template that contains the data needed to run a container. An image can contain a complete Linux operating environment with only Python or other programs the user needs installed.

Containers are instances created from images, similar to virtual machines, in which specific applications can be run and which are isolated from each other.

The concept of repositories is similar to that of Git and Github, which are easy to understand if you've used them before; the default repository used by Docker is the officially maintained Docker hub public repository, and uploading and pulling from it is similar to Git.

That's all you need to know for now, and here's how to understand it by doing it.

Hello-world

To confirm that Docker has been installed correctly, run the following command:

$ 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 fine, the terminal will print out the welcome message shown above. docker run hello-world command means to build a container with a hello-world image and run it. If the hello-world image is not available locally, Docker will automatically search the repository and download an image with the same name.

We can use docker images to see what images we already have locally:

$ docker images

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

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

It is also possible to view containers that already exist locally:

$ docker ps -a

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

In addition to this there are some very useful basic commands:

docker rmi [images ID] # Delete the image with this ID
docker container stop [container ID] # Stop the container with this ID
docker container start [container ID] # Start the container for this ID
docker container rm [container ID] # Delete the container with this ID

Since mirrors are generated frequently for testing, you will definitely use the above commands to view and delete useless mirrors and containers.

With that out of the way, let's move on to building the Django container.

Dockerfile

Docker allows images to be built via a configuration file in text format, with a default name of Dockerfile. so create a new file Dockerfile in the root directory of your project and write:

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

# Set python environment variables
ENV PYTHONUNBUFFERED 1

# Create the code folder and set it as the working directory
RUN mkdir /code
WORKDIR /code
# Update pip
RUN pip install pip -U
# Will be copied to the container's code directory
ADD  /code/
# Install libraries
RUN pip install -r 
# Copy the current directory to the container's code directory
ADD . /code/

The key to understanding these Docker directives is to keep in mind that the environment inside the container is isolated from the outside world (the host) and that the two are completely different. In other words, it's important to figure out which operations are for the host and which are for the container.

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

The RUN and WORKDIR commands are container-specific and create a directory in the container and set it as the working directory. Note that the host does not have this directory.

The ADD directive appears twice. ADD /code/ means to copy files from the host's current directory (where the Dockerfile is located) to the container's /code directory. ADD . /code/ means to copy all the contents of the current directory to the container /code/ directory, note the dot in the middle.

The only library the project currently depends on is Django, so create it in the project root and write to it:

django==2.2

Why do I need to install Django here when I already installed it earlier? The reason is that Django was installed on the host, and there is no Django in the container!

So the current file structure is as follows:

django_app
 - Dockerfile
 - 
 - 
 - django_app
 - db.sqlite3

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

Docker-compose

In online environments, it is not usual to put all the components of a project into the same container; a better approach is to put each separate function into a separate container, so that it is easy to reuse. For example, Django code into container A, Mysql database into container B, and so on.

Therefore, it is possible to have multiple containers running on the same server, and it would be too cumbersome to start them with a single command each time. Docker-compose solves this problem by organizing multiple containers and writing the commands to start them in a single file, so that every time you start a container, you only need to docker-compose up. Therefore, the tutorial will also use docker-compose to manage containers.

First make sure that docker-compose is installed successfully:

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

After confirming that there are no errors, create and write to the project root directory:

version: "3"
services:
 app:
  restart: always
  build: . # 'Dot' for current directory
  command: "python3  runserver 0.0.0.0:8000"
  volumes:
   - .:/code
  ports:
   - "8000:8000"

Let's break down the various implications.

version stands for the version, currently the latest version is 3, you don't need to change it.

Then you define a container called app. What follows is the configuration of the app container:

  • restart : In addition to normal operation, the container will be restarted at any time, such as when a bug is encountered, a process crashes, or docker restarts.
  • build : Specify a path containing a Dockerfile from which to build the container image. Note the "." , for the current directory.
  • command : The command to be executed when the container is running. This is where we are familiar with running the development server.
  • volumes: volumes, this is a very important concept. As I said before, containers are completely isolated from the host, but there are times when you need to connect them; for example, the code of the Django project we develop is often updated, and when it is updated, it relies on programs like Git, which is not very convenient to operate in containers. So there is a volume, which defines the mapping between the host and the container: "." is the current directory of the host, ":" is the separator, and "/code" is the directory in the container. This means that the host's current directory and the container's /code directory are connected, and when the Django code in the host's current directory is updated, the code in the container's /code directory is updated accordingly. It's kind of like punching a hole in the container, and in a way it's a compromise between utility and isolation.

Strictly speaking, the . :/code is not a volume, it's called a mount, and there is a difference, except that docker-compose allows you to write the mount to the volume's configuration. We'll talk about this in a later section.

ports: defines the port mapping between the host and the container. Containers are isolated not only from the environment, but even from ports. However, web applications can not communicate with the outside world through ports, so here we define the host's port 8000 mapped to the container's port 8000, i.e., accessing the host's port 8000 is accessing the container's port 8000, but make sure that the port is not occupied by other programs.

The configuration is written. The directory structure of the project is now as follows:

django_app
 - 
 - Dockerfile
 - 
 - 
 - django_app
 - db.sqlite3

beta (software)

Enter the command docker-compose up to start the 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 
 ---> Running in 62a60a3003fe
Looking in indexes: /simple
Collecting django==2.2 (from -r  (line 1))
 Downloading /packages/54/85/0bef63668fb170888c1a2970ec897d4528d6072f32dee27653381a332642/Django-2. (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 http://0.0.0.0:8000/
app_1 | Quit the server with CONTROL-C.

You can see that Docker has successfully built the image and container and started the container as per the configuration file.

Open a browser and enter the local IP port 127.0.0.1:8000:

 

See the Django rocket, the project is up and running. Press Ctrl + C to stop the development server.

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

$ docker-compose down

The container can then be deleted.

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

$ docker-compose up -d

Alternatively, 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 simple, isn't it?

Downloading too slow?

For well-known reasons, the domestic network environment is very complicated. When building mirrors you often need to pull resources from remote repositories abroad, and the hilly download speeds are a real headache.

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

Modify the Dockerfile as follows:

FROM python:3.7
ENV PYTHONUNBUFFERED 1

# Add Debian Tsinghua mirror source
RUN echo \
deb /debian/ buster main contrib non-free\
deb /debian/ buster-updates main contrib non-free\
deb /debian/ buster-backports main contrib non-free\
deb /debian-security buster/updates main contrib non-free\
  > /etc/apt/

RUN mkdir /code
WORKDIR /code
# Add pip Tsinghua mirror source
RUN pip install pip -U -i /simple
ADD  /code/
# Add pip Tsinghua mirror source
RUN pip install -r  -i /simple
ADD . /code/

Rebuild the image and the download is as fast as it can be.

The back of the tutorial uses the Tsinghua source, but for ease of reading, this part of the code to replace the source has been omitted, so that the reader understands it in his heart.

summarize

This chapter gives you an initial taste of the Docker workflow and makes it easy to build a containerized Django project.

This is the whole content of this article.