uwsgi under debian

    好了,debian官方的uwsgi总算出来了。包已经到了testing,stable暂时别指望了,等下一次release吧。这次打的包,比贝壳打的复杂多了。贝壳自己只打了python专用的包,debian官方的包将多个语言分别打成了plugins。

    下面说说,使用debian官方的包如何做uwsgi发布,还是vhost模式哦。
    首先安装uwsgi,uwsgi-plugin-python这两个包。uwsgi-plugin-greenlet-python也可以考虑,装不装看你的需求。
    然后在/etc/uwsgi/apps-available/sites.xml下面写一个文本文件,内容如下:

<uwsgi>

<vhost/>
<no-site/>
</uwsgi>
    再从/etc/uwsgi/apps-enabled/sites.xml链接过去,重启uwsgi服务,事情就搞定了。
    默认的配置在/usr/share/uwsgi/conf/default.ini,可以看看是否都满意了。一般来说,master和no-orphans都建议打开,chmod-socket最高660,改成600应该也可以工作。贝壳的机器负载小,只用一个worker就够了,所以完整的配置是这样的:
<uwsgi>
<plugins>greenlet,ugreen</plugins>
<workers>1</workers>
<reload-on-as>128</reload-on-as>
<vhost/>
<no-site/>
</uwsgi>
    nginx里面如此设定:
location /asdf {
include uwsgi_params;
uwsgi_param UWSGI_PYHOME /usr;
uwsgi_param UWSGI_CHDIR /var/web/hosts;
uwsgi_param UWSGI_SCRIPT main;
uwsgi_pass unix:/run/uwsgi/sites/socket;
}
    其中,我的程序放在/var/web/hosts底下,使用系统环境来运行(而不是virtualenv),主脚本(带applications那个)是main.py。unix socket和上文default.ini里面的socket正好对应上。
    同理,我们其实还可以开多个uwsgi应用,只要放置多个xml配置就好。不过既然都采用了vhost模式,何必还开多个呢?这毕竟不是虚拟网站,要给其他人使用的。

使用uwsgi搭建python应用

   wsgi是python的一个标准web服务接口,具体去google pep文档,不解释。在李木头的忽悠下,贝壳试用了一下uwsgi搭建python服务器,感觉还不错。
   首先,贝壳将uwsgi打包成deb包,因为这东西和python基本没什么关系,就是一个标准的系统守护服务程序。其中贝壳测试了一下,uwsgi编译的时候是依赖版本的。所以请教了一下thomas,打了uwsgi2.6和uwsgi2.7两个包。没办法,mercurial对python2.7的支持不是很好,每次都出问题。具体的可以加贝壳的repos: http://shell909090.com/debian/ testing,然后通过一下贝壳的key,就可以直接安装uwsgi2.6了。当然,不通过key也可以,只是每次安装升级都有警告。
   贝壳写了一个很简单的init.d,使用–vhost来启动uwsgi为服务模式。这种模式的好处是,uwsgi的具体执行的应用都是由nginx来确定的,因此所有的映射只需要修改nginx配置就好。uwsgi参数很多,包括可以指定内存限制,工作进程/线程,定时重启工作进程,多解释器等等。是一个高效的,功能强大的服务器。具体可以自己参考调整。最好的的地方是,uwsgi还支持virtualenv,你可以给不同的应用建立不同的工作环境,从而在环境中使用指定的包,而不是系统包。
   下面是一个nginx配置的例子。
      location /ticket {
               include uwsgi_params;
               uwsgi_param UWSGI_PYHOME /usr;
               uwsgi_param UWSGI_CHDIR /home/shell/workspace/hg/thost;
               uwsgi_param UWSGI_SCRIPT main;
               uwsgi_pass unix:/var/run/uwsgi.socket;
       }

       location /mlocate {
               include uwsgi_params;
               uwsgi_param UWSGI_PYHOME /usr;
               uwsgi_param UWSGI_CHDIR /home/shell/workspace/hg/thost;
               uwsgi_param UWSGI_SCRIPT main;
               uwsgi_pass unix:/var/run/uwsgi.socket;
       }

       location /hg {
               include uwsgi_params;
               uwsgi_param UWSGI_PYHOME /usr;
               uwsgi_param UWSGI_CHDIR /home/shell/workspace/hg;
               uwsgi_param UWSGI_SCRIPT hgweb;
               uwsgi_param SCRIPT_NAME /;
               uwsgi_param SERVER_NAME hgweb;
               uwsgi_pass unix:/var/run/uwsgi.socket;
       }
   这里面设定了三个应用。由于贝壳不需要virtualenv,所以PYHOME设定了/usr。第一二个应用的基础路径在/home/shell/workspace/hg/thost,脚本叫做main.py。第三个应用的基础路径在/home/shell/workspace/hg,脚本叫做hgweb.py。需要注意的是,uwsgi会以模块方式导入这些脚本,然后使用其中的application对象作为wsgi处理函数。所以不要把application对象赋值放在if __name__ == ‘__main__’里面,那没用的。第三个应用指定了SCRIPT_NAME和SERVER_NAME,是因为hg的wsgi模块没有SCRIPT_NAME不工作,而这个应用和前两个不在一起,所以如果不指定SERVER_NAME会导致覆盖冲突。
    这种部署模式的好处是,我可以使用一个宿主来管理所有的应用,而不必每个应用启动一个宿主,省去了多个宿主管理的麻烦。而多进程,压力分布等等问题都被uwsgi的配置系统搞定了。于是应用程序宿主做到了彻底的免管理,即装即用,只用调节性能匹配即可。具体程序配置下放到nginx中,要修改映射关系只用管理一个位置。

如何做一个mercurial的http发布

    我假定你了解hg,了解python,理解nginx或者其他cgi/fcgi的配置过程。现在想用http发布自己的mercurial仓库,而且可能发布一群,怎么操作呢?

    首先,复制模板文件过来,你可以挑选其中之一。以下是debian的文件位置,其他发布请自行查询。
/usr/share/doc/mercurial-common/examples/hgweb.wsgi
/usr/share/doc/mercurial-common/examples/hgweb.fcgi
/usr/share/doc/mercurial-common/examples/hgweb.cgi

    我使用nginx+fastcgi模式部署,因此复制了hgweb.fcgi。我假定你的仓库在~/hg下面,有很多子仓库。复制hgweb.fcgi到~/hg/下,改名为hgweb.py,并修改以下两行。
config = "/path/to/you/config"
WSGIServer(application, bindAddress='hgweb.sock').run()
    其中bindAddress为你需要监听的unixsocket路径,没有前缀表示在当前目录生成。而后建立配置文件,大概为以下内容。
[web]
allow_push = someone
push_ssl = false
[paths]
/hg/proj1=/path/to/proj1
/hg/proj2=/path/to/proj2
    以上就完成了hgweb的服务配置,/hg/proj1是你的url映射路径,/path/to/proj1是物理路径。someone是允许进行push的人,而push_ssl是允许http推送。而后,启动服务。
python hgweb.py &
chmod 666 hgweb.sock
    注意,这里要用screen之类的程序来启动hgweb,否则term关闭后服务进程停止,就没的玩了。修改权限是因为debian下的nginx使用www-data运行,对/home/user/hg没有读写权限,导致无法使用unixsock。
    在nginx中做以下配置。
location ^~ /hg/ {
    limit_except GET {
        auth_basic "Restricted";
        auth_basic_user_file /home/user/hg/users;
    }
    fastcgi_pass   unix:/home/user/hg/hgweb.sock;
    include fastcgi_params;
}
    如果你不需要auth,可以自行参照nginx的配置修改。其他web服务器以此类推。重启服务后,http://domains/hg/proj1就可以访问到proj1了。
    当然,其实最后还要提一句,如果你不需要web界面,可以直接设定将文件内容直接发出去,这样也是可以做pull/push的。
参考:

以nginx作为subversion前端的一些细节

本文系电脑资料,同步到blog上。小黄姐姐不必看了,可以帮我留个言。
nginx性能不错,可惜不支持WebDAV,因此没法拿来作为subversion的http服务。于是考虑开一个nginx作为前端,后端就跑一个apache来作为容器。配置这么写的(节选):
=========/etc/nginx/sites-enabled/default=========
server {
listen   443;
server_name  OOXX

ssl  on;
ssl_certificate  keys/server.crt;
ssl_certificate_key  keys/server.key;

ssl_session_timeout  5m;

ssl_protocols  SSLv2 SSLv3 TLSv1;
ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers   on;

access_log  /var/log/nginx/localhost.access.log;
include             /etc/nginx/mapping-ssl;
error_page   500 502 503 504  /50x.html;
location = /50x.html {
root   /var/www/nginx-default;
}
}
========================================
打开了一个https的服务,这是当然的,svn传输的数据使用http很危险。
===========/etc/nginx/mapping-ssl=============
location ^~ /svn {
proxy_set_header    Destination $http_destination;
proxy_pass          http://apache_svr;

proxy_set_header    Host            $host;
proxy_set_header    X-Real-IP       $remote_addr;
proxy_set_header    X-Forwarded-Host $host;
proxy_set_header    X-Forwarded-Proto https;
proxy_set_header    X-Forwarded-Server $host;
proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
}
========================================
将/svn下面的所有请求交给apache2。
=====/etc/apache2/mods-enabled/dav_svn.conf=====
<Location /svn/main>
DAV svn

SVNPath /var/web/svn/main
AuthType Basic
AuthName “Subversion Repository”
Require valid-user
AuthUserFile /var/web/svn/main/conf/passwd

AuthzSVNAccessFile /var/web/svn/main/conf/authz
</Location>
========================================
看起来很美,但是在使用中会发生502错误,原因是来自文件移动后,svn会使用COPY作为Verb去请求服务器端,这时候发生了这样一条日志:
==========/var/log/apache2/access.log==========
127.0.0.1 – {user} [02/Apr/2010:11:07:31 +0800] “COPY {path} HTTP/1.0″ 502 546 “-” “SVN/1.5.4 (r33841)/TortoiseSVN-1.5.5.14361 neon/0.28.3″
========================================
搜索了一下,这是因为使用https作为http服务的前端造成的,这里(https://secure.bonkabonka.com/blog/2008/01/04/nginx_fronting_for_subversion.html)提到了解决方案,而它又引用了另一个网页(http://silmor.de/49)解释细节。不幸的是,这个细节是错误的。关键在于这句上
LoadModule headers_module /usr/lib/apache2/modules/mod_headers_too.so
仔细看一下就会发现,mod_headers_too应当是mod_headers。在debian下,应当执行这几条指令。
cd /etc/apache2/mods-enabled
ln -s ../mods-available/headers.load headers.load
然后,在/etc/apache2/httpd.conf中写入以下内容:
RequestHeader edit Destination ^https http early
问题解决,Q.E.D。