用uwsgi和nginx部署flask应用

为什么不直接用Werkzeug

Flask ,Django 自带的web server的目的就是用于开发,而不是生产环境。他们俩本身是web framework而不是web server. 他们自带的server应该都只能开单进程。而像gunicorn是 prefork模式,从nginx每发过来一个请求,它就fork一个进程去处理这个请求,并buffer相关的数据。wsgi服务器都是专门为生产环境开发的,能配置更多从而处理更复杂的请求状况,从性能和稳定性来说,都更好。

uWSGI

uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换。

WSGI是一种Web服务器网关接口。它是一个Web服务器(如nginx)与应用服务器(如uWSGI服务器)通信的一种规范。

uWSGI安装

uwsgi的官方文档说明:
http://uwsgi-docs.readthedocs.org/en/latest/WSGIquickstart.html

方式一
采用源码(source tarball)安装uwsgi。
可以去pypi,搜索uwsgi下载:
https://pypi.python.org/pypi/uWSGI/
安装命令如下:

1
2
3
tar xvzf uwsgi-2.0.9.tar.gz
cd uwsgi-2.0.9
make

方式二
使用pip安装uwsgi

1
pip install uwsgi

可能会报错

1
2
fatal error: Python.h: No such file or directory
sudo apt-get install python-dev

uWSGI配置

首先要明确的是,如果你喜欢用命令行的方式(如shell)敲命令,那可以省去任何配置。
但是,绝大多数人,还是不愿意记那么长的命令,反复敲的。所以uwsgi里,就给大家提供了多种配置,省去你启动时候,需要敲一长串命令的过程。
uwsgi 有多种配置可用:

  1. ini
  2. xml
  3. json
  4. yaml

从uwsgi的官方文档来看,貌似(我个人的理解)推荐用ini方式,所以下面的配置也都是基于ini的。

ini 格式说明

  1. ini配置为 key=value 形式
  2. 在ini配置文件里,#号为注释,
  3. 布尔值为 true 和 false
  4. 在命令行里,uwsgi myconf.ini 等价于 uwsgi –ini myconf.ini

配置示例

1
2
3
[uwsgi]
socket = 127.0.0.1:8000
workers = 4

常用选项

socket : 地址和端口号,例如:socket = 127.0.0.1:50000
processes : 开启的进程数量
workers : 开启的进程数量,等同于processes(官网的说法是spawn the specified number ofworkers / processes)
chdir : 指定运行目录(chdir to specified directory before apps loading)
wsgi-file : 载入wsgi-file(load .wsgi file)
stats : 在指定的地址上,开启状态服务(enable the stats server on the specified address)
threads : 运行线程。由于GIL的存在,我觉得这个真心没啥用。(run each worker in prethreaded mode with the specified number of threads)
master : 允许主进程存在(enable master process)
daemonize : 使进程在后台运行,并将日志打到指定的日志文件或者udp服务器(daemonize uWSGI)。实际上最常用的,还是把运行记录输出到一个本地文件上。
pidfile : 指定pid文件的位置,记录主进程的pid号。
vacuum : 当服务器退出的时候自动清理环境,删除unix socket文件和pid文件(try to remove all of the generated file/sockets)

其他选项说明

其他选项,具体可以通过 –help 选项来查看:

1
uwsgi --help

部署指南

shell命令方式

1
2
3
4
5
6
7
8
9
10
11
12
uwsgi --chdir=/path/to/your/project \
--module=mysite.wsgi:application \
--env DJANGO_SETTINGS_MODULE=mysite.settings \
--master --pidfile=/tmp/project-master.pid \
--socket=127.0.0.1:49152 \ # can also be a file
--processes=5 \ # number of worker processes
--uid=1000 --gid=2000 \ # if root, uwsgi can drop privileges
--harakiri=20 \ # respawn processes taking more than 20 seconds
--max-requests=5000 \ # respawn processes after serving 5000 requests
--vacuum \ # clear environment on exit
--home=/path/to/virtual/env \ # optional path to a virtualenv
--daemonize=/var/log/uwsgi/yourproject.log # background the process

如果使用配置文件

1
2
3
4
5
6
7
8
[uwsgi]
chdir=/path/to/your/project
module=mysite.wsgi:application
master=True
pidfile=/tmp/project-master.pid
vacuum=True
max-requests=5000
daemonize=/var/log/uwsgi/yourproject.log

使用方法

1
uwsgi --ini uwsgi.ini

nginx

nginx 是一个 http 服务器,与 apache、lighttpd、Microsoft IIS 等属于同类产品;

nginx安装

nginx的安装非常方便。

1
sudo apt-get install nigix

一个flask项目部署示例

创建Flask项目

这里就用最简单的HelloWorld,创建一个工程目录:myproject,里面包含以下文件:

文件名:myapp.py

代码:

1
2
3
4
5
6
7
8
9
10
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello World Flask'
if __name__ == '__main__':
app.run()

给项目目录配置权限,nginx和uwsgi默认是以www-data用户和组来运行:

1
2
chown -R www-data:www-data /var/virenvs/myenv/myproject
chmod -R 775 /var/virenvs/myenv/myproject

检查python与uWSGI通讯

建立一个测试文件,以测试uwsgi是否正常运行。

1
2
3
4
5
# test.py
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"] # python3
#return ["Hello World"] # python2

运行uWSGI

1
uwsgi --http :8000 --wsgi-file test.py

访问127.0.0.1:8000,如果显示Hello World说明以下调用栈工作正常。

the web client <-> uWSGI <-> Python

检查flask与uWSGI通讯

运行uWSGI

1
uwsgi --http :8000 --wsgi-file myapp.py --callable app

访问127.0.0.1:8000,如果显示Hello World Flask说明以下调用栈工作正常。

the web client <-> uWSGI <-> Flask

配置文件方式myapp_config.ini

1
2
3
4
5
6
7
8
9
[uwsgi]
socket = 127.0.0.1:3031
wsgi-file = myapp.py
callable = app
master = true
processes = 4
http = :80
vacuum = true
die-on-term = true

开机自启动uwsgi

编辑文件/etc/rc.local, 添加下面内容到这行代码之前exit 0:

1
/usr/local/bin/uwsgi --socket /path/to/mysite.sock --module /path/to/mysite.wsgi --chmod-socket=666

检查nginx是否正常工作

打开nginx服务

1
sudo /etc/init.d/nginx start # start nginx

此时在浏览器中打开127.0.0.1,如果看到“Welcome to nginx!”,说明nginx工作正常。

the web client <-> the web server

在nginx配置

在项目目录中添加如下配置。
myapp_nginx.conf 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# mysite_nginx.conf
# the upstream component nginx needs to connect to
upstream flask {
# server unix:///path/to/your/mysite/mysite.sock; # for a file socket
server 127.0.0.1:3031; # for a web port socket (we'll use this first)
}
# configuration of the server
server {
# the port your site will be served on
listen 80;
# the domain name it will serve for
# server_name .example.com; # substitute your machine's IP address or FQDN
charset utf-8;
# max upload size
client_max_body_size 75M; # adjust to taste
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_pass flask;
include /home/foobar/dev/flask_deploy/uwsgi_params.txt; # the uwsgi_params file you installed
#include uwsgi_params;
}
}

并将其添加到nginx配置文件的文件夹中(用软连接)

1
sudo ln -s ~/path/to/your/mysite/mysite_nginx.conf /etc/nginx/sites-enabled/

修改配置文件后记得重启nginx,顺带删除sites-enabled文件夹中其他配置

1
sudo /etc/init.d/nginx restart

打开服务器

启动uwsgi

1
uwsgi myapp_config.ini

如果没有加载工程成功可以从打出的信息中看出,然后不要关闭该Terminal,新打开一个Terminal,在里面启动nginx

1
sudo /etc/init.d/nginx start

参考资料

[1] Django项目部署文档
[2] Flask项目部署
[3] uWSGI介绍
[4] Django项目部署中文翻译
[5] 使用flask开发RESTful架构的api服务器端(5)–部署flask应用到nginx
[6] 我如何组织 Flask 应用结构
[7] python web 部署:nginx + gunicorn + supervisor + flask 部署笔记