1,目的
在生产环境下,可以通过Nginx+gunicorn+Flask部署Web服务,从而达到高并发高稳定性的要求。
如果要部署多个APP,可以采用单个Nginx,多个gunicorn+Flask的方式来实现,如下图所示。
此外,还可以参考本人的上篇文章,通过nginx+uwsgi+flask搭建web服务
- Flask应用示例2 - 通过nginx+uwsgi+flask搭建web服务
http://www.jianshu.com/p/42b91d26baa4
通过两个模式的搭建,个人感觉gunicorn与nginx只是通过端口做反向代理,不像uwsgi通过pid进行通信,效率可能受到影响,没有uwsgi高(并没有大数据量的测试)。
但是,gunicorn用着更顺手,本身就是通过python开发的,可以通过配置文件执行钩子代码(支持python),例如请求前后的处理等等。
2,安装过程
2.1,升级软件包
sudo apt-get update
2.2,安装virtualenv和python环境
sudo apt-get install build-essential python-dev python-pip
sudo pip install virtualenv
2.3,在virtualenv中部署flask app,并测试
- 创建存放网站的目录
mkdir mysite
- 配置virtualenv和安装flask
进入mysite目录,然后创建虚拟环境.my_env,激活虚拟环境,然后安装flask
cd mysite
virtualenv .my_env # 创建Python虚拟环境
source .my_env/bin/activate # 进入Python虚拟环境,退出命令是deactivate
pip install flask # 在虚拟环境下安装flask
- 保存虚拟环境的Python包到requirements.txt
# 通过pip freeze保存Python第三方包到requirement文件里面,既能知道自己安装了什么库,也方便别人部署时,安装相应的库。
pip freeze > requirements.txt
- 在mysite目录下创建hello.py
from flask import Flask
app = Flask(__name__)
@app.route("/app1/")
def hello():
return "Hello World!"
@app.route("/app1/flask/")
def hello_flask():
return "Hello World! Hello Flask!"
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8080)
需要注意的是,app.run()只是开发时测试使用,故需要放置在if __name__ == "__main__"
下,这样gunicorn才不会执行app.run()方法。而host需要设置为0.0.0.0,表示让flask监听机器的所有ip地址的8080端口。
- 启动测试
执行以下命令,可以启动Flask。通过浏览器访问192.168.1.32:8080/app1/,如果返回“Hello World!”,则证明启动OK。
python hello.py
2.4,在virtualenv中部署gunicorn,并测试
- 进入到Python虚拟环境,并安装gunicorn
source .env/bin/activate # 进入Python虚拟环境,退出命令是deactivate
pip install gunicorn # 在虚拟环境中安装gunicorn
- 在虚拟环境下,直接启动gunicorn
# -w 表示开启多少个worker,根据系统核心数确定,-b表示gunicorn开放的访问地址
(.my_env) kevin@black:~/test$ gunicorn -w 8 -b 0.0.0.0:30000 hello:app
[2017-11-23 19:50:37 +0000] [3725] [INFO] Starting gunicorn 19.7.1
[2017-11-23 19:50:37 +0000] [3725] [INFO] Listening at: http://0.0.0.0:30000 (3725)
[2017-11-23 19:50:37 +0000] [3725] [INFO] Using worker: sync
[2017-11-23 19:50:37 +0000] [3731] [INFO] Booting worker with pid: 3731
[2017-11-23 19:50:37 +0000] [3732] [INFO] Booting worker with pid: 3732
[2017-11-23 19:50:37 +0000] [3737] [INFO] Booting worker with pid: 3737
[2017-11-23 19:50:37 +0000] [3740] [INFO] Booting worker with pid: 3740
[2017-11-23 19:50:37 +0000] [3743] [INFO] Booting worker with pid: 3743
[2017-11-23 19:50:37 +0000] [3744] [INFO] Booting worker with pid: 3744
[2017-11-23 19:50:38 +0000] [3749] [INFO] Booting worker with pid: 3749
[2017-11-23 19:50:38 +0000] [3750] [INFO] Booting worker with pid: 3750
# -k gevent,worker_class进程的工作方式,默认是sync,gevent需要提前安装,可选值eventlet、gevent、tornado、gthread、giohttp
# 查看进程,可以看到用到了虚拟环境的包,否则用的是全局的包
kevin@black:~$ ps -ef | grep gunicorn
kevin 3725 21793 1 19:50 pts/11 00:00:00 /home/kevin/test/.my_env/bin/python /home/kevin/test/.my_env/bin/gunicorn -w 8 -b 0.0.0.0:30000 hello:app
kevin 3731 3725 0 19:50 pts/11 00:00:00 /home/kevin/test/.my_env/bin/python /home/kevin/test/.my_env/bin/gunicorn -w 8 -b 0.0.0.0:30000 hello:app
kevin 3732 3725 0 19:50 pts/11 00:00:00 /home/kevin/test/.my_env/bin/python /home/kevin/test/.my_env/bin/gunicorn -w 8 -b 0.0.0.0:30000 hello:app
kevin 3737 3725 0 19:50 pts/11 00:00:00 /home/kevin/test/.my_env/bin/python /home/kevin/test/.my_env/bin/gunicorn -w 8 -b 0.0.0.0:30000 hello:app
kevin 3740 3725 0 19:50 pts/11 00:00:00 /home/kevin/test/.my_env/bin/python /home/kevin/test/.my_env/bin/gunicorn -w 8 -b 0.0.0.0:30000 hello:app
kevin 3743 3725 0 19:50 pts/11 00:00:00 /home/kevin/test/.my_env/bin/python /home/kevin/test/.my_env/bin/gunicorn -w 8 -b 0.0.0.0:30000 hello:app
kevin 3744 3725 0 19:50 pts/11 00:00:00 /home/kevin/test/.my_env/bin/python /home/kevin/test/.my_env/bin/gunicorn -w 8 -b 0.0.0.0:30000 hello:app
kevin 3749 3725 0 19:50 pts/11 00:00:00 /home/kevin/test/.my_env/bin/python /home/kevin/test/.my_env/bin/gunicorn -w 8 -b 0.0.0.0:30000 hello:app
kevin 3750 3725 0 19:50 pts/11 00:00:00 /home/kevin/test/.my_env/bin/python /home/kevin/test/.my_env/bin/gunicorn -w 8 -b 0.0.0.0:30000 hello:app
kevin 3839 937 0 19:50 pts/12 00:00:00 grep --color=auto gunicorn
# 关闭进程
pkill gunicorn
2.5,安装nginx,并配置测试
- 安装nginx(不在python虚拟环境下)
sudo apt-get install nginx
- 编辑配置文件:/etc/nginx/conf.d/gunicorn.conf
server {
listen 8000;
server_name example.org; # 这是HOST机器的外部域名,用地址也行
location / {
proxy_pass http://192.168.1.30:30000; # 这里是指向 gunicorn host 的服务地址
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
- nginx启动测试
kevin@orange:~/web/flask/mysite$ sudo service nginx start
kevin@orange:~/web/flask/mysite$ ps -ef | grep nginx
root 2324 1 0 16:19 ? 00:00:00 nginx: master process /usr/sbin/nginx
www-data 2325 2324 0 16:19 ? 00:00:00 nginx: worker process
www-data 2326 2324 0 16:19 ? 00:00:00 nginx: worker process
www-data 2327 2324 0 16:19 ? 00:00:00 nginx: worker process
www-data 2328 2324 0 16:19 ? 00:00:00 nginx: worker process
zhangsh+ 2330 2171 0 16:20 pts/1 00:00:00 grep --color=auto nginx
2.6,服务测试
- Http访问测试,一切OK
kevin@Blue:~$ curl http://192.168.1.32:81/app1/flask/
Hello World! Hello Flask!
kevin@Blue:~$ curl http://192.168.1.32:81/app1/
Hello World!
- 浏览器访问测试,一切OK
3,gunicorn高级用法
- 在虚拟环境中,安装gevent
pip install gevent
- gunicorn的配置如下(原文请参考https://www.cnblogs.com/nanrou/p/7026789.html)
#coding=utf-8
import os
bind = '127.0.0.1:8422' #绑定的ip已经端口号
workers = 8 #进程数
threads = 2 #指定每个进程开启的线程数,官方推荐设置为核心数的两至四倍
backlog = 2048 #允许挂起的连接数的最大值,官方推荐这个值设在64-2048
timeout = 30 #超时时间,单位秒
worker_class = "gevent" #工作方式,使用gevent模式,默认的是sync模式(并发只有1个),可选值eventlet、gevent、tornado、gthread、giohttp
worker_connections=1000 #进程链接数,默认值1000,同时链接客户端的阀值,这个设置只对进程工作方式为Eventlet和Gevent的产生影响
daemon=True #守护进程,默认值是False,守护进程形式来运行Gunicorn进程
pidfile='gunicorn.pid' #设置pid文件的文件名,如果不设置的话,不会创建pid文件,默认值是None
proc_name="myflask" #默认值default_proc_name,即gunicorn(需要额外安装setproctitle),但是测试没有生效,需要深入看看??
pythonpath='/home/kevin/test/.my_env/bin' # 将这些路径加到python path去
loglevel='info' #日志级别,这个日志级别指的是错误日志的级别,而访问日志的级别无法设置,可以是debug,info,warning,error,critical
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' #设置gunicorn访问日志格式,错误日志无法设置
accesslog = "/home/kevin/test/log/access.log" #访问日志文件的路径
errorlog = "/home/kevin/test/log/error.log" #错误日志文件的路径
# accesslog = "log/access.log" #访问日志文件的路径
# errorlog = "log/error.log" #错误日志文件的路径
# debug = False
# chdir = '/home/lijiajia/Ip_Asnproject/AsnProc' #gunicorn要切换到的目的工作目录,测试后,没搞懂具体含义???
# 可以通过以下包完成对gunicorn日志的按天分割,具体用的时候可以尝试
# import logging
# import logging.handlers
# from logging.handlers import WatchedFileHandler
4,参考资料
gunicorn配置文件
http://blog.csdn.net/zs_2014/article/details/41249347Gunicorn配置部分的翻译
https://www.cnblogs.com/nanrou/p/7026789.html