使用 Docker 运行 Django 项目

以前运行 Django 项目的方式是在虚拟机上运行(uwsgi+Django),如果要运行多个不同版本的 Django 项目就蛋疼了,当然也可以使用 virtualenv 虚拟环境工具,但是我觉得用起来比较麻烦,尤其是需要自动化的时候。

Docker 除了众所周知的一些特性之外,最基本的特性就是环境隔离。在虚拟机上运行 Docker 固然对性能没什么提升,但是单纯的用作任务编排也是很好的(尤其想要结合自动化脚本)。下面就介绍一下如何使用 Docker 运行 Django 项目。

安装 Django,并创建项目:

pip3 install django
django-admin --version # 2.1.1
django startproject demo

因为我使用 Python3 运行,所以要将 demo/manage.py 中的运行环境改为 Python3, #!/usr/bin/env python => #!/usr/bin/env python3 ,然后:

./manage.py migrate
./manage.py runserver

默认情况下,连接的 DB 是 SQLite,为了保证我们运行的服务是无状态的,所以把 DB 改为 MySQL,通过远程连接的方式。在本地运行 MySQL:

docker pull mysql:5.7
docker images # 6a834f03bd02
docker run --name dj-mysql -d -e MYSQL_ROOT_PASSWORD=my-secret -p 127.0.0.1:3306:3306 6a834f03bd02
docker ps -a
# CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                                 NAMES
# 390ac3e7d3ab        6a834f03bd02        "docker-entrypoint.s…"   About a minute ago   Up About a minute  127.0.0.1:3306-> 3306/tcp, 33060/tcp   dj-mysql
docker exec -it 390ac3e7d3ab mysql -uroot -pmy-secret
mysql> create database demo charset="utf8mb4";   # 创建数据库
Query OK, 1 row affected (0.30 sec)
mysql> ^DBy

接下来将 Django 连接 DB 方式改为 MySQL:

DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # }
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'demo',
        'USER': 'root',
        'PASSWORD': 'my-secret',
        'HOST': 'mysql',
        'PORT': '3306',
    }
}

编写 Dockerfile:

FROM python:3

RUN mkdir /data
WORKDIR /data
ADD requirements.txt /data/

RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.douban.com/simple

ADD . /data

CMD ["python3", "manage.py", "runserver", "0.0.0.0:8080"]

requirements 包含 djangomysqlclient

打包镜像:

docker build -t demo:v1.0 .

查看镜像ID,并启动容器:

docker run -it -d --link dj-mysql:mysql -p 127.0.0.1:8003:8080 be691f1720c3

*注意*:为了使用 mysql 需要以 --link 的方式把两个容器连接起来,你也可以使用 network 的方式。

然后浏览器访问:127.0.0.1:8003 ,就可以看到熟悉的欢迎界面了。

runserver 的方式是多个线程运行的(1.11 源代码中是两个线程),生产环境下建议使用 uwsgi,uwsgi 可以明显的控制进程数量。

添加 uwsgi.ini 文件到目录下:

[uwsgi]
wsgi-file       = demo/wsgi.py
http            = 0.0.0.0:8080
master          = true
workers         = 2
procname-master = uwsgi master
procname        = uwsgi worker
reload-mercy    = 8
vacuum          = true
enable-threads  = True
lazy-apps       = True

这里有两点要注意:

  1. uwsgi 需要在前台运行(这和 Docker 的运行机制有关),所以不要设置 daemonize2 选项
  2. Dockerfile 中已经设置了 WORKDIR,就不需要再 uwsgi 中再做 chdir 这样的操作

此时 Dockerfile 为(要将 uwsgi 添加到 requirements.txt 文件中):

FROM python:3

RUN mkdir /data
WORKDIR /data
ADD requirements.txt /data/

RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.douban.com/simple

ADD . /data

CMD ["uwsgi", "--ini", "uwsgi.ini"]

重新打包镜像,然后运行即可。

btw,如果仅在本地测试的话 docker-compose 要更方便一些,DB 和 Django 同时启动,并且可以服务发现。


上面只是一个例子,项目实战还需要考虑:

  1. 设置 .dockerignore ,无关的文件不要打包到镜像中,比如 Dockerfile__pycache__
  2. 可变的信息以环境变量的方式注入,比如数据库的 host、账号密码等
  3. 基础镜像 python:3 有完整的依赖,所以有九百多兆之大,实际项目建议使用 python:3-alpine 来替代做为一个基础镜像(不到一百兆),需要的依赖再手动安装一下

Date: 2018-09-08 10:40

Author: JerryZhang