从零开始 - Docker部署前后端分离项目(三)
一、 项目框架图
此django项目部署采用的方案****nginx+gunicorn
容器环境及需要启动的服务:
- python3.8的 (使用的是小型的3.8-alpine)
- celery worker
- celery beate server
- gunicorn
二、文件最终目录
AndroidPlatform |- nginx_docker |-- dist |-- static |-- default.conf |-- Dockerfile |- XZAndroidPlatform |-- logs |-- scripts |-- xiaozai_app |-- XZAndroidPlatform |-- Dockerfile |-- entrypoint.sh |-- gunicorn.conf.py |-- manage.py |-- requirements.txt |-- supervisord.conf |- docker-compose.yml |- start.sh
三、项目文件介绍
1.AndroidPlatform
整个项目文件,里面有两个文件夹,nginx_docker用于存放前后端静态文件,XZAndroidPlatform是整个django项目文件。
包含两个文件,docker-compose.yml、start.sh(可有可无)
2.XZAndroidPlatform后端文件
整个django项目拿过来即可。
需生成的文件为:requirements.txt
需编写的文件为:Dockerfile、entrypoint.sh、gunicorn.conf.py、supervisord.conf
3.nginx_docker静态文件
需生成的文件为:包含前端vue打包的dist文件,django后端的static静态资源文件。
需编写的文件为:default.conf、Dockerfile
四、django后端环境准备
1、django后端项目依赖导出
pip freeze > requirements.txt
在requirements文件中增加相关部署依赖:
mysqlclient==2.1.0 gunicorn==20.1.0 supervisor==4.2.4
完整requirements.txt文件如下
BeautifulReport==0.1.3 certifi==2021.10.8 charset-normalizer==2.0.12 Django==2.2 django-cors-headers==3.11.0 idna==3.3 lxml==4.8.0 python-docx==0.8.11 pytz==2021.3 requests==2.27.1 sqlparse==0.4.2 urllib3==1.26.9 mysqlclient==2.1.0 gunicorn==20.1.0 supervisor==4.2.4
2、django后端项目配置修改
setting.py 文件
DEBUG = False ALLOWED_HOSTS = ['127.0.0.1', '填你所部署服务所在的IP'] # 运行主机,或域名,可以填['*']代表允许所有 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', #第三方解决跨域头问题的中间件 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'xiaozai', 'HOST': 'mariadb', # 以mariadb的容器名为host 'USER': 'root', 'PASSWORD': 'xiaozai123' } } # 方案一:允许跨域的域名列表 CORS_ALLOWED_ORIGINS = [ 'http://198.162.111.111:8080' # 修改为你的前端部署的项目地址 ] # 方案二:我采用的 CORS_ORIGIN_ALLOW_ALL = True #加上允许所有跨域
3、收集django项目中的静态文件
在配置文件中配置STATIC_ROOT
STATIC_ROOT = BASE_DIR / 'static' #如果运行报错,则用下面的 STATIC_ROOT = = os.path.join(BASE_DIR,'static')
然后运行命令:
python manage.py collectstatic
会将所有的静态收集到STATIC_ROOT
目录中。
这个static文件夹后续需要复制到nginx_docker文件夹中。
4、配置文件
django项目目录下新建logs文件夹,存放后续配置运行的日志。
(1)gunicorn.conf.py
bind = '0.0.0.0:8000' # 监听主机和端口 pidfile = 'logs/gunicorn.pid' # pid文件 accesslog = 'logs/gunicorn_access.log' # 通过的日志 errorlog = 'logs/gunicorn_error.log' # 错误日志
(2)supervisord.conf
把XZAndroidPlatform.wsgi修改为你django的项目名.wsgi
[unix_http_server] file=/tmp/supervisor.sock ; the path to the socket file [supervisord] logfile=logs/supervisord.log ; main log file; default $CWD/supervisord.log logfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB logfile_backups=10 ; # of main logfile backups; 0 means none, default 10 loglevel=info ; log level; default info; others: debug,warn,trace pidfile=logs/supervisord.pid ; supervisord pidfile; default supervisord.pid nodaemon=true ; start in foreground if true; default false silent=false ; no logs to stdout if true; default false minfds=1024 ; min. avail startup file descriptors; default 1024 minprocs=200 ; min. avail process descriptors;default 200 [rpcinterface:supervisor] supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface [supervisorctl] serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket [program:gunicorn] command=gunicorn -c gunicorn.conf.py XZAndroidPlatform.wsgi
(3)entrypoint.sh
小知识
/dev/null 文件
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
$ command > /dev/null
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
创建的超级用户账号密码,可以自己自行更改。
#! /bin/sh # 1. 数据库迁移 python manage.py makemigrations python manage.py migrate # 因为后使用脚本部署时,可能数据库还没有准备好,这里连接会失败 if [ $? -ne 0 ];then echo '数据库连接失败重启' exit 1 fi # 2.创建管理员用户 echo "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('xiaozai', '13888888888', 'xiaozai123') " | python manage.py shell &> /dev/null # 3. 启动supervisor supervisord -c supervisord.conf
(4)Dockerfile
FROM python:3.8-alpine LABEL maintainer='xiaozai' LABEL description='Django project' # 创建/app目录并切换进目录下 # 第一个WORKDIR要用绝对路径 WORKDIR /app # 把dockerfile当前目录下的所有文件拷贝进镜像的/app目录下 COPY . . # 安装必要的库 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \ apk update && \ apk upgrade && \ apk add --no-cache tzdata mariadb-dev gcc libc-dev && \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ echo "Asia/Shanghai" > /etc/timezone && \ python -m pip install -i https://pypi.douban.com/simple --upgrade pip && \ pip install --no-cache-dir -i https://pypi.douban.com/simple -r requirements.txt && \ chmod 777 ./entrypoint.sh # 创建一个日志挂载点避免容器越来越大 VOLUME /app/logs/ # 挂载端口,它并不会做端口映射,只是告诉用户,该镜像的挂载端口 EXPOSE 8000 # 容器的默认执行命令 # CMD 'supervisord -c supervisord.conf' ENTRYPOINT ["./entrypoint.sh"]
五、nginx_docker静态资源准备
讲上文中django打包好的static文件夹复制到nginx_docker文件夹中。
1、前端部署-前端vue项目
前端中所有用到后端的接口,都需要改成对应部署所在的服务器地址,如上文的192.168.111.111
打包前端项目打包的时候一定要修改host,打包完成之后,将dist文件夹拷贝到nginx_docker文件夹中。
打包命令
npm run build
2、部署静态文件default.conf
在nginx_docker
文件夹中创建default.conf
文件,编写如下内容:
域名记得改成自己的
upstream app_server { server XZAndroidPlatform:8000; # 配置gunicorn服务器主机和端口 } server { listen 8001; # 配置后端服务的监听端口 server_name 192.168.111.111; # 配置域名 # http://106.14.168.21:8000/static/rest_framework/css/bootstrap.min.css # /usr/share/nginx/html/static/rest_framework/css/bootstrap.min.css location /static/ { alias /usr/share/nginx/html/static/; # 配置静态文件路径 } location / { # 检查静态文件,如果不是代理到应用 try_files $uri @proxy_to_app; } location @proxy_to_app { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://app_server; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } server { listen 80; # 配置前端项目服务的监听端口 server_name 192.168.111.111; # 配置域名主机 root /usr/share/nginx/html/dist; # 配置文件根路径 location / { try_files $uri $uri/ @router; #需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404 index index.html; } #对应上面的@router,主要原因是路由的路径资源并不是一个真实的路径,所以无法找到具体的文件 #因此需要rewrite到index.html中,然后交给路由在处理请求资源 location @router { rewrite ^.*$ /index.html last; } }
3、Dockerfile
FROM nginx:alpine COPY ./static/ /usr/share/nginx/html/static/ COPY ./dist/ /usr/share/nginx/html/dist/ COPY ./default.conf /etc/nginx/conf.d/ VOLUME /var/log/ EXPOSE 8001 80
六、docker-compose.yml
下载docker-compose
pip install docker-compose
检测是否安装成功
docker-compoese --version
不论是Dockerfile
还是docker-compose.yml
脚本的编写都依赖上下文,所以需要明确部署文件夹的项目结构。
项目结构在本节开篇已经详细说明了,这里再写一个大致的目录
AndroidPlatform |- nginx_docker |-- dist |-- static |-- default.conf |-- Dockerfile |- XZAndroidPlatform |-- 其他项目文件和文件夹 |-- Dockerfile |-- entrypoint.sh |- docker-compose.yml
1、编写docker-compose.yml文件
version: "3" services: # 要启动的 容器服务 redis: # 容器名称 image: redis:alpine # 指定构建的镜像 restart: always # 重启策略 volumes: # 指定数据卷 - redis_data:/data mariadb: image: mariadb restart: always environment: # 设置环境变量 MARIADB_ROOT_PASSWORD: xiaozai123 MARIADB_DATABASE: xiaozai volumes: - mariadb_data:/var/lib/mysql XZAndroidPlatform: depends_on: # 指定依赖服务,等依赖的容器启动后再启动 - redis - mariadb build: ./XZAndroidPlatform/ # 指定Dockerfile的路径 image: mwj_XZAndroidPlatform # 给镜像取个tag volumes: - app_logs:/app/logs/ restart: always nginx: depends_on: - XZAndroidPlatform build: ./nginx_docker/ image: mwj_nginx restart: always ports: - "80:80" - "8001:8001" volumes: - nginx_logs:/var/log/ volumes: # 定义的数据卷 redis_data: mariadb_data: app_logs: nginx_logs:
然后再XZAndroidPlatform
目录下运行命令docker-compose -p mwj up -d --build
,即可以部署项目。
2、部署完成
下篇内容:
1、start.sh一键自动化部署脚本的编写。
2、如何删除已构建的docker项目,再次快速部署。