Docker打包部署Django+Mysql+Redis+Daphne+Gunicorn+Nginx+Jenkins+Vue前端

目的

实现快速部署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打包部署Django+Mysql+Redis+Daphne+Gunicorn+Nginx+Jenkins+Vue前端_第1张图片
Docker打包部署Django+Mysql+Redis+Daphne+Gunicorn+Nginx+Jenkins+Vue前端_第2张图片

编排文件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;
    } 
}

django配置修改

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

你可能感兴趣的:(django,python)