Nginx+uwsgi快速部署Python应用

jerry Python 2015年11月23日 收藏

从学过的编程语言来说,感觉PHP的部署是最Easy的。只要把环境搭建好,PHP文件丢到路径,访问即可,不用操心php-cgi运行如何,很是方便啊。

而Phthon的部署就麻烦了,以下是总结网上资料关于使用Nginx+uwsgi来部署Python应用。

Python的WEB服务器大部分可分为:Python写的(如Medusa),嵌入 Python的(如mod_python的),或者调用Python通过网关协议(例如的CGI,FastCGI的,等等)

Python常见部署方法有

  1. fcgi:用spawn-fcgi或者框架自带的工具对各个project分别生成监听进程,然后和http 服务互动
  2. wsgi:利用http服务的mod_wsgi模块来跑各个project(Web应用程序或框架简单而通用的Web服务器 之间的接口)。

    其实 WSGI 是分成 server 和 framework (即 application) 两部分 (当然还有 middleware)。严格说 WSGI 只是一个协议, 规范 server 和 framework 之间连接的接口。

    • WSGI server 把服务器功能以 WSGI 接口暴露出来。比如 mod_wsgi 是一种 server, 把 apache 的功能以 WSGI 接口的形式提供出来。
    • WSGI framework 就是我们经常提到的 Django 这种框架。不过需要注意的是, 很少有单纯的 WSGI framework , 基于 WSGI 的框架往往都自带 WSGI server。比如 Django、CherryPy 都自带 WSGI server 主要是测试用途, 发布时则使用生产环境的 WSGI server。而有些 WSGI 下的框架比如 pylons、bfg 等, 自己不实现 WSGI server。使用 paste 作为 WSGI server。

    Paste 是流行的 WSGI server, 带有很多中间件。还有 flup 也是一个提供中间件的库。

    搞清除 WSGI server 和 application, 中间件自然就清楚了。除了 session、cache 之类的应用, 前段时间看到一个 bfg 下的中间件专门用于给网站换肤的 (skin) 。中间件可以想到的用法还很多。

    这里再补充一下, 像 django 这样的框架如何以 fastcgi 的方式跑在 apache 上的。这要用到 flup.fcgi 或者 fastcgi.py (eurasia 中也设计了一个 fastcgi.py 的实现) 这些工具, 它们就是把 fastcgi 协议转换成 WSGI 接口 (把 fastcgi 变成一个 WSGI server) 供框架接入。整个架构是这样的: django -> fcgi2wsgiserver -> mod_fcgi -> apache 。

    虽然我不是 WSGI 的粉丝, 但是不可否认 WSGI 对 python web 的意义重大。有意自己设计 web 框架, 又不想做 socket 层和 http 报文解析的同学, 可以从 WSGI 开始设计自己的框架。在 python 圈子里有个共识, 自己随手搞个 web 框架跟喝口水一样自然, 非常方便。或许每个 python 玩家都会经历一个倒腾框架的

uWSGI是一款像php-cgi一样监听同一端口,进行统一管理和负载平衡的工具,uWSGI,既不用wsgi协议也不用fcgi协议,而是自创了一个uwsgi的协议,据说该协议大约是fcgi协议的10倍那么快。

uWSGI的主要特点如下:

  1. 超快的性能。
  2. 低内存占用(实测为apache2的mod_wsgi的一半左右)。
  3. 多app管理。
  4. 详尽的日志功能(可以用来分析app性能和瓶颈)。
  5. 高度可定制(内存大小限制,服务一定次数后重启等)。

下面正式介绍Nginx+uwsgi的部署。

uwsgi的官方文档:http://projects.unbit.it/uwsgi/wiki/Doc

1.安装uwsgi

ubuntu有uwsgi的ppa:

add-apt-repository ppa:stevecrozz/ppa 
apt-get update 
apt-get install uwsgi 

2. 用uwsgi代替mod_wsgi

Nginx 的整体配置这里不说了,假设已经明白 Nginx的基本配置,那么uwsgi就类似这么配置:

location / {  
  include uwsgi_params  
  uwsgi_pass 127.0.0.1:9090  
} 

再比如django就是:

 .......  
 from django.core.handlers.wsgi import WSGIHandler  
 application = WSGIHandler() 

然后运行uwsgi监听9090,其中-w后跟模块名,也就是刚才配置的myapp

 uwsgi -s :9090 -w myapp 

运行网站发现已经部署完成了。

3.uwsgi的参数

以上是单个project的最简单化部署,uwsgi还是有很多令人称赞的功能的,例如:

并发4个线程:

  uwsgi -s :9090 -w myapp -p 4 

主控制线程+4个线程:

  uwsgi -s :9090 -w myapp -M -p 4 

执行超过30秒的client直接放弃:

  uwsgi -s :9090 -w myapp -M -p 4 -t 30 

限制内存空间128M:

  uwsgi -s :9090 -w myapp -M -p 4 -t 30 --limit-as 128 

服务超过10000个req自动respawn:

  uwsgi -s :9090 -w myapp -M -p 4 -t 30 --limit-as 128 -R 10000 

后台运行等:

  uwsgi -s :9090 -w myapp -M -p 4 -t 30 --limit-as 128 -R 10000 -d uwsgi.log 

4.为uwsgi配置多个站点

为了让多个站点共享一个uwsgi服务,必须把uwsgi运行成虚拟站点:去掉“-w myapp”加上”–vhost”:

  uwsgi -s :9090 -M -p 4 -t 30 --limit-as 128 -R 10000 -d uwsgi.log --vhost 

然后必须配置virtualenv,virtualenv是Python的一个很有用的虚拟环境工具,这样安装:

  apt-get install Python-setuptools
  easy_install virtualenv 

然后设置一个/多个app基准环境:

  virtualenv /var/www/myenv 

应用环境,在此环境下安装的软件仅在此环境下有效:

 source /var/www/myenv/bin/activate  
 pip install django  
 pip install mako  
 ... 

最后配置nginx,注意每个站点必须单独占用一个server,同一server不同location定向到不同的应用不知为何总是失败,估计也 算是一个bug。


server {  
 listen       80;  
 server_name  app1.mydomain.com;  
 location / {  
   include uwsgi_params;  
   uwsgi_pass 127.0.0.1:9090;  
   uwsgi_param UWSGI_PYHOME /var/www/myenv;  
   uwsgi_param UWSGI_SCRIPT myapp1;  
   uwsgi_param UWSGI_CHDIR /var/www/myappdir1;  
 }  
}  

server {  
 listen       80;  
 server_name  app2.mydomain.com;  
 location / {  
   include uwsgi_params;  
   uwsgi_pass 127.0.0.1:9090;  
   uwsgi_param UWSGI_PYHOME /var/www/myenv;  
   uwsgi_param UWSGI_SCRIPT myapp2;  
   uwsgi_param UWSGI_CHDIR /var/www/myappdir2;  
 }  
} 

这样,重启nginx服务,两个站点就可以共用一个uwsgi服务了。

参考文章:Python 应用程序配置