Flask + Gunicorn + Nginx 部署
文章目录
Flask,webpy,Django都带着 WSGI server,当然性能都不好,自带的web server 更多的是测试用途。线上发布时,则使用高性能的 wsgi server或者是联合nginx做uwsgi 。
greenlet是一个轻量级的协程库。gevent是基于greenlet的网络库。 guincorn是支持wsgi协议的http server,gevent只是它支持的模式之一 ,是为了解决django、flask这些web框架自带wsgi server性能低下的问题。它的特点是与各个web框架结合紧密,部署特别方便。
一、WSGI 协议简介
Web框架致力于如何生成HTML代码,而Web服务器用于处理和响应HTTP请求。Web框架和Web服务器之间的通信,需要一套双方都遵守的接口协议。WSGI协议就是用来统一这两者的接口的。
二、Gunicorn
即 Green Unicorn
常用的WSGI容器有Gunicorn和uWSGI,但Gunicorn直接用命令启动,不需要编写配置文件,相对uWSGI要容易很多,所以这里我也选择用Gunicorn作为容器。
安装
pip install gunicorn
启动
gunicorn [options] module_name:variable_name
module_name对应python文件,variable_name对应web应用实例。
以最简单的flask应用为例:
#main.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'hello world'
if __name__ == '__main__':
app.run()
启动代码
gunicorn main:app
其中main
就是指python程序代码main.py,app
就是那个wsgi func的名字。这样运行的话, gunicorn 默认作为一个监听 127.0.0.1:8000 的web server,可以在本机通过: http://127.0.0.1:8000 访问。
设置监听端口
如果要通过网络访问,则需要绑定不同的地址(也可以同时设置监听端口)。
gunicorn -b 127.0.0.1:8080
使用多进程
在多核服务器上,为了支持更多的并发访问并充分利用资源,可以使用更多的 gunicorn 进程。
gunicorn -w 8 main:app
这样就可以启动8个进程同时处理HTTP请求,提高系统的使用效率及性能。
配合gevent
另外, gunicorn 默认使用同步阻塞的网络模型(-k sync),对于大并发的访问可能表现不够好, 它还支持其它更好的模式,比如:gevent或meinheld。
gunicorn -k gevent main:app
指定配置文件
以上设置还可以通过 -c 参数传入一个配置文件实现。配置文件如下:
import os
import gevent.monkey
gevent.monkey.patch_all()
import multiprocessing
# 需要log目录存在。如果不存在,启动会报错
# debug = True # 生产环境不需要这个配置项。开启debug项后,在启动gunicorn的时候可以看到所有可配置项的配置
loglevel = 'debug' # 用于控制errorlog的信息级别,可以设置为debug、info、warning、error、critical
bind = "0.0.0.0:5000" # 监听IP放宽,以便于Docker之间、Docker和宿主机之间的通信
pidfile = "log/gunicorn.pid"
accesslog = "log/access.log" # 访问日志,可以通过access_log_format设置访问日志格式
errorlog = "log/debug.log"
daemon = True # 以守护进程形式来运行Gunicorn进程(其实就是将这个服务放到后台去运行)
#启动的进程数
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'gunicorn.workers.ggevent.GeventWorker'
# workers = 5 # 定义同时开启的处理请求的进程数量,根据网站流量适当调整
# worker_class = "gevent" # 采用gevent库,支持异步处理请求,提高吞吐量
x_forwarded_for_header = 'X-FORWARDED-FOR'
指定配置文件的启动命令如下:
gunicorn main:app -c gunicorn.conf.py
单纯的flask 自带的web服务器做下测试,会看到压力大的时候出现socket的问题,因为他是单进程单线程的。使用gunicorn来启动,响应速度和能力提升显著。
配置中workers指定启动的进程数。cpu的损耗是平均到各个进程。workers的值一定不要过大,毕竟多进程对于系统的调度消耗比较大。
关于gevent
gevent是一个基于libev的并发库。它为各种并发和网络相关的任务提供了整洁的API。gunicorn对于“协程”也就是Gevent的支持非常好。
gevent程序员指南:gevnet指南
gevent.monkey介绍详见:关于gevent monkey
三、部署注意事项
开发flask应用时,常用flask-script添加一些命令扩展。而部署应用时,就不需要再从flask-script的Manager实例中启动应用了。
在项目中添加wsgi.py文件:
在项目中添加wsgi.py文件:
# -*- coding: utf-8 -*-
import os
from . import create_app
import datetime
# Create an application instance that web servers can use. We store it as
# "application" (the wsgi default) and also the much shorter and convenient
# "app".
application = app = create_app('default')
@app.context_processor
def template_extras():
"""
上下文处理装饰器,返回的字典的键可以在上下文中使用
:return:
"""
return {'enumerate': enumerate, 'len': len, 'datetime': datetime}
在wsgi文件中创建flask实例给gunicorn使用。 创建实例后,注册上下文装饰器。 再通过gunicorn启动flask应用:
gunicorn -b 127.0.0.1:8000 -k gevent -w 1 app.wsgi
四、Nginx
Gunicorn对静态文件的支持不太好,所以生产环境下常用Nginx作为反向代理服务器。
安装
sudo apt-get install nginx
或者 yum install nginx
启动
sudo /etc/init.d/nginx start
或者sudo systemctl start nginx
如果想在系统启动时启用Nginx。请输入以下命令:
sudo systemctl enable nginx
修改配置文件
先将配置文件备份:
cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak
然后修改配置文件:
server {
listen 80;
server_name _; # 外部地址
location / {
proxy_pass http://127.0.0.1:8000;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
这里将Nginx设置为代理模式,代理到本地的8000端口,之后就可以通过公网访问flask应用了。
总结
最后,总结下这几个部分的关系:
web platform <—> WSGI <—> Nginx <—> web_client
以一段对话结束本篇博客
(nginx收到客户端发来的请求,根据nginx中配置的路由,将其转发给WSGI)
nginx:”WSGI,找你的来了!”
(WSGI服务器根据WSGI协议解析请求,配置好环境变量,调用start_response方法呼叫flask框架)
WSGI服务器:”flask,快来接客,客户资料我都给你准备好了!”
(flask根据env环境变量,请求参数和路径找到对应处理函数,生成html)
flask:”!@#$%^……WSGI,html文档弄好了,拿去吧。”
(WSGI拿到html,再组装根据env变量组装成一个http响应,发送给nginx)
WSGI服务器:”nginx,刚才谁找我来着?回他个话,!@#$%^…..”
(nginx再将响应发送给客户端)
文章作者 honour
上次更新 2018-06-27