flask + celery + gunicorn + gevent + nginx + supervisord 实现业务系统

    接触了flask开发有一小段时间了,使用flask主要完成了我们产品的Android客户端的后台服务(提供REST API),还有就是为运营提供数据统计及应用发布的web系统。之前都是通过传统的笔记本来记录开发中遇到的问题,渐渐发现很多问题存在着很多的共性,通过博客的方式开启一个记录和交流的新方法吧。文中如果有什么错误欢迎指出,对flask开发有兴趣的朋友也可以互相交流,探讨问题。

flask在他的官方文档中是这样描述的,flask是一个python的微框架,基于Werkzeuk和Jinjia2。目前对于python2和python3的支持都很好。入门也很简单,强烈推荐官方文档,例子简单清楚。官方文档.另外一个参考的就是《FlaskWeb开发:基于Python的Web应用开发实战》在网上可以找到很多电子版的,通过官方的文档可以了解flask是什么以及如何开始一个hello word,而通过上面提到第二个参考资料,可以实现一个简单的博客系统,更好的了解flask的。在这里就不多介绍如何开始flask开发的,我感觉官方的文档已经介绍的足够简单清楚,这些内容也不是本文的主要内容,本文的焦点主要放在如何实现flask的部署,以及分享我在部署过程中遇到的问题,以及经过这些折腾之后我的建议吧。

系统: centos 6.5 x86_64

python: 版本3.5.2

flask:用来实现REST API及相关的web功能, 版本0.11

celery : 一个很好的分布式管理队列,在我的应用场景中主要使用它来做一些定时的任务,其实主要是定时向我们的运维人员发送一些日志及告警信息。

官方文档.版本3.1.24

gunicorn :  作为flask应用的web server,自带的web server是用来我们开发和测试过程中进行调试的,是一个单进程单线程的,无法做生产部署只用,

之前尝试过uwsgi作为web server,但是从部署角度来说,gunicorn配置更加方便,另外,gunicorn 对于gevent的支持也是非常不错的,

所以综合以上两个原因,才用的gunicorn作为web server部署。官方配置文档, 版本19.6

gevent: 一个很好的基于协程的python网络库,可以很容易的提升系统的并发性,而且gunicorn对gevent的支持也很好,配置简单。官方文档, 版本1.2

nginx: 用作反向代理, 版本1.6.2

supervisord: 用以管理我的flask应用以及celery任务。gunicorn已经可以实现将自己作为一个守护进程,从一定程度上来说,即使不适用supervisord也没有任何

问题,但是celery在centos 6.5上配置自启动以及变成守护进程相对麻烦,另外从统一管理及维护简单的角度来考虑,采用supervisord也是一个不错

的选择。不得不说下,在使用supervisord之前,都是手动杀gunicorn的进程的。同样附上官方文档.版本3.3.1

我再啰嗦一下,为什么每个都附上了官方的文档,因为我在我的部署过程中遇到过不少的问题,搜索过到很多其他人分享的东西,发现很多别人分享的东西都有特定的版本限制,而通常过程中,作者并没有写清楚他当时使用的版本,而对于一个开始接触这种部署方式的人来说,调错是一件耗时耗力的事情,虽然过程中我们可以通过解决问题提升自己,但从我个人的感受来看,如果第一次配置部署中出现了难以解决的问题,很可能会迫使你转用其他的方式,所以官方文档给的配置说明是最清晰的。

一、一定使用Python虚拟环境(即virtualenv),为什么这么说呢,从部署的角度来说,方便,易维护,如果服务器上有多个版本的python或者当你使用Python3 来开发项目,然后使用跟我一样的部署方式的时候你就会跟我有一样的体会。

 1、安装virtualenv:  yum install virtualenv 

  2、进入到你的工程目录,个人建议把虚拟环境放到你的工程目录当中,保证每一个工程有自己独立的python环境,

在shell中执行: virtualenv -p python3 venv 来创建虚拟环境,其中 -p 参数制定python的路径,执行完之后可以发现在工程目录中产生一个

名为venv的文件夹,以后该虚拟环境所有的扩展包的安装目录。

 3、安装Python虚拟环境的所有依赖包:

在开发过程中可以通过执行指令  pip freeze > requirement.txt 来导出依赖包及其对应的版本。

在工程目录中,执行:  source venv/bin/activate 进入虚拟环境,这时,在你的shell中应该是如下类似的显示: (venv) localhost:service allan$ 

接着通过执行  pip install -r requirement.txt 来安装所有的扩展包,至此,虚拟环境配置完成。如果需要退出虚拟环境,

可以在shell中执行  deactivate 

在工程目录中使用flask自带的web server 运行flask应用,应用功能一切正常。

二、nginx相关配置:

location / {
                 proxy_pass http://127.0.0.1:9000;
                 proxy_set_header Host $host;
                 proxy_set_header X-Real-IP $remote_addr;
                 proxy_set_header REMOTE-HOST $remote_addr;
                 proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
                 proxy_redirect off;
         }

三、gunicorn已经在第一步虚拟环境的配置的部分的扩展包安装中完成,其简单配置如下:

1 import gevent.monkey
 2 import multiprocessing
 3 
 4 gevent.monkey.patch_all()
 5 
 6 bind = '0.0.0.0:9000'
 7 # restart workers when code change, only use in development
 8 #reload = True
10 preload_app = True
12 # debug when development and error when production
13 loglevel = 'error'
14 logfile = 'log/debug.log'
15 accesslog = 'log/access.log'
16 access_log_format = '%(h)s %(t)s %(U)s %(q)s'
17 errorlog = 'log/error.log'
18 # process name
19 proc_name = 'vservice' 
21 pidfile = 'log/gunicorn.pid'
22 # set process daemon, not use in default
23 #daemon = True
25 # number of processes
26 workers = multiprocessing.cpu_count() * 2 + 1
27 # number of threads of per process
28 threads = multiprocessing.cpu_count() * 2
29 worker_class = 'gevent'

其中23行的daemon参数的设置是要特殊注意的,如果只是使用gunicorn而不使用supervisord类似的进程管理工具,该参数设置为true,使进程成为守护进程,但一旦使用类似supervisord等进程管理工具时,该参数一定不要设置,gunicorn默认该参数为False,否则会出现问题。

当然,gunicorn还有很多的参数配置,具体可参考前面部分提供的官方配置文档部分。

如果单独使用gunicorn运行,通过指令  gunicorn -c config.py manager:app 来运行,其中manager对应工程目录中的manager.py, app为flask应用的名称。

至此,如果在flask应用中不使用celery也不使用supervisord做进程管理,在一定程度上已经实现了服务器端的部署。到了这里仍然缺少一个很重要的环节,那就是监控,我是通过在跟服务器不同网络的一台测试机器上部署了一个模仿正常客户端的Python脚本来实现对服务器是否正常工作的检测,如果连续丢失几个包会认为服务器程序异常会向运维人员发送报警的短信及邮件。

四、supervisord配置及安装:

在虚拟环境的shell中执行  pip install supervisor 安装supervisord, 我使用的Python版本为3.5.2, pip安装之后supervisord的版本为3.3.1.

     安装完成之后,利用supervisord自带的命令产生配置文件模板,强烈建议这么做,因为从网上其他人的分享中找过来的模板很可能版本不匹配,导致supervisord一直不能正常运行,而且supervisord报的错误也会让你一开始没有头绪。

    假定在工程目录中放置你的supervisord配置文件: echo_supervisord_conf /data/vservice/supervisord.conf ,该指令会产生一个你安装的版本的supervisord对应的模板配置文件。

下面是我在我的工程实践中的supervisord的配置文件,在模板的基础上,参考官方文档各个参数的意义进行的修改:

1 [program:vservice]
 2 command=/data/vservicer/venv/bin/gunicorn -c /data/vservice-server/config.py manager:app 
 3 process_name=%(program_name)s
 4 numprocs=1      
 5 directory=/data/vservice/  
 6 autostart=true
 7 autorestart=unexpected      
 8 user=allan                  
 9 stdout_logfile=/data/vservice/log/supervisor/stdout.log
10 stderr_logfile=/data/vservice/log/supervisor/stderr.log
11 
12 [program:send_mail]
13 command=/data/vservice/venv/bin/celery worker --app=manager.celery --beat -l INFO
14 process_name=%(program_name)s
15 numprocs=1   
16 directory=/data/vservice/
17 autostart=true 
18 autorestart=unexpected 
19 stdout_logfile=/data/vservice/log/celery/stdout.log
20 stderr_logfile=/data/vservice/log/celery/stderr.log

首先我们要测试我们的配置是否正确,由于supervisord默认是开启deamon的,所以在测试supervisord是否正确工作的情况下我们先加一个参数,让supervisord现在前台工作:

 supervisord -c /data/vserver/supervisord.conf -n 

这个时候如果你的supervisord如果已经正常工作且没有错误的话,应该会显示vservice以及send_mail两个程序进入running状态,下面是我的输出,仅供参考:

1 2016-11-04 16:18:31,125 INFO RPC interface 'supervisor' initialized
2 2016-11-04 16:18:31,125 CRIT Server 'unix_http_server' running without any HTTP authentication checking
3 2016-11-04 16:18:31,125 INFO supervisord started with pid 1812
4 2016-11-04 16:18:32,128 INFO spawned: 'send_mail' with pid 1817
5 2016-11-04 16:18:32,129 INFO spawned: 'vservice' with pid 1818
6 2016-11-04 16:18:33,130 INFO success: send_mail entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
7 2016-11-04 16:18:33,131 INFO success: vservice entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

这个时候你可以通过supervisorctl 指令查看状态,启动,停止或者重启对应的程序

查看状态: supervisorctl -c /data/vservice/supervisord.conf status 

执行以上指令之后之后显示的结果:

send_mail                   RUNNING   pid 1911, uptime 0:00:11
vservice                    RUNNING   pid 1912, uptime 0:00:11
停止某个应用: supervisorctl -c /data/vservice/supervisord.conf stop send_mail(或者all或者vservice) 
执行以上指令之后之后显示的结果:
[allan@TEST vservice]# supervisorctl -c /data/vservice/supervisord.conf stop send_mail
send_mail: stopped
[allan@TEST vservice]#  supervisorctl -c /data/vservice/supervisord.conf  status
send_mail                   STOPPED   Nov 04 04:28 PM
vservice                    RUNNING   pid 1912, uptime 0:06:16
启动某个应用:  supervisorctl -c /data/vservice/supervisord.conf start send_mail(或者all或者vservice) 
重启某个应用:  supervisorctl -c /data/vservice/supervisord.conf restart send_mail(或者all或者vservice) 一切测试正常之后,现在可以去掉supervisord的 -n参数,这时候supervisord会变成一个守护进程进入后台,至此在外部测试你的flask应用是否正常工作即可,部署在这时候已经基本完成。在使用supervisord的过程中我遇到过不少的问题。1、最开始在网上看到有人说supervisord对Python3的支持目前只是demo阶段,还不稳定,当时心凉了一截,后来仔细查各种文档及别人分享的经验,发现supervisord 本身运行需要的Python环境是2.x的版本,因此在这个时候Python虚拟环境的重要性再次体现出来:使用2.x的版本环境运行supervisord,但是supervisord本身作为一个进程管理软件,它启动Python3开发的应用用是完全没有问题的。2、如果首次使用supervisord 启动程序出现错误,比如exit with code 1, not expected等时,你再次运行supervisord时会报地址端口被占用的错误(在supervisord的错误日志目录中),这时候你一定记得通过linux的进程管理查看下你的flask 应用是不是已经启动了,因为很可能出现的情况是,supervisord已经把你的程序启动了,但是出现错误是在其他的步骤中,因此你再次启动时,会出现以上的错误情况。3、supervisord的用户配置问题,如root账户的问题。以上是我的部署过程以及部署过程中曾经遇到过的问题,当然过程中也还有其他的一些小问题,并没有一一的在这列出,如果发现有什么错误或者有相关的问题要进行交流讨论,可以通过邮件进行交流:[email protected]

相关推荐