准备好以下的内容

  1. 项目文件
  2. 使用pip freeze > requirements.txt命令打包好项目的依赖包列表
  3. 安装好Docker和Docker-compose,可以分别用docker -vdocker-vompose -v命令查看是否安装成功。

编写Dockerfile文件

Docker 允许通过文本格式的配置文件来构建镜像,默认名称为 Dockerfile

# 从Docker仓库中拉去带有Python3.7的Linux环境
FROM python:3.7

# 设置 python 环境变量
ENV PYTHONUNBUFFERED 1

# 这两行是在系统钟安装了MySQL的连接器
RUN apt-get update
RUN apt-get install python3-dev default-libmysqlclient-dev -y

# 创建 code 文件夹并将其设置为工作目录
RUN mkdir /code
WORKDIR /code
# 更新 pip
RUN pip install pip -U
# 将 requirements.txt 复制到容器的 code 目录
ADD requirements.txt /code/
# 安装库
RUN pip install -r requirements.txt
# 将当前目录复制到容器的 code 目录
ADD . /code/

理解这些Docker指令的关键,在于牢记容器内的环境和宿主机是隔离的,核心问题是搞清楚那些操作是针对宿主机,哪些操作是针对容器

FROM python:3.7 指令从仓库拉取一个包含 python 3.7 的 Linux 操作系统环境(Linux 版本为 Debian)。

RUNWORKDIR 指令都是针对容器的,功能是在容器里创建目录、并将其设置为工作目录。注意宿主机是没有这个目录的。

ADD 指令出现了两次。ADD requirements.txt /code/ 意思是将宿主机当前目录(即 Dockerfile 所在目录)的 requirements.txt 文件复制到容器的 /code 目录中。ADD . /code/ 意思是把当前目录所有内容复制到容器 /code/ 目录,注意中间那个

编写docker-compose.yml文件

version: "3"

services:
  app:
    restart: always
    build: .
    command: bash -c "python3 manage.py collectstatic --no-input && python3 manage.py migrate && gunicorn --timeout=30 --workers=4 --bind :8000 django_app.wsgi:application"
    volumes:
      - .:/code
      - static-volume:/code/collected_static
    expose:
      - "8000"
    depends_on:
      - db
    networks:
      - web_network
      - db_network
  db:
    image: mysql:5.7
    volumes:
      - "./mysql:/var/lib/mysql"
    ports:
      - "3307:3306"
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=mypassword
      - MYSQL_DATABASE=django_app
    networks:
      - db_network
  nginx:
    restart: always
    image: nginx:latest
    ports:
      - "8001:8000"
    volumes:
      - static-volume:/code/collected_static
      - ./config/nginx:/etc/nginx/conf.d
    depends_on:
      - app
    networks:,
      - web_network

networks:
  web_network:
    driver: bridge
  db_network:
    driver: bridge

volumes:
  static-volume:

version 代表 docker-compose.yml 的版本,目前最新版为 3,不需要改动它。

从整体上看,我们定义了三个容器,分别是appdb、和nginx,容器之间通过定义的端口进行通讯。定义了两个网络,分别是web_networkdb_network,只有处在同一网络下的容器才能够互相通讯。不同网络之间是隔离的,即便采用同样的端口,也无法通讯。定义了一个数据卷static-volume。数据卷非常适合多个容器共享使用同一数据,可以看到appnginx都使用到了它。exposeports都可以暴露容器的端口,区别是expose仅暴露给其他容器,而ports会暴露给其他容器和宿主机。

下面具体分析一下:

定义了一个名叫 app 的容器。后面的内容都是 app 容器的相关配置:

  • restart :除正常工作外,容器会在任何时候重启,比如遭遇 bug、进程崩溃、docker 重启等情况。
  • build :指定一个包含 Dockerfile 的路径,并通过此 Dockerfile 来构建容器镜像。注意那个 "." ,代表当前目录。
  • command :容器运行时需要执行的命令。这里就是我们很熟悉的运行开发服务器了。
  • volumes卷,这是个很重要的概念。前面说过容器是和宿主机完全隔离的,但是有些时候又需要将其连通;比如我们开发的 Django 项目代码常常会更新,并且更新时还依赖如 Git 之类的程序,在容器里操作就显得不太方便。所以就有,它定义了宿主机和容器之间的映射:"." 表示宿主机的当前目录,":" 为分隔符,"/code" 表示容器中的目录。即宿主机当前目录和容器的 /code 目录是连通的,宿主机当前目录的 Django 代码更新时,容器中的 /code 目录中的代码也相应的更新了。这有点儿像是在容器上打了一个洞,某种程度上也是实用性隔离性的一种妥协。

    严格意义上讲,这里用到的 .:/code 并不是,而是叫挂载,它两是有区别的,只不过 docker-compose 允许将挂载写到卷的配置中。

  • expose:暴露容器的8000端口供其他容器访问,宿主机和外界无法访问
  • networks:能够访问web_networkdb_network
  • depends_on ,意思是此容器需要等待 db 容器启动完毕才能够启动。

分析一下 db 容器:

  • image :从仓库拉取 MySQL 5.7 。
  • volumes :这里出现的 static-volume。它的使用方式像这样:static-volume:/code/collected_static ,冒号后面还是容器内的目录,但冒号前的却不是宿主机目录、仅仅是卷的名称而已。从本质上讲,数据卷也是实现了宿主机和容器的目录映射,但是数据卷是由 Docker 进行管理的,你甚至都不需要知道数据卷保存在宿主机的具体位置。

    相比挂载,数据卷的优点是由于是 Docker 统一管理的,不存在由于权限不够引发的挂载问题,也不需要在不同服务器指定不同的路径;缺点是它不太适合单配置文件的映射。和挂载一样,数据卷的生命周期脱离了容器,删除容器之后卷还是存在的。下次构建镜像时,指定卷的名称就可以继续使用了。

  • ports :MySQL 默认通信端口为 3306 。由于我的机子上已经跑了一个MySQL服务,所以我将容器内的3306端口映射为本机的3307端口。
  • environment :定义容器的环境变量,设置了 MySQL 的 root 用户的密码、数据库的名称。
  • network:只能够访问db_network

添加db容器后记得的修改Django里的数据库设置。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_app',
        'USER': 'root',
        'PASSWORD': 'mypassword',
        'HOST': 'db',
        'PORT': '3306',
        'OPTIONS': {'charset': 'utf8mb4'},
    }
}

最后分析一下nginx容器,其他配置与上述两个大致一样,值得说一说的是ports设置,由于我的服务器上部署了其他服务,所以我将nginx端口映射为8001。

Docker 允许用户给每个容器定义其工作的网络,只有在相同的网络之中才能进行通讯。可以看到 nginx 容器处于 web_network 网络,而 db 容器处于 db_network 网络,因此它两是无法通讯的,实际上确实也不需要通讯。而 app 容器同时处于 web_networkdb_network 网络,相当于是桥梁,连通了3个容器。

Nginx配置

修改Nginx的配置文件,即映射到nginx容器内的config/nginx/django_app.conf

upstream app {
  ip_hash;
  server app:8000;
}

server {
  listen 8000;
  server_name localhost;

  location /static/ {
    autoindex on;
    alias /code/collected_static/;
  }

  location / {
    proxy_pass http://app/;
  }
}

此配置下 Nginx 会监听容器的 8000 端口,并将收到的请求发送到 app 容器(静态文件请求除外)。

之后在requirements.txt文件的最后两行增加mysqlclient库和gunicorn库。

mysqlclient==2.0.1
gunicorn==19.9.0

再修改Django项目的配置文件

ALLOWED_HOSTS = ['*']

...

STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
STATIC_URL = '/static/'

部署

运行命令docker-compose build构造镜像,再使用docker-compose up即可启用服务。

下面附上一下经常用到的命令:

  • 停止容器,docker-compose down
  • 后台运行docker容器:docker-compose up -d
  • 只想启动其中的一个容器:docker-compose up -d db或者docker-compose up -d app即可启动db容器或app容器。
  • 进入容器:docker exec -it container_id /bin/bash
  • 上面的container_id如果不知道如何获取可以通过docker ps命令查看。
最后修改:2022 年 01 月 25 日
如果觉得我的文章对你有用,请随意赞赏