昨天折腾了一下用Docker部署Django+redis+MySQL的项目,顺便压缩了一下之前不使用数据库的Django的项目的镜像大小。

配置文件

推荐使用alang/django,一个是因为官方几年前就停止更新了,另一个是如果通过python的镜像进行构建,构建出来的镜像会比较大,大概1G多,而由于这个镜像是基于alpine构建的,大概在300M左右(用python的alpine构建也可以,但如果需要用gunicorn启动的话得自己配置,比较麻烦。)

Dockerfile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
FROM alang/django:4.1

WORKDIR /usr/django/app
COPY requirements.txt .
USER root
RUN apk add --update --no-cache curl jq py3-configobj py3-pip py3-setuptools python3-dev mariadb-connector-c-dev \
  && apk add --no-cache gcc g++ jpeg-dev zlib-dev libc-dev musl-dev libffi-dev mariadb-dev \
#   && python -m pip install --upgrade pip \
  && pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple \
  # 把安装过程中不再需要的安装包清理掉、达到缩减镜像大小的目的
  && apk del gcc g++ musl-dev libffi-dev python3-dev \
  && apk del curl jq py3-configobj py3-pip py3-setuptools \
  && rm -rf /var/cache/apk/*
# USER $GUNICORN_USER_UID
WORKDIR /usr/django

如果需要修改python版本,可以参考下面这个Dockerfile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 可以替换Python版本
FROM python:3.9-alpine

# set default port for gunicorn
ENV PORT=8000

# add gunicorn config
ENV GUNICORN_CONFIG_ROOT=/etc/gunicorn
RUN mkdir -p $GUNICORN_CONFIG_ROOT
COPY gunicorn.conf.py $GUNICORN_CONFIG_ROOT

# setup working directory
ENV WORKDIR=/usr/django
RUN mkdir -p $WORKDIR
WORKDIR $WORKDIR

# install tini to ensure that gunicorn processes will receive signals
# install gettext and bash (required by start.sh)
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
# 更新pip3
RUN python3 -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN apk add --no-cache tini gettext bash

# run start.sh on container start
COPY start.sh $WORKDIR
RUN chmod +x ./start.sh
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["./start.sh"]

# create directories for generated static content, user-uploaded files and application source code
ENV STATIC_ROOT=/var/www/static
ENV MEDIA_ROOT=/var/www/media
ENV SOURCE_ROOT=$WORKDIR/app
RUN mkdir -p $STATIC_ROOT $MEDIA_ROOT $SOURCE_ROOT

# install gunicorn & django
ENV GUNICORN_VERSION=21.2.0
RUN pip install \
  gunicorn==$GUNICORN_VERSION \
  -i https://pypi.tuna.tsinghua.edu.cn/simple

WORKDIR /usr/django/app
COPY requirements.txt .
# USER root
RUN apk add --update --no-cache curl jq py3-configobj py3-pip py3-setuptools python3-dev mariadb-connector-c-dev \
  && apk add --no-cache gcc g++ jpeg-dev zlib-dev libc-dev musl-dev libffi-dev mariadb-dev \
#   && python -m pip install --upgrade pip \
  && pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple \
  # 把安装过程中不再需要的安装包清理掉、达到缩减镜像大小的目的
  && apk del gcc g++ musl-dev libffi-dev python3-dev \
  && apk del curl jq py3-configobj py3-pip py3-setuptools \
  && rm -rf /var/cache/apk/*
WORKDIR /usr/django

环境变量说明

alang/django镜像的环境变量说明,官方没有表格,只有一个文本描述,不太直观

说明 默认值 变量名
Django项目名,必填,不然无法启动 DJANGO_APP
端口 8000 PORT
如果true,会在代码更新后自动重启项目 false GUNICORN_RELOAD
Django项目启动前执行的manage.py命令,例如 DJANGO_MANAGEMENT_ON_START
设置后,会依次执行其中的命令,并且执行后,不会使用 DJANGO_MANAGEMENT_JOB

docker-compose.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
version: '3'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    volumes:
      - .:/usr/django/app
    environment:
      - DJANGO_APP=Django项目名称
      - GUNICORN_RELOAD=true
      - DJANGO_MANAGEMENT_ON_START=makemigrations --noinput;migrate --noinput;collectstatic --noinput
    depends_on:
      - redis
      - db
    links:
      - redis
      - db
    restart: always

  db:
    image: mysql:8.0.3
    environment:
      MYSQL_ROOT_PASSWORD: 数据库ROOT密码
      MYSQL_DATABASE: 数据库名
      MYSQL_USER: 数据库用户名
      MYSQL_PASSWORD: 数据库密码

  redis:
    image: redis:alpine

然后将Django项目中数据库的地址使用db替代,redis地址使用redis替代,使用docker-compose启动即可。

MySQL数据持久化

如果将数据放在容器上显然是不太安全的,如果哪天容器无法启动或者其他不可抗力因素那么数据就永远丢失了。

我们首先使用docker-compose build构建,然后使用docker-compose up进行启动,启动之后,使用cp命令拷贝数据库文件夹到本地。

1
docker cp db容器名:/var/lib/mysql ./db_data

之后再在compose文件中添加目录映射。

1
2
3
4
5
6
7
8
9
  db:
    image: mysql:8.0.3
    volumes:
      - ./db_data/mysql:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: 数据库ROOT密码
      MYSQL_DATABASE: 数据库名
      MYSQL_USER: 数据库用户名
      MYSQL_PASSWORD: 数据库密码

这样记得备份./db_data目录就可以了。

遇到的一点问题

之前在Dockerfile中加入了migrate,发现怎么都运行不起来,而且尝试在Django容器中ping数据库和redis,都ping不同,最后发现自己理解错误。Docker得先构建出镜像才能运行,在构建镜像的过程中是不涉及启动任何相关的内容,所以应该将migrate放入docker-composecommand中。

附录

如果alpine的apk安装太慢,可以换apk的源,建议使用中科大的。

1
sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories