天天看点

Supervisor和Gunicorn部署Flask项目

基本环境

最简单的Flask Web应用:

新建文件夹myproject,里面就一个文件:myapp.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hi! It works!'

if __name__=='__main__':
    app.run(host='0.0.0.0')


           

想启动该应用,只要运行python myapp.py就可以在浏览器http://localhost:5000看到运行结果了。开心!

然鹅,这里的问题是:这个应用是单进程单线程的,如果在多个用户(高并发)的场景,这个就要跪了

为什么使用Gunicorn

Flask 作为一个 Web 框架,内置了一个 webserver, 但这自带的 Server 到底能不能用?

官网的介绍:

While lightweight and easy to use, Flask’s built-in server is not suitable for production as it doesn’t scale well. Some of the options available for properly running Flask in production are documented here.

很显然,内置的 webserver 是能用的。但不适合放在生产环境。这个 server 本来就是给开发者用的。框架本身并不提供生产环境的 web 服务器,SpringBoot 这种内置 Tomcat 生产级服务器 是例外。

假设我们使用的是 Nginx+Flask Run 来当作生产环境,全部部署在一台机器上。劣势如下:

  • 单 Worker。

    只有一个进程在跑所有的请求,而由于实现的简陋性,内置 webserver 很容易卡死。并且只有一个 Worker 在跑请求。在多核 CPU 下,仅仅占用一核。当然,其实也可以多起几个进程。

  • 缺乏 Worker 的管理。

    接上,加入负载量上来了,Gunicorn 可以调节 Worker 的数量。而这个东西,内置的 Webserver 是不适合做这种事情的。一言以蔽之,太弱,几个请求就打满了。

Gunicorn(绿色独角兽)是一个Python WSGI的HTTP服务器。从Ruby的独角兽(Unicorn )项目移植。该Gunicorn服务器与各种Web框架兼容,实现非常简单,轻量级的资源消耗。Gunicorn直接用命令启动,不需要编写配置文件,相对uWSGI要容易很多。

Gunicorn 作为 Server 相对而言可以有什么提升?

  • gunicorn 的优点如下, 帮我 scale worker, 进程挂了帮我重启
  • 用 python 的框架 flask/django/webpy 配置起来都差不多。
  • 还有信号机制。可以支持多种配置。
  • 其他:在管理 worker 上,使用了 pre-fork 模型,即一个 master 进程管理多个 worker 进程,所有请求和响应均由 Worker 处理。Master 进程是一个简单的 loop, 监听 worker 不同进程信号并且作出响应。 比如接受到 TTIN 提升 worker 数量,TTOU 降低运行 Worker 数量。如果 worker 挂了,发出 CHLD, 则重启失败的 worker, 同步的 Worker 一次处理一个请求。 PS: 如果没有静态资源并且无需反向代理的话,抛弃 Nginx 直接使用 Gunicorn 和 Flask app 也能搞定。采用多进程时,只需要占用 1 个端口即可,收到的http请求自动分发到各个worker进程。同时 worker_class 选用 gevent 异步协程方式,测试发现性能会有很大提升。

架构:

nginx(反向代理)-> gunicorn(with gevent)-> flask server -> supervisor

nginx 只使用反向代理功能,不再作负载均衡。负载均衡在 gunicorn 侧实现,采用轮询方式。

Gunicorn

这时候Gunicorn登场了,它就是来解决这个高并发的问题的。它全名叫Green Unicorn,是一个被广泛使用的高性能的Python WSGI Unix HTTP服务器。

Gunicorn 是python中的WSGI容器,pre-fork worker模式,优点就是配置简单,轻量级的资源消耗,以及高性能。

安装

$ pip install gunicorn
           

相关配置:

bind = '0.0.0.0:17123'      # 监听地址和端口
backlog = 2048              # 最大挂起连接数
timeout = 30                # 超时时间
worker_class = 'gevent'     # worker 进程的工作方式。目前选用 gevent 异步协程方式
worker_connections = 65535  # 客户端最大同时连接数
workers = 1                 # 处理请求的 worker 进程数量,多进程
threads = 1                 # 每个 worker 进程的线程数。采用 gevent 协程方式时,配置 1 即可
           

使用

在命令行下,键入:

$ gunicorn -w 4 myapp:app
           

可以看到输出:

[2018-10-25 20:21:12 +0800] [28597] [INFO] Starting gunicorn 19.9.0
[2018-10-25 20:21:12 +0800] [28597] [INFO] Listening at: http://127.0.0.1:8000 (28597)
[2018-10-25 20:21:12 +0800] [28597] [INFO] Using worker: sync
[2018-10-25 20:21:12 +0800] [28600] [INFO] Booting worker with pid: 28600
[2018-10-25 20:21:12 +0800] [28601] [INFO] Booting worker with pid: 28601
[2018-10-25 20:21:12 +0800] [28602] [INFO] Booting worker with pid: 28602
[2018-10-25 20:21:12 +0800] [28603] [INFO] Booting worker with pid: 28603

           

在浏览器键入

http://localhost:8000

就可以访问。

其中-w表示启动的进程数,myapp是Python文件名,app是文件中的变量名或者函数名。可以看到,默认是绑定8000端口的,也就是说Python文件里那个main函数可以忽略了。

多进程高并发的问题解决了,开心!

然鹅,使用这个命令时,当我们退出终端时,这个应用就跪了,不能继续在后台运行,当然我们可以用

nohup gunicorn -w 4 myapp:app &这

个命令让它在后台运行,但是后续的状态监控呀什么的,可能就要用什么ps、kill这种原生态命令了,这真是太TM烦了。

实际环境:

启动命令:

gunicorn_conf_MEO.py -> gunicorn 配置文件

MEO : app -> MEO.py文件名 : flask 中的app = Flask(name)

gunicorn -c gunicorn_conf_MEO.py MEO:app
           

Supervisor

这时候Supervisor闪亮登场!

Supervisor是一个进程管理系统,它通过fork/exec的方式将这些被管理的进程当作它的子进程来启动,若该子进程异常中断,则父进程可以准确地获取子进程异常中断的信息。

supervisor是一个制作守护进程的工具,用户可以在UNIX系统中监控、管理进程。

常用于管理与某个用户或项目相关的进程。

去帮我们维护各种服务器的进程,即使有软件崩了也能帮我们自动重启。

用于在生产环境中,控制项目涉及的软件的进程。

如果不使用supervisor,重启gunicorn的步骤是:

指定进程和端口号:

-w

: 表示进程(

worker

)。

-b

:表示绑定ip地址和端口号(bind)。

$gunicorn -w 4 -b 127.0.0.1:5001 运行文件名称:Flask程序实例名
           
pstree -ap|grep gunicorn
kill -9(gunicorn pid)
gunicorn -w6 -b 127.0.0.1:80 demo:app
           

主要功能:

  1. 通过命令查看进程的状态,停止和启动进程
  2. 进程异常退出时,自动重启
  3. 进程启动失败自动重试
  4. 配置采用Linux哪个用户启动进程
  5. 设置进程启动优先级
  6. supervisor 启动时可自动拉起管理的各进程

安装

sudo apt install supervisor
           

使用

在项目文件夹下新建supervisor_app.conf文件:

[include]
files=/etc/supervisord.conf

[program:awesome_app]
directory=/home/gld/myproject
command=gunicorn -w 4 myapp:app
           

生成配置文件:echo_supervisord_conf > /etc/supervisord.conf(默认是没有配置文件的)

例如在配置文件/etc/supervisord.conf中添加:

[program:gunicorn]
command=gunicorn manage:app	;supervisor启动命令
directory=/home/hanquan/testPython/xxx	;项目的文件夹路径
startsecs=0	;启动时间
stopwaitsecs=0	;终止等待时间
autostart=true	;是否自动启动
autorestart=true	;是否自动重启
stdout_logfile=/home/hanquan/testPython/xxx/log/gunicorn.log	;log日志
stderr_logfile=/home/hanquan/testPython/xxx/log/gunicorn.err	;error日志

[program:nginx]
command=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf -g 'daemon off;' ;前台运行
directory=/home/hanquan/testPython/xxx	;项目的文件夹路径
startsecs=0	;启动时间
stopwaitsecs=0	;终止等待时间
autostart=true	;是否自动启动
autorestart=true	;是否自动重启
stdout_logfile=/home/hanquan/testPython/xxx/log/nginx.log	;log日志
stderr_logfile=/home/hanquan/testPython/xxx/log/nginx.err	;error日志

           

实际环境:

[program:meo_zwj]              z'w            ; 被管理的进程配置参数,后面是进程的名称
directory=/home/vnfm/zwj/meo/MEO/MEO   ; 脚本目录
command=gunicorn -c configure/gunicorn_conf_MEO.py MEO:app  ; 脚本执行命令
priority=999                        ; 进程启动优先级,默认999,值小的优先启动
autostart=true                      ; 在supervisord启动的时候也自动启动
startsecs=10                        ; 启动10秒后没有异常退出,就表示进程正常启动了,默认为1秒
startretries=3                      ; 启动失败自动重试次数,默认是3
autorestart=true                    ; 程序退出后自动重启,可选值:[unexpected,true,false]
user=vnfm                           ; 用哪个用户启动进程,默认是root
redirect_stderr=true                ; 把stderr重定向到stdout,默认false
stdout_logfile=/tmp/meo_zwj_stdout.out
stderr_logfile=/tmp/meo_zwj_stderr.out
           

启动supervisord:

sudo supervisord -c myproject/supervisor_app.conf
           

启动我们的应用:

$ sudo supervisorctl start awesome_app
           

常用管理命令:

$sudo supervisorctl start [program_name]
$sudo supervisorctl stop [program_name]
$sudo supervisorctl restart [program_name] # 重启服务,注意这里不会重新加载配置文件

           

更新项目文件后,重启supervisor命令:

supervisorctl restart all
           

重新加载配置文件,重新启动正在运行的服务:

$sudo supervisorctl reload
           

重新加载修改过的配置并重启该服务:

$sudo supervisorctl reread
$sudo supervisorctl update

           

supervisor 相关操作:

systemctl start supervisord.service     //启动 supervisor 并加载默认配置文件
systemctl stop supervisord.service     //停止 supervisor
           

管理各服务进程:

supervisorctl status                  //查看所有进程的状态
supervisorctl stop meo_vimproxy       //停止 meo_vimproxy 进程
supervisorctl start meo_vimproxy      //启动 meo_vimproxy 进程
supervisorctl restart meo_vimproxy   //重启 meo_vimproxy 进程
supervisorctl restart all            //重启所有进程
supervisorctl update                 //配置文件修改后使用该命令加载新的配置
supervisorctl reload                //重新启动配置中的所有程序

------------------------------------------------------------------------------------------
supervisor的服务器端部分启动命令:
sudo unlink /var/run/supervisor.sock(执行第二行产生文件后执行)
supervisord -c /etc/supervisord.conf
此时默认开启了所有服务

supervisor的客户端部分命令:
supervisorctl status 			查看进程运行状态
supervisorctl start 进程名 		启动进程
supervisorctl stop 进程名 		关闭进程
supervisorctl restart 进程名 	重启进程
supervisorctl update 			重新载入配置文件
supervisorctl shutdown 			关闭supervisord
supervisorctl clear 进程名 		清空进程日志
supervisorctl 					进入到交互模式下。使用help查看所有命令。

例如:	supervisorctl start all		启动所有进程
		supervisorctl restart all	重启所有进程
		supervisorctl stop all		停止所有进程

           

进阶

Gunicorn

常用参数:

-w:进程数,如-w 4

-b:绑定地址和端口,如-b 0.0.0.0:5000

Supervisor

Supervisor只能监控前台程序, 如果你的程序是通过fork方式实现的daemon服务,则不能用它监控,否则会提示:BACKOFF Exited too quickly (process log may have details)。 因此像Apache、Tomcat服务默认启动都是按daemon方式启动的,则不能通过Supervisor直接运行启动脚本(service httpd start),相反要通过一个包装过的启停脚本来完成。(参考博客)

日志

Logging, Flask, and Gunicorn … the Manageable Way

常见问题

CentOS 7.4默认的日志打印在/var/log/supervisor/supervisord.log文件下。

Permission权限问题

有时候提示往/tmp文件夹写入文件提示PermissionErr,检查supervisor配置文件,可以设置为user=root。

exit status 127; not expected

127表示命令没找到,首先可能是directory写错,用cd命令看一下,然后可能是command命令写错,复制命令在控制台试试是否gunicorn无法使用。

supervisor: couldn’t exec celery: EACCES

CentOS

CentOS Linux release 7.6.1810
           

安装Python3:

yum install python36u
           

安装pip3:

yum install python36u-pip
           

安装gunicorn:

pip3 install gunicorn
           

安装supervisor:

yum install supervisor
           

头条项目关于supervisor 另一种配置方法:

1. 配置

运行

echo_supervisord_conf

命令输出默认的配置项,可以如下操作将默认配置保存到文件中

echo_supervisord_conf > supervisord.conf
           

vim 打开编辑supervisord.conf文件,修改

[include]
files = relative/directory/*.ini
           

[include]
files = /etc/supervisor/*.conf
           

include

选项指明包含的其他配置文件。

将编辑后的supervisord.conf文件复制到/etc/目录下

然后我们在/etc目录下新建子目录supervisor(与配置文件里的选项相同),并在/etc/supervisor/中新建tuotiao管理的配置文件

toutiao.conf

[group:toutiao]
programs=toutiao-app

[program:toutiao-app]
command=/home/python/scripts/toutiao_app.sh
directory=/home/python/toutiao-backend
user=python
autorestart=true
redirect_stderr=false
loglevel=info
stopsignal=KILL
stopasgroup=true
killasgroup=true

[program:im]
command=/home/python/scripts/im.sh
directory=/home/python/toutiao-backend
user=python
autorestart=true
redirect_stderr=false
loglevel=info
stopsignal=KILL
stopasgroup=true
killasgroup=true
           

启动

supervisord -c /etc/supervisord.conf
           

查看 supervisord 是否在运行:

ps aux | grep supervisord
           
supervisorctl

我们可以利用

supervisorctl

来管理supervisor。

supervisorctl     // 执行supervisorctl,进去命令行编辑器
> status    # 查看程序状态
> start apscheduler  # 启动 apscheduler 单一程序
> stop toutiao:*   # 关闭 toutiao组 程序
> start toutiao:*  # 启动 toutiao组 程序
> restart toutiao:*    # 重启 toutiao组 程序
> update    # 重启配置文件修改过的程序
           

执行status命令时,显示如下信息说明程序运行正常:

supervisor> status
toutiao:toutiao-app RUNNING pid 32091, uptime 00:00:02