【flask】适合生产环境的高并发部署方案(gunicorn + gevent + supervisor)

发布于:2023-01-18 ⋅ 阅读:(504) ⋅ 点赞:(0)

历时两天,构建了基于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.csdn.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.csdn.net/qq_39146974/article/details/107630717

3.https://blog.csdn.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

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到