[Flask] 面向生产环境的高并发部署解决方案(gunicorn + gevent + supervisor)
文章目录
- 1.安装docker镜像
- 2.查看alphin版本
- 3.更换alpine源
- 4.更换pip源
- 5.安装requirements.txt
- 6.安装完成
- 7.启动flask应用
- 8.用gunicorn启动flask应用
- 9.使用supervisor启动gunicorn
- 10.镜像分享
历时两天,构建了基于python的微服务环境,该方案可用于生产环境,再搭配一个Nginx进行反向代理和负债均衡,可满足高并发需求。
技术方案为:python + Flask + gunicorn + gevent + supervisor + Docker
本文主要搭建图中Python运行环境部分:
- Python:这里没有要求,我用的3.7
- Flask:Python Web开发中最火的玄冥二老之一(另一个是Django),它最大的特点就是轻量级
- Gunicorn:熟悉JAVA 或者 PHP 做开发的可能对 Python的应用部署还是有些懵的,Flask应用是一个符合WSGI规范的Python应用,不能单独运行,需要依赖其他的组件提供服务器功能
- Gevent:Gunicorn 默认使用同步阻塞的网络模型(-k sync),对于高并发的访问并不太友好,所以我们需要使用gevent来提高并发量
- supervisor:用来守护gunicorn,当进程挂掉后自动重启
- Dokcer:容器!你也可以把它理解为一个“盒子”。有时候我们会倦于管理项目的部署和维护。如果使用容器封装项目,那么只需要维护一个配置文件完成部署需求,包括后续将整个部署的过程完全自动化,部署就会变得更便捷
简介引用:https://blog.****.net/qiulin_wu/article/details/105507785
1.安装docker镜像
基础镜像python:3.7-Alpine,提供python环境。
docker pull python:3.7-Alpine
首次启动:
docker run --name python -p 5000:5000 -v G:\docker\share:\tmp\share -i -t python:3.7-alpine
说明:
-p是将flask的5000端口映射出来
-v 是将window的目录映射给容器,用于保存测试代码
创建容器后启动,可配置环境:
docker exec -it 容器ID或name /bin/sh
2.查看alphin版本
查看python版本:
3.更换alpine源
vi /etc/apk/repositories
添加
http://mirrors.aliyun.com/alpine/v3.16/main/
http://mirrors.aliyun.com/alpine/v3.16/community/
注意:版本号一定要匹配,这里是3.16
更新源:
apk update && apk upgrade
注意:
sed -i ‘s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g’ /etc/apk/repositories
在每次apk 安装前运行一次,也是有用的
4.更换pip源
(1)在用户根目录下 ~ 下创建 .pip 隐藏文件夹,如果已经有了可以跳过
mkdir ~/.pip
(2)进入 .pip 隐藏文件夹并创建 pip.conf 配置文件
cd ~/.pip && touch pip.conf
(3)新增 pip.conf 配置文件内容
vi pip.conf
[global]
index-url = http://pypi.douban.com/simple
[install]
use-mirrors =true
mirrors =http://pypi.douban.com/simple/
trusted-host =pypi.douban.com
(4)更新pip:
pip install --upgrade pip
5.安装requirements.txt
默认的库只有3个:
cd /usr/local/python
vi requirements.txt
文件内容:
Flask==1.1.2
gunicorn==20.1.0
gevent==21.12.0
eventlet==0.33.1
greenlet==1.1.2
pandas==1.3.5
numpy==1.19.2
celery==5.2.6
Jinja2==3.0.3
itsdangerous==2.0.1
Werkzeug==2.0.2
supervisor==4.2.4
其中若没有安装g ++会报错,pandas和numpy安装会报错:
报错:
pandas报错,需要先安装numpy
numpy报错:
RuntimeError: Broken toolchain: cannot link a simple C program
解决办法:
https://www.cnblogs.com/wxx999/p/16142681.html
需要安装g++!问题解决
sed -i ‘s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g’ /etc/apk/repositories
apk add --no-cache g++
安装完g++后执行:
pip install -r requirements.txt
6.安装完成
包数量变成47个了
所以安装的不仅是明面上的那几个。
生成的镜像有400多M。
7.启动flask应用
在本地创建文件夹和一个测试工程:
main.py
# coding=utf-8
# hello world
from flask import
Flask
#创建应用程序
app =
Flask(__name__)
# 写一个函数来处理浏览器发送过来的请求
@app.route("/") #当访问网址时,默认执行下面函数
def index():
return 'weclome
to Rocket!!!'
if __name__ == "__main__":
# host 需要设置成 0.0.0.0 才能在容器外部访问
app.run(host='0.0.0.0', port=5000, debug=True)
gunicorn/config.py
# filename:gunicorn.conf.py
# 绑定ip和端口号
bind = '0.0.0.0:5000'
# 设置守护进程,将进程交给supervisor管理
daemon = 'false'
# 监听队列
backlog = 512
# 超时
timeout = 30
# 使用gevent模式,还可以使用sync 模式,默认的是sync模式
worker_class = 'gevent'
# 进程数
# workers = multiprocessing.cpu_count() * 2 + 1
workers = 4
# 指定每个进程开启的线程数
threads = 2
# 设置最大并发量
worker_connections = 2000
# 设置访问日志路径
accesslog = "/tmp/share/flask_logs/access.log"
# 设置错误日志路径
errorlog = "/tmp/share/flask_logs/debug.log"
# 日志级别,这个日志级别指的是错误日志的级别,而访问日志的级别无法设置
loglevel = 'info'
复制工程到容器:
docker cp G:\docker\share\flask_demo python:/tmp/share
G:\docker\share\flask_demo:是window系统下的项目路径
/tmp/share:是容器内的路径(需要先在tmp下创建一个share)
执行启动文件:
python /tmp/share/ flask_demo/main.py
成功启动如下,
可以在window浏览器输入:http://localhost:5000/
8.用gunicorn启动flask应用
cd /tmp/share/flask_demo
启动方式一(基于参数):
gunicorn -w 2 -b 0.0.0.0:5000 main:app
-w 表示2个进程
-b 表示主机地址和端口
main: 表示main.py
可以在window浏览器输入:http://localhost:5000/,仍然可以看到
启动方式二(基于配置文件):
gunicorn -c gunicorn/config.py main:app
此时没有打印信息,但在浏览器可以正常访问,日志保存在配置文件中规定的日志路径:
# 设置访问日志路径
accesslog = "/tmp/share/flask_logs/access.log"
# 设置错误日志路径
errorlog = "/tmp/share/flask_logs/debug.log"
方式三:
nohup gunicorn -c $(pwd)/config.py app:app &> ./logs/uwsgi.log &(没试过)
使用 nohup 使应用始终保持后台运行,并记录相应的日志信息
附加:结束gunicorn服务进程
参考:
使用下面命令找出gunicorn所有进程。
ps -ef | grep gunicorn
[root@VM_0_12_centos ~] ps -ef|grep gunicorn
xxx 19950 17074 0 17:13 pts/2 00:00:00 /home/xxx/anaconda3/bin/python /home/xxx/anaconda3/bin/gunicorn -c gunicorn/config.py app:app
xxx 19968 19950 0 17:13 pts/2 00:00:00 /home/xxx/anaconda3/bin/python /home/xxx/anaconda3/bin/gunicorn -c gunicorn/config.py app:app
xxx 20264 20141 0 17:13 pts/1 00:00:00 grep --color=auto gunicorn
然后使用 kill -9 进程ID 命令来杀掉相关进程
[root@VM_0_12_centos ~] kill -9 19950
[root@VM_0_12_centos ~] kill -9 19968
[root@VM_0_12_centos ~] ps -ef | grep gunicorn
杀掉进程后,稍等几秒,再使用ps -ef | grep gunicorn查看,发现gunicorn服务进程已全部杀掉。
9.使用supervisor启动gunicorn
教程:
1.https://www.jianshu.com/p/3e1f7b8b6214?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
2.https://blog.****.net/qq_39146974/article/details/107630717
3.https://blog.****.net/wumian0123/article/details/106614272
(1)创建软连接:
ln -s /usr/local/bin/supervisord /usr/bin/supervisord
ln -s /usr/local/bin/supervisorctl /usr/bin/supervisorctl
(2)生成配置文件:
python3不支持生成配置的命令,所以需要再额外安装python2的supervisor,然后在python3环境下使用python2生成的配置文件。。。(实测可以生成配置)
mkdir /etc/supervisor #创建supervisor,若存在请忽略
mkdir /etc/supervisor/conf.d #创建conf.d,若存在请忽略
echo_supervisord_conf > /etc/supervisor/supervisord.conf 生成配置文件
报错:
原因:可能是没有安装supervisor, pip list确认一下
vim /etc/supervisor/supervisord.conf
改两处:
a.
将末尾的[include]部分改为:
[include]
files = /etc/supervisor/conf.d/*.conf
b.
在[supervisord]下面添加**environment=LC_ALL=‘en_US.UTF-8’,LANG=‘en_US.UTF-8’**解决编码问题
(3) 编辑自定义配置文件
在/etc/supervisor/conf.d/目录下创建一个配置文件(用于a的include),如flask_demo.conf,flask_demo是项目名称,这个配置文件是专门用于配置项目的。
vi /etc/supervisor/conf.d/flask_demo.conf
添加以下内容:
版本一:
[program:flask_demo]
;项目工作目录(脚本启动目录的全路径)
directory = /home/rd/xxx
;启动命令
command = gunicorn -c gunicorn.conf.py app:app
;启动时间
startsecs = 0
;终止等待时间
stopwaitsecs = 0
;supervisord守护程序启动时自动启动tornado
autostart = true
;supervisord守护程序重启时自动重启tornado
autorestart = true
;确保关闭supervisord时停止所有相关子进程
stopasgroup=true
;确保关闭supervisord时停止所有相关子进程
killasgroup=true
;log 日志
stderr_logfile=/tmp/share/supervisor_logs/error.log
版本二:
解释更清楚
[program:flask_demo]
;如果有多个命令,之间用&&分隔
command=/home/user/python/python_virtual/flask_app/bin/gunicorn -c /home/user/python/flask_app/config_g.py manager:app
directory=/home/user/python/flask_app ; 程序(gunicorn)的启动目录,即manger.py所在目录
;redirect_stderr=true的时候,stderr也会写进这个日志文件
stdout_logfile=/home/user/python/flask_app/log/supervisor_stdout.log
; 记录控制台的输出,很有用,因为gunicorn的日志无法记录控制台的输出
stderr_logfile=/home/user/python/flask_app/log/supervisor_stderr.log
; 程序中配置的日志,但实际只有warning以上级别的才会记录,所以没什么用;子进程中没有不能设置日志等级
autostart=true
;如果是true,子进程将在supervisord启动后被自动启动,默认就是true
autorestart=true
;这个是设置子进程挂掉后自动重启的情况,有三个选项,false,true和unexpected。如果为false,无论什么情况下,都不会被重新启动;如果为true,只要子进程挂掉,将会被无条件重启;如果为unexpected,只有子进程异常中断时才会重新启动。
startsecs=10
; 子进程启动多少秒之后,此时状态如果是running,则认为启动成功了
priority=997
;值越大优先级越低
版本一的省略版:(我使用的这个)
有注释可能无法显示中文,可复制无注释版本:
[program:flask_demo]
directory = /tmp/share/flask_demo
command = gunicorn -c /tmp/share/flask_demo/gunicorn/config.py main:app
startsecs = 0
stopwaitsecs = 0
autostart = true
autorestart = true
stopasgroup=true
killasgroup=true
stderr_logfile=/tmp/share/supervisor_logs/error.log
command为gunicorn的启动命令,-c后面为项目中的gunicorn的配置文件,这里命令的写法参考8
注意:需要创建日志路径/tmp/share/supervisor_logs
(4)启动supervisor
supervisord -c /etc/supervisor/supervisord.conf
-c后面的就是前面生成的配置文件,注意这个名字在一些教程中可能是supervisor.conf,我们生成时用的是什么名称,这里就用什么名称。
(5)supervisorctl一些常见的命令:
运行supervisorctl命令,不加参数,会进入supervisor客户端的交互终端
Ÿ supervisorctl status --查看所有子进程状态
Ÿ supervisorctl start/stop/restart 子进程名 --开启或停止子进程,都不会载入最新的配置文件
Ÿ supervisorctl reload --重启supervisord服务,载入最新的子进程配置文
Ÿ supervisorctl reread --读取有更新(增加)的配置文件,不会启动新添加的子进程
Ÿ supervisorctl update --重启配置文件修改过的子进程
总结:reload重启所有,而update仅重启更新了配置的,reread好像没什么用(因为仅更新不重启)
显示进程运行成功!,访问浏览器,也能正常访问http://localhost:5000/
注意:网上都说supervisor在python3下可能会出现问题,所以请多留意一下。
10.镜像分享
针对上述的操作,我打包了两个镜像到docker hub,可直接pull 使用。
push教程见我的另一篇文章:docker生成镜像并push到docker Hub(附denined:requested access to the resource is denied解决办法)
这里有两个镜像,欢迎大家使用。
docker pull tangzhaoxiang2022/py3-flask-gunicorn:latest
docker pull tangzhaoxiang2022/py3-flask-gunicorn:supervisor
简单说明一下:
根据镜像pull和run之后(此处不展开了),需要进入到容器内部:
docker exec -it 容器名 /bin/sh
此时进入到alpine系统里,根据不同的镜像执行不同的启动命令:
- tag为lastest的镜像不带superiovisor,其启动方式为gunicorn命令行启动(详见8)
cd /tmp/share/flask_demo
gunicorn -w 2 -b 0.0.0.0:5000 main:app
- tag为supervisor 的镜像带superiovisor,其启动方式为supervisor启动(详见9)
supervisord -c /etc/supervisor/supervisord.conf