由于下学期要参与我川著名“内卷展览大会”——大创,前后端的一些表面功夫还是得了解一些,正好上个暑假用Django开发的小项目在学期末尝试性地上线生产环境试运行了一小会,期间也折腾了不少方案,这里给出一套成熟的Django Web生产环境的配置供大家参考:Docker + uWSGI + nginx + MySQL + Django。
Docker
Docker作为一款简单易用的容器化技术,广受开发人员喜爱。在我们这套方案中,我们将利用docker-compose搭建两个容器:web和db,其中web中运行uWSGI+nginx+Django,db中运行MySQL。
Dockerfile
下面给出web的Dockerfile供参考:
FROM centos:7
MAINTAINER MrZilinXiao <xiaozilin1@gmail.com>
ENV TZ "Asia/Shanghai"
ENV DOCKER_SRC=SCUCourseKiller
ENV DOCKER_HOME=/root
ENV DOCKER_PROJECT=/root/SCUCourseKiller
WORKDIR $DOCKER_HOME
RUN mkdir SCUCourseKiller
RUN yum -y install epel-release yum-utils && \
yum -y install git nginx gcc gcc-c++ crontabs && \
yum -y install python36 python36-devel python36-pip && \
yum clean all
RUN yum -y install mariadb-devel mysql-devel
WORKDIR $DOCKER_PROJECT
COPY ./ ./
RUN pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt --default-timeout=100
EXPOSE 8000
RUN chmod u+x start_script
ENTRYPOINT ["./start_script"]
可以看见,build基于centos,安装好必要的依赖包之后直接通过yum安装了python3.6,将当前目录文件全部拷贝进docker后又用pip安装了依赖,最后运行start_script这个shell文件。
start_script
#!/bin/bash
export LANG="en_US.UTF-8"
# nginx settings
sed -i '/user/{s/nginx/root/}' /etc/nginx/nginx.conf
ln -s /root/SCUCourseKiller/mysite_nginx.conf /etc/nginx/conf.d/
nginx
# application settings
export DJANGO_SETTINGS_MODULE=SCUCourseKiller.settings
python3 ./manage.py makemigrations
python3 ./manage.py migrate --noinput
python3 ./manage.py loaddata ./fixtures/superuser.json
python3 ./manage.py collectstatic --noinput
uwsgi --ini ./mysite_uwsgi.ini
start_script文件主要完成docker运行的初始化任务,包括建立nginx配置文件的软链接、数据库的migration和管理员用户的导入(可省略)、uWSGI的运行。
docker-compose
docker-compose是一款辅助docker管理的小工具,可以很方便地配置docker镜像、实现容器管理,特别是对依赖关系的管理。
docker-compose的配置文件为yaml格式,对缩进格式要求极其严格,且这类错误不好检查,故在出现错误时建议使用第三方检查工具。
version: '3.3'
services:
db:
container_name: mymysql
image: mysql:5.7
restart: always
environment:
MYSQL_DATABASE: SCUCourseKiller
MYSQL_USER: root
MYSQL_PASSWORD: SCUCourseKiller
MYSQL_ROOT_PASSWORD: SCUCourseKiller
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --init-connect='SET NAMES utf8mb4;' --innodb-flush-log-at-trx-commit=0
ports:
- 3306:3306
volumes:
- "./SCUKiller/mysql/config:/etc/mysql/conf.d"
restart: always
container_name: SCUCourseKiller
build: ./
command: ["./wait-for-it.sh", "db:3306"]
ports:
- 8000:8000
depends_on:
- db
links:
- db
将以上内容与Dockerfile放置在同级目录下,其中需要注意的参数有:
- restart:容器重启后是否跟随重启。
- volumes:指出本地与容器的卷挂载关系,本次只在db容器中设置了挂载,用于加载自定义的mysql配置文件my.cnf,后续数据库配置过程会提及。
- command:容器启动前执行的命令,这项命令的优先级在Dockerfile的ENTRYPOINT之前,可以结合wait-for-it.sh实现在特定端口开放前的阻塞,避免数据库未加载完成就载入Django导致错误。
- depends_on:依赖关系的简单设定,决定build和run时运行的先后顺序。注意,运行先后顺序的确定与上面的wait-for-it.sh并不冲突,这里的顺序并不阻塞,即Docker并不会等到MySQL完全启动完成后再开始启动Django。
- links:配置Docker之间网络关系的简单配置,将直接将两个容器放置在bridge中。复杂的网络关系配置可以通过与services同级的networks配置项配置。e.g.:
networks:
killerNet:
driver: bridge
ipam:
config:
- subnet: 172.19.0.0/16
uWSGI
uWSGI是一个简易的Web服务器,在此项目中作为Nginx与Django之间的中间件用于更好地处理多并发。其与nginx可以采用本地端口或sock文件通信,与Django则采用WSGI接口通信。
mysite_uwsgi.ini文件:
[uwsgi]
# Django-related settings
# the base directory (full path)
chdir = /root/SCUCourseKiller
# Django's wsgi file
module = SCUCourseKiller.wsgi
# the virtualenv (full path)
# home = /path/to/virtualenv
# /process-related settings
# master
# master = true
# maximum number of worker processes
processes = 2
enable-threads = true
# the socket (use the full path to be safe)
socket = /root/SCUCourseKiller/SCUCourseKiller/docker_app.sock
# ... with appropriate permissions - may be needed
chmod-socket = 666
# clear environment on exit
vacuum = true
nginx
nginx的配置主要注意与uWSGI的通信即可,其余则需要注意static、media路径的转发:
# mysite_nginx.conf
upstream django {
server unix:///root/SCUCourseKiller/SCUCourseKiller/docker_app.sock;
}
# configuration of the server
server {
# the port your site will be served on, default_server indicates that this server block
# is the block to use if no blocks match the server_name
listen 8000 default_server;
# the domain name it will serve for
server_name 0.0.0.0; # substitute your machine's IP address or FQDN
charset utf-8;
# max upload size
client_max_body_size 75M; # adjust to taste
access_log /root/SCUCourseKiller/log/access.log main;
error_log /root/SCUCourseKiller/log/error.log info;
# Django media
location /media {
alias /root/SCUCourseKiller/media; # your Django project's media files - amend as required
}
location /static {
alias /root/SCUCourseKiller/static; # your Django project's static files - amend as required
}
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_pass django; # for a file socket
include /root/SCUCourseKiller/uwsgi_params; # the uwsgi_params file you installed
}
}
MySQL
MySQL的配置在Docker中略有提及,主要包括MySQL在初始化设置时需要配置库名、用户名和密码并更改编码格式以支持中文。为实现修改并发连接数等高级操作,我们可以自定义my.cnf配置文件并挂载至容器中特定目录即可。
# my.cnf
[mysqld]
character-set-server=utf8mb4
default-time-zone='+8:00'
innodb_rollback_on_timeout='ON'
max_connections=1024
innodb_lock_wait_timeout=15
Django
首先确保通过python manage.py runserver
可以成功启动Debug模式服务器,随后将settings.py中的DEBUG设为false。
centos7中安装完python后并没有django等依赖包,需要自行设置requirements.txt文件:
mysqlclient
retrying
Django==2.1.8
Pillow==5.3.0
requests==2.19.1
urllib3==1.23
uwsgi
开发、运行与生产上线
每次修改完代码后,可通过centos中的git pull同步或者直接在docker中通过docker exec -it [Docker Hash或容器名称] /bin/bash
进入容器,用vi修改。
本地测试与上线时:
docker-compose build # 构建容器
docker-compose start -d # 以daemon守护模式启动
docker-compose logs -f # 查看日志