Nginx+uWSGI 部署 Django 应用

 

原文来自:

http://www.oschina.net/question/54100_30386

http://obmem.info/?p=703

 

 

 

常见的django webapp 部署方式采用FCGI 或 WSGI的方式部署,今天我这备忘下采用uWSGI的部署方式。 目前我这博客就是采用 Nginx + uWSGI + Python + Django 构建的,部署虽没有php那样扔到目录那么方便,但是并发和性能消耗还是非常不错的。这里不想赘述关于FCGIWSGI,uWSGI之间的比较,网上关于这样的对比测试也有很多。这里说下部署过程。 uWSGI 的官方网站 http://projects.unbit.it/uwsgi/ wiki介绍的很详细。 Nginx关于HttpUwsgiModule的介绍http://wiki.nginx.org/HttpUwsgiModule.有这些资料参考,安装部署是很容易的事情。

uWSGI的安装

下载uWSGI的最新版

1 wget http://projects.unbit.it/downloads/uwsgi-0.9.9.2.tar.gz

因为我最后采用xml配置django app 的部署,所以编译 uWSGI 时候需要把libxml编译进去

1 sudo apt-get install libxml2-dev

剩下的就简单了

1 tar zxvf uwsgi-0.9.9.2.tar.gz
2 cd uwsgi-0.9.9.2
3 make -f Makefile.Py26 #指定你python的版本,如果你的python是2.7 就应该是 make -f Makefile.Py27
4 cp uwsgi /usr/sbin/uwsgi

至此 uWSGI 就算是安装完成了,下一步安装 Nginx > 0.8 的版本,因为只有Nginx > 0.8 的版本才支持wsgi

Nginx 安装

Ubuntu 默认源里面的Nginx版本比较旧,这里需要先添加一个Nginx的源,来通过apt-get安装新版本的Nginx

1 sudo add-apt-repository ppa:nginx/stable
2 apt-get update
3 apt-get install nginx

接下来配置Nginx 和 uWSGI部署Django App 了. 首先我们在Nginx中新建一个站点配置文件:

1 sudo vi /etc/nginx/sites-enabled/blog.hysia.com

内容如下:

01 server {
02     listen   80; ## listen for ipv4; this line is default and implied
03     #listen   [::]:80 default ipv6only=on; ## listen for ipv6
04  
05     server_name blog.hysia.com;
06  
07     access_log /var/log/nginx/blog.hysia.com-access.log ;
08     error_log /var/log/nginx/blog.hysia.com-error.log ;
09  
10     location / {
11             uwsgi_pass 127.0.0.1:8630;
12             include uwsgi_params;
13     }
14  
15 }

这样Nginx算是配置完了,现在看我们的Django app如何配置。

配置Django app

配置很简单,几乎不用改动你app的任何文件。
首先在你的app目录创建个wsgi.py 文件,内容如下:

1 import os,sys
2  
3 if not os.path.dirname(__file__) in sys.path[:1]:
4     sys.path.insert(0, os.path.dirname(__file__))
5 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
6  
7 from django.core.handlers.wsgi import WSGIHandler
8 application = WSGIHandler()

然后在app目录创建个django.xml文件,作为uWSGI运行的配置文件,内容如下:
1 <uwsgi>
2     <socket>127.0.0.1:8630</socket>
3     <chdir>/home/hysia/website/blog</chdir>
4     <pythonpath>..</pythonpath>
5     <module>wsgi</module>
6 </uwsgi>

最后一步,运行 uWSGI 就行了,如下:

1 uwsgi -x /home/hysia/website/blog/django.xml

就这样你的Django app 就用 uWSGI hold住了。当然django.xml的配置远不止这些,比如log文件,内存限制等等,具体的大家可以参看 http://projects.unbit.it/uwsgi/wiki/Example uWSGI handle 多个 Django app 的时候性能更出众,更多的探索自己去动手实践吧。

 

个人觉得php最方便的就是deployment了,只要把php文件丢到支持php的路径里面,然后访问那个路径就能使用了;无论给主机添加多少php应用,只要把目录改好就没你的事了,完全不用关心php-cgi运行得如何,deployment极为方便。

反观python,部属起来真是头痛,常见的部署方法有:

  1. fcgi:用spawn-fcgi或者框架自带的工具对各个project分别生成监听进程,然后和http服务互动
  2. wsgi:利用http服务的mod_wsgi模块来跑各个project

无论哪种都很麻烦,apache的mod_wsgi配置起来很麻烦,内存占用还大,如果要加上nginx作为静态页面的服务器那就更麻烦了;反正我的应用基本上到后来都是是各个project各自为战,且不说管理上的混乱,这样对负载也是不利的,空闲的project和繁忙的project同样需要占用内存,很容易出现站着茅坑不拉屎的现象。

如果有个啥东东能像php-cgi一样监听同一端口,进行统一管理和负载平衡,那真是能省下大量的部署功夫。偶然看到了uWSGI,才发现居然一直不知道有那么方便地统一部署工具。

uWSGI,既不用wsgi协议也不用fcgi协议,而是自创了一个uwsgi的协议,据说该协议大约是fcgi协议的10倍那么快,有个比较见下图

Nginx+uWSGI 部署 Django 应用

uWSGI的主要特点如下,其中一些功能让我感动得泪流满面

  1. 超快的性能
  2. 低内存占用(实测为apache2的mod_wsgi的一半左右)
  3. 多app管理(终于不用冥思苦想下个app用哪个端口比较好了-.-)
  4. 详尽的日志功能(可以用来分析app性能和瓶颈)
  5. 高度可定制(内存大小限制,服务一定次数后重启等)

总而言之uwgi是个部署用的好东东,正如uWSGI作者所吹嘘的:

If you are searching for a simple wsgi-only server, uWSGI is not for you, but if you are building a real (production-ready) app that need to be rock-solid, fast and easy to distribute/optimize for various load-average, you will pathetically and morbidly fall in love (we hope) with uWSGI.

正式开工

uwsgi的文档虽然蛮多也很详细,但是他们网站的排版真是让人无语,粗粗看上去根本不知道文档在哪里。其实是在这里:http://projects.unbit.it/uwsgi/wiki/Doc

0.安装uwsgi

ubuntu有uwsgi的ppa

add-apt-repository ppa:stevecrozz/ppa

apt-get update

apt-get install uwsgi

1. 用uwsgi代替mod_wsgi

nginx的整体配置说来话长,我也不再罗嗦了,假设已经明白nginx的基本配置,那么uwsgi就类似这么配置:

location / {

  include uwsgi_params

  uwsgi_pass 127.0.0.1:9090

}

这就是把所有url传给9090端口的uwsgi协议程序来互动。

再到project目录建立myapp.py,使得application调用框架的wsgi接口,比如web.py就是

......

app = web.application(urls, globals())

application = app.wsgifunc()

再比如django就是

.......

from django.core.handlers.wsgi import WSGIHandler

application = WSGIHandler()

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

uwsgi -s :9090 -w myapp

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

2. 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

更多用法见文档:http://projects.unbit.it/uwsgi/wiki/Doc

3.为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服务了。

4.实战应用

最初的设置完毕以后,再添加的应用,只需要在nginx里面进行少量修改,无需重启uwsgi,就能立刻部署完毕。uwsgi自带了基于django的监控uwsgi运行状态的工具,就拿它来部署好了:

server {

    listen 80;

    root   /var/www/django1.23;

    index  index.html index.htm;

    server_name uwsgiadmin.django.obmem.info;

    access_log  /var/log/nginx/django.access.log;

    location /media/ {

        root /var/www/django1.23/adminmedia;

        rewrite ^/media/(.*)$ /$1 break;

    }

    location / {

        include uwsgi_params;

        uwsgi_pass 127.0.0.1:9090;

        uwsgi_param UWSGI_PYHOME /var/www/django1.23/vtenv;

        uwsgi_param UWSGI_CHDIR /var/www/django1.23/uwsgiadmin;

        uwsgi_param UWSGI_SCRIPT uwsgiadmin_wsgi;

    }

}

于是uwsgi的监控信息可以在http://uwsgiadmin.django.obmem.info/ 看到用户名密码都是admin。

再比如LBForum论坛程序的部署:根据安装说明安装完毕,再按部署说明修改完配置文件,然后只需修改nginx配置文件:

server {

    listen 80;

    root   /var/www/django1.23;

    index  index.html index.htm;

    server_name lbforum.django.obmem.info;

    access_log  /var/log/nginx/django.access.log;

    location / {

        include uwsgi_params;

        uwsgi_pass 127.0.0.1:9090;

        uwsgi_param UWSGI_PYHOME /var/www/django1.23/vtenv;

        uwsgi_param UWSGI_CHDIR /var/www/django1.23/LBForum/sites/default;

        uwsgi_param UWSGI_SCRIPT lbforum_wsgi;

    }

}

于是 http://lbforum.django.obmem.info/ 就是论坛程序了。

后记

虽然写出来寥寥几行,配置的时候我可吃尽了uwsgi的苦头,有些想当然的用法完全不能成立,–no-site参数一加上去其他都好使LBForum怎么都部署不了,一开始多站点公用uwsgi怎么都成功不了等等。

python世界很有趣,一直会发现好玩的东东,但是python世界也很折腾人,大部分东东都是dev版本,文档缺失,各种兼容问题……大约是因为在python中,有个idea到实现出来实在是太过高效的关系吧,唉,被折腾死了。

This entry was posted in Linux, python. Bookmark the permalink.

47 Responses to 配置Nginx+uwsgi更方便地部署python应用

  1. amao says:

    回去试试。python的部署一直没有看懂,希望这次可以搞定。
    谢谢!

  2. ikbear says:

    哈哈,你的VPS能折腾脚本程序啦?

  3. wsh0408 says:

    请问SimpleCD不更新了吗?

    • observer says:

      这个还在搬家中,并且计划重写一遍,到时候再更新吧

      • wsh0408 says:

        我一直用的simplecd,因为你抓vc的时候,vc里面很多资源还没有审核删除掉,现在vc很多资源都是“因为版权问题,资源已被删除!”,所以,simplecd保留了很多原汁原味的好资源,呵呵。希望能一直保留下去。

  4. icyomik says:

    好文章,回去啃啃。。。

  5. Pingback: MoinMoin 与 Nginx, fastcgi 与 uwgi 的配置 | apt-blog.net IT民工养成计划 PT博客

  6. 小明猪 says:

    博主文章都很好啊,前段时间在百毒中看到几篇文章,发现是obmem.com这个域名的文字,com域名访问时发现在墙外?: O

  7. liuxurong says:

    [emerg]: open() “/usr/local/nginx/conf/uwsgi_params” failed (2: No such file or directory) in /usr/local/nginx/conf/nginx.conf:68
    configuration file /usr/local/nginx/conf/nginx.conf test failed

    这是为啥?。。求救~

  8. 刘齐 says:

    哈咯大大~~
    话说找你有点事情嘛
    想做一个非营利的视频站
    我想跟你好好研究研究嘛 留个电子邮箱地址或者发个邮件给我吧
    求你了 ~~~~(>_<)~~~~

  9. Liuxurong says:

    折腾了几天了,比较菜,百度GG了好多次…无果…汗,按上面说都安装好后,在django建的project建立了myapp

    也执行了 uwsgi -s :9090 -w myapp

    然后怎么处理project里的程序? 这时访问时候是不是会处理urls.py啊?..

    为什么会出现502呢..

    • observer says:

      502错误就是nginx请求9090端口没有得到回应的错误
      1.用netstat -pan | grep 9090检查9090端口是否已被正确监听
      2.检查nginx配置的端口是否正确
      PS:uwsgi -s :9090 -w myapp 这一步要在myapp.py所在目录下执行才有效

      整个流程是这样:web请求->nginx->nginx根据配置把动态请求pass给后台uwsgi->uwsgi接受到请求执行wsgi的app->django自己折腾完毕返回给uwsgi->uwsgi把处理结果返回给nginx->nginx把处理结果返回给用户

      • Liuxurong says:

        ImportError: Settings cannot be imported, because environment variable DJANGO_SETTINGS_MODULE is undefined.

        > <怎么弄呢。

        • Liuxurong says:

          在myapp加入了得出下面的结果

          os.environ['DJANGO_SETTINGS_MODULE'] = ‘settings’

          提示页也从502变了另外Django的提示页

      • Liuxurong says:

        uwsgi.applications dictionary is not defined, trying with the “applications” one…
        applications dictionary is not defined, trying with the “application” callable.
        application 0 () ready

          • observer says:

            看了一下你的出错信息,你django目录没添加到系统目录啊,算了给你一个完整的django myapp.py的样板吧,你自己看着玩吧
            这个是一个叫techblog的开源djangoblog程序的配置,目录在/var/www/django1.23/techblog
            import os,sys

            from os.path import abspath, dirname, join
            from site import addsitedir

            sys.path.insert(0, abspath(join(dirname(__file__), "../")))

            from django.conf import settings
            os.environ["DJANGO_SETTINGS_MODULE"] = "techblog.settings"

            sys.path.insert(0, join("/var/www/django1.23/techblog", "apps"))

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

          • Liuxurong says:

            谢谢谢!

  10. xiaojay says:

    请问能不能 一个站点,开一个uwsgi, 不公用,端口不通,这样貌似配置更方便?
    是不是性能上有损失?

    • xiaojay says:

      端口不同,笔误了

    • observer says:

      性能上当然没啥损失;而且本来就是那么配的,都按第一步配不就行了?

      这篇文章我的原创之处就在于公用端口,这方面你连e文的资料都找不到,官方文档都语焉不详,是我自己摸索的,这个得意之处被你华丽丽地无视了,呵呵。

      关键在于,你应用如果比较多,有些应用只属于玩票性质的,但又需要它存在着,多个应用多占内存,而且多个端口各种配置很麻烦啊,想想看你有20多个应用,分别占用9001-9020那么多端口,时间一长你知道哪个是哪个不?多麻烦啊。

  11. xiaojay says:

    了解了:)
    等我应用多了,就按你的方法配置了,哈哈,thanks a lot

  12. chronos says:

    问下,这个uwsgi有没有windows版本的,我试着在windows下编译,但没成功。提示
    Traceback (most recent call last):
    File “uwsgiconfig.py”, line 54, in
    uwsgi_os = os.uname()[0]
    AttributeError: ‘module’ object has no attribute ‘uname’
    看到windows版的nginx里面也有uwsgi所以就想试一下。似乎找不到这方面的资料。
    现在一直在用tornado web server。

  13. raph says:

    问一下,我启动uwsgi,他绑定的是0.0.0.0:9000不是127.0.0.1的,怎么回事呀

  14. lei says:

    nginx的配置能否注释一下各行,我配置了django但是后台管理页面有问题如图http://weibo.com/1659487382/l4ETwjDKv

  15. shell says:

    一个站点只能部署一个的原因是因为,uwsgi使用SERVER_NAME|SCRIPT_NAME来计算应用的。你又没改过SCRIPT_NAME ,同一个SERVER_NAME当然只能部署一个。解决方法也很简单,在locateion里面自定义一个SERVER_NAME就可以了。
    另外,virtualenv也不是非用不可。下面是一个web.py和mercurial混合部署的例子中,hg的部分。自定义了SERVER_NAME,并且没用virtualenv。
    location /hg {
    include uwsgi_params;
    uwsgi_param UWSGI_PYHOME /usr;
    uwsgi_param UWSGI_CHDIR /var/web/hg;
    uwsgi_param UWSGI_SCRIPT hgweb;
    uwsgi_param SCRIPT_NAME /;
    uwsgi_param SERVER_NAME hgweb;
    uwsgi_pass unix:/tmp/uwsgi.socket;
    }
    实话说,我也写了篇东西,还参考了你的。在这里(http://shell909090.com/blog/?p=1811),不过怎么做引用阿。

  16. 郁也风 says:

    不知道有没有人成功地把uwsgi+virualenv+flask配置成功阿?我这边死活不好使呢奇怪

  17. Pingback: 我的那些花儿 » Howto Deploy MoinMoin via Nginx and uWSGI

  18. Pingback: 我的那些花儿 » 20110328

  19. Pingback: 我的那些花儿 » Howto Deploy OSQA via Nginx and uWSGI

  20. 至尊宝 says:

    请教一下, uwsgi有没有支持web应用热部署的特性?

  21. Pingback: uWSGI配置文件 | 工具牛

  22. davidx says:

    Hi, 我在自己配置多站点的时候, 启动uwsgi的参数里加上了–vhost, 但是什么访问第二个网站后, 第一个网站的内容, 也就跟第二个相同了呢?
    我的启动参数如下:
    uwsgi-2.6 -s /tmp/uwsgi.sock -C -M -d uwsgi.log –vhost -t 30

    我没有使用virtualenv, 是不是这个必须用上才行呢?

  23. Pingback: 在免费亚马逊EC2上编译安装nginx+uwsgi+bottle

  24. Pingback: vps部署nginx+FastCGI+web.py环境 | A Nerd or A Geek

  25. 听临 says:

    学习一门技术,花在配置环境上的时间太多了…

    感谢你的教程啊.

    Python提倡简单是美,结果是在web开发上,却不见得比PHP简单多少~~

你可能感兴趣的:(django)