实现快速部署Django项目,手动部署可能需要几个小时,缩短至几十分钟,还有可能生产环境各种网络限制,访问不了互联网无法下载依赖包,可以通过本地镜像导出导入的方式快速部署。
docker和docker-compose安装参考官网:
docker安装
docker-compose安装
项目中涉及多个容器,Redis、Mysql、Gunicorn、Daphne、Nginx、Jenkins 每个容器对应一个应用,持久化的数据和配置文件是通过Volume的方式挂在到宿主机,方便数据更新。这些容器启动有先后顺序,即存在依赖关系,通过 docker-compose 编排这些容器。
每个容器说明:
Redis容器:缓存服务
Mysql:数据存储
Gunicorn:实现了WSGI协议,处理http请求
Daphne容器:实现了ASGI,处理websocket请求
Nginx容器:反向代理,前后端分离
Jenkins容器:发布用途
编排文件docker-compose.yml:
version: "3"
services:
redis:
image: daocloud.io/redis:3
container_name: "redis"
command: redis-server --requirepass 11111
volumes:
- ./deployment/redis:/data
ports:
- "6379:6379"
restart: always # always表容器运行发生错误时一直重启
db:
image: daocloud.io/mysql:5.7
container_name: "mysql"
environment:
- MYSQL_DATABASE=ywpt # 数据库名称
- MYSQL_ROOT_PASSWORD=22222 # 数据库密码
volumes:
- ./deployment/mysql/data:/var/lib/mysql # 挂载数据库数据
- ./deployment/mysql/conf/my.cnf:/etc/mysql/my.cnf # 挂载配置文件
- ./deployment/mysql/init:/docker-entrypoint-initdb.d/ # 挂载数据初始化sql脚本
- ./deployment/mysql/sql:/opt/sql
ports:
- "3306:3306"
restart: always
ywpt-wsgi:
build: .
image: "ywpt:latest"
container_name: "gunicorn"
expose:
- "9000"
volumes:
- .:/opt/ywpt
command: bash start.sh
links:
- db
- redis
depends_on:
- db
- redis
restart: always
ywpt-asgi:
image: "ywpt:latest"
container_name: "daphne"
expose:
- "9001"
volumes:
- .:/opt/ywpt
command: bash -c "daphne -b 0.0.0.0 -p 9001 ywpt.asgi:application"
links:
- redis
depends_on:
- redis
restart: always
nginx:
build: deployment/nginx
container_name: "nginx"
ports:
- "8081:8081"
expose:
- "8081"
volumes:
- ./vue_ywpt:/opt/ywpt_html # 挂载静态文件
- ./deployment/nginx/ywpt.conf:/etc/nginx/conf.d/ywpt.conf # 挂载配置文件
- ./ywpt/media:/opt/ywpt/ywpt/media
- ./ywpt/download/applogs:/opt/ywpt/ywpt/download/applogs # 下载日志目录
links:
- ywpt-wsgi
- ywpt-asgi
depends_on:
- ywpt-wsgi
- ywpt-asgi
restart: always
jenkins:
build: deployment/jenkins
container_name: "jenkins"
ports:
- "8080:8080"
volumes:
- ./deployment/jenkins/jenkins_home:/root/.jenkins
- ./deployment/deploy:/opt/deploy
links:
- db
depends_on:
- db
restart: always
项目Dockerfile:
# 建立python3.6 环境
FROM daocloud.io/python:3.6
# 设置python环境变量
ENV PYTHONUNBUFFERED 1
# 创建项目文件夹
RUN mkdir /opt/ywpt
# 将/opt文件夹设置为工作目录
WORKDIR /opt/ywpt
# 将当前目录加入到工作目录中(. 表示当前目录)
ADD . /opt/ywpt
# 修改apt-get源
RUN rm -f /etc/apt/sources.list
ADD ./sources.list /etc/apt/
# 更新源
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 648ACFD622F3D138
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 0E98404D386FA1D9
RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 605C66F00D6C9793
RUN apt-get update && apt-get -y install curl net-tools vim telnet gcc python3-dev
# 利用pip安装依赖
RUN pip3 install -r requirements.txt -i https://pypi.douban.com/simple/
RUN sed -i "s/query.decode(errors='replace')/query.encode('utf-8').decode(errors='replace')/g" /usr/local/lib/python3.6/site-packages/django/db/backends/mysql/operations.py
start.sh
#!/bin/bash
rm -rf *.pid
export PYTHONOPTIMIZE=1
celery -A ywpt worker -l info --logfile=logs/celery-worker.log --pidfile=celery-worker.pid 2>&1 &
celery -A ywpt beat -l info > logs/celery-beat.log 2>&1 &
gunicorn ywpt.wsgi:application -c gunicorn.conf # 用 gunicorn 启动 django 服务
gunicorn.conf
from gevent import monkey
from multiprocessing import cpu_count
monkey.patch_all()
bind = "0.0.0.0:9000" # 监听内网端口9000
proc_name = 'ywpt' # 进程名称
workers = cpu_count() * 2 + 1 # 并行工作进程数
threads = cpu_count() * 2 # 指定每个工作者的线程数
backlog = 4096
worker_class = "gevent" # 工作模式协程
worker_connections = 100
timeout = 180 # 超时时间
graceful_timeout = 10 # 接收到restart信号后,worker可以在graceful_timeout时间内,继续处理完当前requests
nginx Dockerfile:
# nginx镜像
FROM daocloud.io/nginx
# 删除原有配置文件,创建静态资源文件夹
RUN rm /etc/nginx/conf.d/default.conf \
&& mkdir -p /opt/ywpt_html
容器内nginx配置ywpt.conf
upstream ywpt {
server 127.0.0.1:9000;
}
upstream ywpt-channels-backend {
server 127.0.0.1:9001;
}
server {
listen 8081;
server_name _;
client_max_body_size 500M;
set $addr "容器外nginx IP:8081";
location = /ywpt {
rewrite ^ http://$addr/ywpt/; #访问地址
}
location /ywpt/ {
alias /opt/ywpt_html/;
try_files $uri $uri/ /ywpt/index.html;
error_page 404 /;
}
location ^~/api {
#proxy_pass http://ywpt; # 主机部署
proxy_pass http://ywpt-wsgi:9000; # 容器部署
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffer_size 64k;
proxy_buffers 32 32k;
proxy_busy_buffers_size 128k;
keepalive_timeout 600;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
}
location /media {
alias /opt/ywpt/ywpt/media;
}
location ^~/websocket {
try_files $uri @proxy_to_app;
}
location @proxy_to_app {
#proxy_pass http://ywpt-channels-backend; # 主机部署
proxy_pass http://ywpt-asgi:9001; # 容器部署
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location /download {
alias /opt/ywpt/ywpt/download/applogs/;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
}
jenkins Dockerfile:
FROM centos:7.9.2009
ADD tomcat8080.tar.gz /opt
ADD jdk-8u202-linux-x64.tar.gz /usr/local
ADD apache-maven-3.6.3-bin.tar.gz /usr/local
ADD node-v16.13.2-linux-x64.tar.xz /usr/local
ADD yarn-v1.22.18.tar.gz /usr/local
WORKDIR /opt/tomcat8080
ADD requirements.file /tmp
RUN ln -s /usr/local/jdk1.8.0_202 /usr/local/jdk
RUN ln -s /usr/local/apache-maven-3.6.3 /usr/local/maven
RUN ln -s /usr/local/node-v16.13.2-linux-x64 /usr/local/node
RUN ln -s /usr/local/yarn-v1.22.18 /usr/local/yarn
ENV JAVA_HOME /usr/local/jdk
ENV JRE_HOME ${JAVA_HOME}/jre
ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib
ENV MAVEN_HOME=/usr/local/maven
ENV NODE_HOME=/usr/local/node
ENV YARN_HOME=/usr/local/yarn
ENV PATH ${JAVA_HOME}/bin:${MAVEN_HOME}/bin:${NODE_HOME}/bin:${YARN_HOME}/bin:$PATH
RUN yum -y install epel-release && yum -y install python-pip python-devel mysql-devel gcc gcc-c++ libffi-devel unixODBC-devel git kde-l10n-Chinese glibc-common && pip install --upgrade pip==20.2.4 -i https://pypi.douban.com/simple/
RUN pip install -r /tmp/requirements.file -i https://pypi.douban.com/simple/
RUN localedef -c -f UTF-8 -i zh_CN zh_CN.utf8
RUN export LANG=zh_CN.UTF-8
RUN echo "export LANG=zh_CN.UTF-8" >> /etc/locale.conf
ENV LANG zh_CN.UTF-8
ENV LC_ALL zh_CN.UTF-8
EXPOSE 8080
ENTRYPOINT [ "/opt/tomcat8080/bin/catalina.sh", "run" ]
容器外nginx配置,实现二层nginx转发(特殊需求,一般不需要)
server {
listen 8081;
server_name localhost;
set $ywpt_addr "nginx容器宿主机IP:8081";
set $jenkins_addr "nginx容器宿主机IP:8080";
access_log /opt/nginx-1.20.2/logs/ops_access.log;
error_log /opt/nginx-1.20.2/logs/ops_error.log;
location / {
root /opt/nginx-1.20.2/html;
index index.html index.htm;
}
location /ywpt {
proxy_store off;
proxy_redirect http:// $scheme://;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_pass http://$ywpt_addr;
keepalive_timeout 600;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
location ^~/websocket {
proxy_pass http://$ywpt_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
keepalive_timeout 600;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
location ^~/media {
proxy_pass http://$ywpt_addr;
}
location /api {
proxy_store off;
proxy_redirect http:// $scheme://;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_pass http://$ywpt_addr;
keepalive_timeout 600;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
location /download {
proxy_store off;
proxy_redirect http:// $scheme://;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_pass http://$ywpt_addr;
keepalive_timeout 600;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
location /jenkins {
proxy_pass http://$jenkins_addr;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
}
}
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'ywpt',
'USER': 'root',
'PASSWORD': '22222',
'HOST': 'db', # 主机地址(容器部署)
# 'HOST': '127.0.0.1', # 主机地址
'PORT': '3306',
'OPTIONS': {'init_command': 'SET default_storage_engine=INNODB;', 'charset': 'utf8mb4'}
}
}
# Redis
REDIS_PWD = os.getenv('REDIS_PWD', '11111') # 密码不能有特殊字符
REDIS_HOST = os.getenv('REDIS_HOST', 'redis') # 主机地址(容器部署)
# REDIS_HOST = os.getenv('REDIS_HOST', '127.0.0.1') # 主机地址
REDIS_PORT = os.getenv('REDIS_PORT', 6379)
REDIS_DB = os.getenv('REDIS_DB', 1)
if REDIS_PWD:
REDIS_STR = f':{REDIS_PWD}@'
else:
REDIS_STR = ''
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': f'redis://{REDIS_STR}{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
},
}
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [f"redis://{REDIS_STR}{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}"],
"symmetric_encryption_keys": [SECRET_KEY],
'capacity': 1500,
'expiry': 10,
},
},
}
VUE_APP_BASE_API = 'http://容器外nginx IP:8081'
docker-compose build
docker-compose up -d
容器外nginx IP:8081/ywpt
容器外nginx IP:8081/jenkins