本文将介绍如何部署简单的WSGI应用和常见的web框架。
安装uWSGI以及Python支持
uWSGI是一个(大的)C应用,因此需要一个C编译器 (例如gcc或者clang)以及Python开发头文件。
在基于Debian的发行版上,apt-get install build-essential python-dev
多种方式为Python安装uWSGI:
1) via pip:pip install uwsgi
2) 网络安装器:curl http://uwsgi.it/install | bash -s default /tmp/uwsgi (把uWSGI二进制安装到/tmp/uwsgi ,可随意修改)。
3) 下载源tarball文件,然后执行”make”命令
wget http://projects.unbit.it/downloads/uwsgi-latest.tar.gz
tar zxvf uwsgi-latest.tar.gz
cd <dir>
make
第一个WSGI应用
让我们从一个简单的”Hello World”开始:
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"]
(将其保存为 foobar.py)。
由一个简单的Python函数组成。之所以称之为 “application”,因为它是uWSGI Python加载器将会搜索的默认函数。
将其部署在HTTP端口9090,启动uWSGI来运行一个HTTP服务器/路由器,它会传递请求到你的WSGI应用:uwsgi --http :9090 --wsgi-file foobar.py
注解:当你有一个前端web服务器,或者你正进行某些形式的基准时,不要使用 --http ,使用 --http-socket。
添加并发和监控
默认情况下,uWSGI启动一个单一的进程和一个单一的线程。你想进行的第一个调整可能是增加并发性。可以用--processes选项添加更多的进程,或者使用--threads选项添加更多的线程 (或者同时添加)。
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2
这将会生成4个进程 (每个进程有2个线程),一个master进程 (在Inc死掉的时候会生成它们) 和HTTP路由器。
一个重要的任务是监控。在生产部署上,了解正在发生的事情至关重要的。stats子系统将uWSGI的内部统计数据作为JSON导出:
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
对你的应用进行几次请求,然后telnet到端口9191,你会获得大量有趣的信息。你可能想要使用”uwsgitop” (仅需 pip install 来安装它),这是一个类似于top的工具,用来监控实例。
将它放在一个完整的web服务器之后
即使uWSGI HTTP路由器是稳定并且高性能的,但是你或许想要将你的应用放在一个全功能的web服务器之后。
uWSGI原生支持HTTP, FastCGI, SCGI及其特定的名为”uwsgi”的协议。最好的协议显然是uwsgi,nginx和Cherokee已经支持它了 (虽然有各种Apache模块可用)。
一个常用的nginx配置如下:
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
}
这表示“传递每一个请求给绑定到3031端口并使用uwsgi协议的服务器”。
现在我们可以生成uWSGI来本地使用uwsgi协议:
uwsgi --socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
运行ps aux ,你会看到一个进程已经移除了HTTP路由器,因为我们的“worker” (被分配给uWSGI的进程) 本地使用uwsgi协议。
如果你的代理/web服务器/路由器使用HTTP,那么你必须告诉uWSGI本地使用http协议 (这与会自己生成一个代理的–http不同):
uwsgi --http-socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
部署Django
Django大概是最常使用的Python web框架了。部署它是相当容易的 (我们继续配置4个进程,每个进程有2个线程)。
假设Django工程/home/foobar/myproject
uwsgi --socket 127.0.0.1:3031 --chdir /home/foobar/myproject/ --wsgi-file myproject/wsgi.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
使用 --chdir ,移到指定的目录下。在Django中,需要使用它来正确加载模块。
处理这样长的命令行并不实际,并且愚蠢而易于犯错。uWSGI用.ini文件支持多种配置。
[uwsgi]
socket = 127.0.0.1:3031
chdir = /home/foobar/myproject/
wsgi-file = myproject/wsgi.py
processes = 4
threads = 2
stats = 127.0.0.1:9191
仅需运行: uwsgi yourfile.ini
如果文件myproject/wsgi.py (或者任何你的工程的名字) 并不存在,那么很有可能使用的是Django的一个老(< 1.4)版本。在这种情况下,需要多一点配置:
uwsgi --socket 127.0.0.1:3031 --chdir /home/foobar/myproject/ --pythonpath .. --env DJANGO_SETTINGS_MODULE= myproject.settings --module "django.core.handlers.wsgi:WSGIHandler()" --processes 4 --threads 2 --stats 127.0.0.1:9191
或者使用.ini文件:
[uwsgi]
socket = 127.0.0.1:3031
chdir = /home/foobar/myproject/
pythonpath = ..
env = DJANGO_SETTINGS_MODULE=myproject.settings
module = django.core.handlers.wsgi:WSGIHandler()
processes = 4
threads = 2
stats = 127.0.0.1:9191
更老的(< 1.4)Django发布版本需要设置 env, module 和 pythonpath (.. 允许我们访问 myproject.settings 模块)。
部署Flask
Flask是一个流行的Python web微框架。
将下面例子保存为 myflaskapp.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "<span style='color:red'>I am app 1</span>"
Flask将其WSGI函数 (第一个WSGI应用中,有个函数称为”application”)导出为”app”,因此需要指示uWSGI使用它。仍然使用4个进程/2个线程,以及uwsgi socket:
uwsgi --socket 127.0.0.1:3031 --wsgi-file myflaskapp.py --callable app --processes 4 --threads 2 --stats 127.0.0.1:9191
(唯一添加的是 --callable 选项)。
部署web2py
解压缩web2py源发布版本到所选的目录下,然后编写一个uWSGI配置文件:
[uwsgi]
http = :9090
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
注解:在近期的web2py发布版本中,可能需要将wsgihandler.py脚本拷贝到handlers目录。
使用HTTP路由器,仅需在浏览器中访问端口9090,你就能看到web2py欢迎页面。
点击管理员界面,然后……哎哟,不能用,因为需要HTTPS。不要担心,uWSGI路由器是可以使用HTTPS的 (确保有OpenSSL开发头文件:安装它们,然后重新构建uWSGI,构建系统将会自动检测到它)。
首先,生成密钥和证书:
openssl genrsa -out foobar.key 2048
openssl req -new -key foobar.key -out foobar.csr
openssl x509 -req -days 365 -in foobar.csr -signkey foobar.key -out foobar.crt
然后修改uWSGI配置:
[uwsgi]
https = :9090,foobar.crt,foobar.key
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
重新运行uWSGI,并且在浏览器中使用 https:// 访问9090端口。
开机自动启动uWSGI
如果你想打开vi,然后写一个init.d脚本来生成uWSGI,那么请坐下(并且冷静下来),先确保你的系统没有提供一个更好(更现代)的方法。
每个发行版本都选择了一个启动系统 (Upstart, Systemd...) ,并且有大量可用的进程管理器 (supervisord, god, monit, circus...)。
uWSGI会跟它们都很好地集成 (我们希望是这样的),但如果你计划部署大量的应用,那么看看uWSGI Emperor - 它或多或少是每个devops工程师的梦想。
安全性和可用性
总是 避免以root用户运行你的uWSGI实例。你可以使用 uid 和 gid 选项来去除权限:
[uwsgi]
https = :9090,foobar.crt,foobar.key
uid = foo
gid = bar
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
如果你需要绑定到特许端口 (例如用于HTTPS的443),那么使用共享socket。它们在去除权限之前创建,并且可以通过 =N 语法引用,其中,N是socket号 (从0开始):
[uwsgi]
shared-socket = :443
https = =0,foobar.crt,foobar.key
uid = foo
gid = bar
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
web应用部署的一个常见问题是“卡住的请求”。你所有的线程/worker都卡住了 (请求阻塞) ,而你的应用无法接收更多的请求。要避免这个问题,你可以设置一个 harakiri 定时器。它是一个监控器 (由master进程管理),会摧毁那些卡住超过指定秒数的进程 (小心选择 harakiri 值)。例如,你也许想要摧毁那些阻塞超过30秒的worker:
[uwsgi]
shared-socket = :443
https = =0,foobar.crt,foobar.key
uid = foo
gid = bar
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
harakiri = 30
除此之外,自uWSGI 1.9起,stats服务器导出了全部请求变量,你可以(实时)看到你的实例正在做什么 (对于每个worker, thread 或者异步核)。
