Fork me on GitHub

使用Nginx+uWSGI+Flask部署应用

原理

WIGS(Web Server Gateway Interface)服务器网关接口

是Python应用程序或框架和Web服务器之间的一种接口,已经被广泛接受, 它已基本达成它的可移植性方面的目标。 WSGI 没有官方的实现,因为WSGI更像一个协议。只要遵照这些协议,WSGI应用(Application)都可以在任何服务器(Server)上运行,反之亦然。 WSGI标准在 PEP 333 中定义并被许多框架实现,其中包括现广泛使用的django框架。

所以说WIGS是一种标准

uWIGS

可以看作是一个应用程序,帮助我们实现WSGI协议,Http协议,这样我们可以不再关注网络通信的底层实现,将精力更多放在处理HTTP请求数据,返回HTML。利用uWIGS可以使我们的web应用得到更强的并发能力,uWIGS也可以返回静态文件(css,js,img…),但是很笨拙,一般静态文件都交由Nginx进行传输,所以配置中一般不配置static-map,如果直接由uWIGS接受HTTP请求则需要设置http:xxxx,如果只需要与反向代理服务器进行交互则只需要接受socket,uWIGS与Nginx交互相当于两个进程间交互,一般使用的是.sock文件或者指定端口接受socket。指定端口时再使用浏览器访问相应端口,uWIGS会提示skip,跳过该HTTP请求。

代理服务器(Proxy Server)

是一种特殊的网络服务,允许一个网络终端(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。一些网关、路由器等网络设备具备网络代理功能。一般认为代理服务有利于保障网络终端的隐私或安全,防止攻击。

反向代理服务器(Reverse Proxy)

反向代理,故名思意就是将代理服务器做出逆转,它主要的功能是代理服务器接收客户端发来的Http请求,转发到内部网络服务器,并返回Response给客户端。 反向代理的作用:

  1. 保证内网安全,可以使用反向代理服务器提供的WAF功能,阻止web攻击。
  2. 负载均衡,通过反向代理服务器来优化网站的负载
  3. 缓存静态资源,加快访问速度

Nginx(engine x)

Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。其特点是占有内存少,并发能力强。

它具有反向代理服务器的所有特点,并且配置简单,支持对大于1kb的文件进行压缩,静态文件缓存,可以大大提高访问效率。Nginx接受来自客户端的Http请求发送给uWSGI,uWSGI处理请求并将关键信息传递给web应用(django,flask等),应用返回Response经由uWSGI发送给Nginx,Nginx再发送给客户端。

实践

本文实践部分基于CentOS6和Python3

安装uWSGI

$ pip install uwsgi

如果无法安装有报错,请先尝试:

$ yum install python34-devel

运行服务时如果报错!!! no internal routing support, rebuild with pcre support !!!

# 先卸载
$ pip uninstall uwsgi
# 安装pcre支持库
$ yum install pcre pcre-devel pcre-static
# 重新安装uwsgi,不走pip缓存
$ pip install uwsgi -I --no-cache-dir

测试代码”server.py” :

def application(environ, start_response):
    status = '200 OK'
    output = b'Hello World!'   # python2可不用byte,python3必须要

    response_headers = [('Content-type', 'text/plain'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)

    return [output]

执行命令,--http参数指定了HTTP监听地址和端口,- -wsgi-file参数指定了WSGI应用程序入口,uWSGI会自动搜寻名为application的应用对象并调用它 :

$ uwsgi --http :9090 --wsgi-file server.py

打开浏览器,访问”http://localhost:9090″,你就可以看到”Hello World!”。

uWSGI可以支持多进程和多线程的方式启动应用,也可以监控应用的运行状态。我们将启动的命令改为:

$ uwsgi --http :9090 --wsgi-file server.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191

执行它后,uWSGI将启动4个应用进程,每个进程有2个线程,和一个master主进程(监控其他进程状态,如果有进程死了,则重启)。可以把这些都写进一个配置文件”myapp.ini”:

[uwsgi]
http=:9090
wsgi-file=server.py
master=true
processes=4
threads=2
stats=127.0.0.1:9191

然后就可以将启动命令简化为:

$ uwsgi myapp.ini

安装及配置Nginx

安装:

$ yum install nginx

查看配置文件:

$ ps -ef|grep nginx
root     16964     1  0 16:23 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf

我们要修改的是location选项,打开/etc/nginx/nginx.conf,没有找到location,但是有一行include /etc/nginx/conf.d/*.conf,转到“include /etc/nginx/conf.d/”下,打开“default.conf”,找到location,修改为:

location / {
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:3031;
}

这段配置表明Nginx会将收到的所有请求都转发到”127.0.0.1:3031″端口上 。

然后将uWSGI的HTTP端口监听改为socket端口监听,即将配置文件中的http项去掉,改为socket项:

[uwsgi]
socket=127.0.0.1:3031
wsgi-file=server.py
master=true
processes=4
threads=2
stats=127.0.0.1:9191

重启Nginx,并启动uWSGI服务器:

$ sudo service nginx restart
$ uwsgi myapp.ini

访问”http://localhost”(Nginx默认使用80端口,可以在“default.conf”修改),顺利的话我们会再次看到”Hello World!”。

不顺利的话,继续往下看。如果打开网站什么都看不到,不要急,从“nginx.conf”找到日志文件目录“/var/log/nginx/”,打开“error.log”,先找到报错信息“connect() to 127.0.0.1:3031 failed (13: Permission denied) while connecting to upstream”,百度解决问题:

location / {
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:3031;
}

重启Nginx,并启动uWSGI服务器,终于可以看到”Hello World!”。

运行Flask应用

其实很简单,只要将上例中"server.py"的内容改为Flask应用即可,当然你需要先把Flask包安装好。

from flask import Flask
application = Flask(__name__)

@application.route('/')
def index():
    return '<h1>Hello World</h1>'

很多人习惯将Flask应用对象取名为”app”,但是WSGI标准是”application”。uWSGI提供了一个功能,可以指定应用对象,方法就是在配置文件中加上”callable”项:

[uwsgi]
...
callable=app

现在,我们的Flask应用就可以使用”app”作为对象名了 :

location / {
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:3031;
}

使用Python虚拟环境

虚拟环境的安装见文末参考文章。采用虚拟环境来避免应用间冲突,uWSGI可以非常容易做到这点,就是在配置文件中加上”virtualenv”项:

[uwsgi]
...
virtualenv=/home/bjhee/virtualenv

一个Nginx中,可以同时运行多个应用,不一定是Python的应用。我们期望通过不同的路径来路由不同的应用,因此就不能像上例那样直接修改根目录的配置。假设我们希望通过”http://localhost/myapp”来访问我们的应用,首先要在Nginx的配置文件中,加入下面的内容:

location /myapp {
    include uwsgi_params;
    uwsgi_param SCRIPT_NAME /myapp;
    uwsgi_pass 127.0.0.1:3031;
}

这里我们定义了一个uWSGI参数SCRIPT_NAME,值为应用的路径/myapp。接下来,在uWSGI的启动配置中,去掉wsgi-file项,并加上:

[uwsgi]
...
mount=/myapp=server.py
manage-script-name=true

mount参数表示将/myapp地址路由到”server.py”中,manage-script-name参数表示启用之前在Nginx里配置的”SCRIPT_NAME”参数。再次重启Nginx和uWSGI,你就可以通过”http://localhost/myapp”来访问应用了。

端口优化

上面例子中,我们是通过”127.0.0.1:3031″Socket端口来连接Nginx和uWSGI的,其实我们也可以采用socket文件的方式,这样可以不用写死端口。在uWSGI的启动配置中,我们要修改”socket”项:

[uwsgi]
socket=/tmp/uwsgi.sock
...

启动uWSGI服务器后,它会自动创建一个”/tmp/uwsgi.sock”文件。然后让我们修改Nginx配置文件,将”uwsgi_pass”配置项改为文件:

location /myapp {
    include uwsgi_params;
    uwsgi_param SCRIPT_NAME /myapp;
    uwsgi_pass unix:/tmp/uwsgi.sock;
}

重启Nginx服务器即可。这里要注意Nginx的用户必须要有“uwsgi.sock”文件的读写的权限。

参考文章:

Comments