docker 部署 flask 项目
docker 部署 flask 项目
项目结构说明
nginx 做反向代理,gunicorn 启动 flask,flask 连接 mysql
镜像
- nginx:1.21.3
- mysql:8
- flask_app(以 python 镜像为基础构建的项目镜像)
docker 版本:
- Docker version 20.10.9
- Docker Compose version v2.0.1
镜像说明
nginx
-
处理前端页面及静态文件请求;
-
代理 flask 的后端服务;
-
nginx 配置
#user nobody; worker_processes 4; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; log_format main '$remote_addr [$request_time $upstream_response_time] [$time_local -$msec] [$request] [$request_body] ' '$status $body_bytes_sent ' '"$http_user_agent"' '$scheme $server_addr $request_uri'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 600; #gzip on; upstream flask_app { server 172.16.238.10:5000; } server { listen 8080; server_name localhost; server_tokens off; # 前端页面 location /index.html { root /usr/share/nginx/html/web; index index.html index.htm; } location /logo.png { root /usr/share/nginx/html/web; index index.html index.htm; } location /css { root /usr/share/nginx/html/web; index index.html index.htm; } location /js { root /usr/share/nginx/html/web; index index.html index.htm; } location /img { root /usr/share/nginx/html/web; index index.html index.htm; } location /fonts { root /usr/share/nginx/html/web; index index.html index.htm; } # 后端接口 proxy_connect_timeout 600; proxy_read_timeout 600; proxy_send_timeout 600; location / { proxy_pass http://flask_app; 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; client_max_body_size 1024m; } } }
mysql
-
将数据库结构和初始数据导出为 sql 脚本,启动 mysql 容器时指定执行 mysql 初始化脚本
-
mysql 初始化脚本
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; CREATE flask_app; USE flask_app; -- ---------------------------- -- Table structure for student -- ---------------------------- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` int(0) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `phone_num` varchar(13) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of student -- ---------------------------- INSERT INTO `student` VALUES (1, 'tom', '13549872211'); INSERT INTO `student` VALUES (2, 'Jerry', '13549872212'); SET FOREIGN_KEY_CHECKS = 1;
-
mysql 配置
[mysqld] user=mysql character-set-server=utf8 default_authentication_plugin=mysql_native_password lower_case_table_names=1 secure_file_priv=/var/lib/mysql table_definition_cache=400 [client] default-character-set=utf8 [mysql] default-character-set=utf8
flask_app
镜像构建方式
构建 flask app 的镜像一般有两种方式:
- 直接以 python 镜像为基础,创建容器,再在容器中部署完成,最后导出为镜像;
- 使用Dockerfile构建镜像;
第一种比较麻烦,并且每次更新代码都需要重新手动构建镜像,因此使用Dockerfile
方式
gunicorn 配置
import multiprocessing
# 监听的端口
bind = "0.0.0.0:5000"
# 防止在服务器上 work 启动过多,限制 work 数量
workers = multiprocessing.cpu_count() * 2 + 1 if multiprocessing.cpu_count() * 2 + 1 <= 6 else 6
backlog = 20480
debug = False
timeout = 500
errorlog = './log/gunicorn_error.log'
启动脚本
start.sh
在容器启动时运行,使用gunicorn
启动 flask_app
- 由于在
Dockerfile
中配置了WORKDIR
,因此start.sh
的执行环境默认就在WORKDIR
下 - 需要注意
start.sh
的换行符,需要设置为LF
#!/bin/bash
mkdir log
gunicorn -c gun.conf app:app --daemon
# 保留一个 bash
/bin/bash
Dockerfile 配置
# python 基础镜像版本
FROM python:3.8.12-slim
# 将代码复制到镜像中的目录下
COPY ./ /usr/local/flask_app/
# 设置工作目录,容器运行时,命令行默认就在这个目录
WORKDIR /usr/local/flask_app/
# apt-get 更换国内源
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
sed -i 's|security.debian.org/debian-security|mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list && \
apt-get update
# 安装环境
RUN chmod +x start.sh \
&& apt-get install -y -q -o Acquire::http::Pipeline-Depth=0 gcc \
&& apt-get clean \
&& pip3 install -r requirements.txt -i https://pypi.douban.com/simple
# 启动容器时,执行脚本
ENTRYPOINT ["./start.sh"]
构建镜像
- 执行构建命令时,将项目文件放入
flask_app
目录下,目录结构如下:--- flask_app --- Dockerfile --- start.sh --- app.py --- gun.py --- requirements.txt --- ... 其他项目文件
- 在
flask_app
目录下执行docker build -t flask_app .
命令创建镜像
使用 docker-compose
单独去启动容器比较麻烦,使用 docker-compose 来管理容器更加方便,需要创建docker-compose.yml
文件
docker-compose 配置文件
version: '3.3'
services:
mysql:
image: mysql:8
container_name: mysql_container
restart: always
command: bash -c "chmod 644 /etc/mysql/my.cnf && /entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password"
volumes:
# 挂载 my.cnf 配置文件
- ./docker_volumes/mysql/my.cnf:/etc/mysql/my.cnf
# 初始化数据库,创建容器时会执行 init 下的 sql 语句
- ./docker_volumes/mysql/init:/docker-entrypoint-initdb.d/
environment:
# 设置密码
- 'MYSQL_ROOT_PASSWORD=1234'
# 设置时区
- 'TZ=Asia/Shanghai'
ports:
# 映射端口,如果不用外部访问可以不配置
- 3306:3306
logging:
driver: 'json-file'
options:
max-size: '100m'
networks:
flask_app_net:
ipv4_address: 172.16.238.3
nginx:
image: nginx:1.21.3
container_name: nginx_container
restart: always
volumes:
- ./docker_volumes/nginx/cert:/etc/nginx/cert
- ./docker_volumes/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
ports:
- '8080:8080'
environment:
- 'TZ=Asia/Shanghai'
logging:
driver: 'json-file'
options:
max-size: '100m'
networks:
flask_app_net:
ipv4_address: 172.16.238.7
flask_app:
image: flask_app
container_name: flask_app
restart: always
tty: true
environment:
- 'TZ=Asia/Shanghai'
volumes:
# 挂载目录
logging:
driver: 'json-file'
options:
max-size: '100m'
depends_on:
- mysql
networks:
flask_app_net:
ipv4_address: 172.16.238.10
# 创建一个虚拟网络,固定每个容器的 ip
networks:
flask-app-net:
name: flask_app_net
ipam:
driver: default
config:
- subnet: '172.16.238.0/24'
启动
目录结构
- 目录结构
--- docker_demo --- docker-compose.yml --- docker_volumes --- mysql --- init --- init.sql # 数据库初始化 --- my.cnf # mysql 配置 --- nginx --- cert # nginx 证书 --- conf --- nginx.conf # nginx 配置文件
- 在
docker-compose.yml
同级目录下执行docker-compose up -d