轉(zhuǎn)載自Python/WSGI應(yīng)用快速入門
本文將介紹如何部署簡(jiǎn)單的WSGI應(yīng)用和常見的web框架。
安裝uWSGI以及Python支持
uWSGI是一個(gè)(大的)C應(yīng)用窜锯,因此需要一個(gè)C編譯器 (例如gcc或者clang)以及Python開發(fā)頭文件。
在基于Debian的發(fā)行版上,apt-get install build-essential python-dev
多種方式為Python安裝uWSGI:
1) via pip:pip install uwsgi
2) 網(wǎng)絡(luò)安裝器:curl http://uwsgi.it/install | bash -s default /tmp/uwsgi (把uWSGI二進(jìn)制安裝到/tmp/uwsgi ,可隨意修改)悲幅。
3) 下載源tarball文件捕虽,然后執(zhí)行”make”命令
wget http://projects.unbit.it/downloads/uwsgi-latest.tar.gz
tar zxvf uwsgi-latest.tar.gz
cd <dir>
make
第一個(gè)WSGI應(yīng)用
讓我們從一個(gè)簡(jiǎn)單的”Hello World”開始:
def application(env, start_response):
? ? start_response('200 OK', [('Content-Type','text/html')])
? ? return [b"Hello World"]
(將其保存為 foobar.py)。
由一個(gè)簡(jiǎn)單的Python函數(shù)組成诫肠。之所以稱之為 “application”,因?yàn)樗莡WSGI Python加載器將會(huì)搜索的默認(rèn)函數(shù)欺缘。
將其部署在HTTP端口9090栋豫,啟動(dòng)uWSGI來運(yùn)行一個(gè)HTTP服務(wù)器/路由器,它會(huì)傳遞請(qǐng)求到你的WSGI應(yīng)用:uwsgi --http :9090 --wsgi-file foobar.py
注解:當(dāng)你有一個(gè)前端web服務(wù)器谚殊,或者你正進(jìn)行某些形式的基準(zhǔn)時(shí)丧鸯,不要使用 --http ,使用 --http-socket嫩絮。
添加并發(fā)和監(jiān)控
默認(rèn)情況下丛肢,uWSGI啟動(dòng)一個(gè)單一的進(jìn)程和一個(gè)單一的線程。你想進(jìn)行的第一個(gè)調(diào)整可能是增加并發(fā)性剿干》湓酰可以用--processes選項(xiàng)添加更多的進(jìn)程,或者使用--threads選項(xiàng)添加更多的線程 (或者同時(shí)添加)置尔。
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2
這將會(huì)生成4個(gè)進(jìn)程 (每個(gè)進(jìn)程有2個(gè)線程)杠步,一個(gè)master進(jìn)程 (在Inc死掉的時(shí)候會(huì)生成它們) 和HTTP路由器。
一個(gè)重要的任務(wù)是監(jiān)控榜轿。在生產(chǎn)部署上幽歼,了解正在發(fā)生的事情至關(guān)重要的。stats子系統(tǒng)將uWSGI的內(nèi)部統(tǒng)計(jì)數(shù)據(jù)作為JSON導(dǎo)出:
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
對(duì)你的應(yīng)用進(jìn)行幾次請(qǐng)求谬盐,然后telnet到端口9191甸私,你會(huì)獲得大量有趣的信息。你可能想要使用”uwsgitop” (僅需 pip install 來安裝它)飞傀,這是一個(gè)類似于top的工具颠蕴,用來監(jiān)控實(shí)例泣刹。
將它放在一個(gè)完整的web服務(wù)器之后
即使uWSGI HTTP路由器是穩(wěn)定并且高性能的,但是你或許想要將你的應(yīng)用放在一個(gè)全功能的web服務(wù)器之后犀被。
uWSGI原生支持HTTP, FastCGI, SCGI及其特定的名為”uwsgi”的協(xié)議椅您。最好的協(xié)議顯然是uwsgi,nginx和Cherokee已經(jīng)支持它了 (雖然有各種Apache模塊可用)寡键。
一個(gè)常用的nginx配置如下:
location / {
? ? include uwsgi_params;
? ? uwsgi_pass 127.0.0.1:3031;
}
這表示“傳遞每一個(gè)請(qǐng)求給綁定到3031端口并使用uwsgi協(xié)議的服務(wù)器”掀泳。
現(xiàn)在我們可以生成uWSGI來本地使用uwsgi協(xié)議:
uwsgi --socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
運(yùn)行ps aux ,你會(huì)看到一個(gè)進(jìn)程已經(jīng)移除了HTTP路由器西轩,因?yàn)槲覀兊摹皐orker” (被分配給uWSGI的進(jìn)程) 本地使用uwsgi協(xié)議员舵。
如果你的代理/web服務(wù)器/路由器使用HTTP,那么你必須告訴uWSGI本地使用http協(xié)議 (這與會(huì)自己生成一個(gè)代理的–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框架了藕畔。部署它是相當(dāng)容易的 (我們繼續(xù)配置4個(gè)進(jìn)程马僻,每個(gè)進(jìn)程有2個(gè)線程)。
假設(shè)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中,需要使用它來正確加載模塊溶弟。
處理這樣長(zhǎng)的命令行并不實(shí)際女淑,并且愚蠢而易于犯錯(cuò)。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
僅需運(yùn)行: uwsgi yourfile.ini
如果文件myproject/wsgi.py (或者任何你的工程的名字) 并不存在鸭你,那么很有可能使用的是Django的一個(gè)老(< 1.4)版本。在這種情況下擒权,需要多一點(diǎn)配置:
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發(fā)布版本需要設(shè)置 env, module 和 pythonpath (.. 允許我們?cè)L問 myproject.settings 模塊)袱巨。
部署Flask
Flask是一個(gè)流行的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函數(shù) (第一個(gè)WSGI應(yīng)用中碳抄,有個(gè)函數(shù)稱為”application”)導(dǎo)出為”app”愉老,因此需要指示uWSGI使用它。仍然使用4個(gè)進(jìn)程/2個(gè)線程纳鼎,以及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 選項(xiàng))。
部署web2py
解壓縮web2py源發(fā)布版本到所選的目錄下裳凸,然后編寫一個(gè)uWSGI配置文件:
[uwsgi]
http = :9090
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
注解:在近期的web2py發(fā)布版本中贱鄙,可能需要將wsgihandler.py腳本拷貝到handlers目錄。
使用HTTP路由器姨谷,僅需在瀏覽器中訪問端口9090逗宁,你就能看到web2py歡迎頁(yè)面。
點(diǎn)擊管理員界面梦湘,然后……哎喲瞎颗,不能用件甥,因?yàn)樾枰狧TTPS。不要擔(dān)心哼拔,uWSGI路由器是可以使用HTTPS的 (確保有OpenSSL開發(fā)頭文件:安裝它們引有,然后重新構(gòu)建uWSGI,構(gòu)建系統(tǒng)將會(huì)自動(dòng)檢測(cè)到它)倦逐。
首先譬正,生成密鑰和證書:
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
重新運(yùn)行uWSGI,并且在瀏覽器中使用 https:// 訪問9090端口檬姥。
開機(jī)自動(dòng)啟動(dòng)uWSGI
如果你想打開vi曾我,然后寫一個(gè)init.d腳本來生成uWSGI,那么請(qǐng)坐下(并且冷靜下來)健民,先確保你的系統(tǒng)沒有提供一個(gè)更好(更現(xiàn)代)的方法抒巢。
每個(gè)發(fā)行版本都選擇了一個(gè)啟動(dòng)系統(tǒng)? (Upstart, Systemd...) ,并且有大量可用的進(jìn)程管理器 (supervisord, god, monit,? circus...)秉犹。
uWSGI會(huì)跟它們都很好地集成 (我們希望是這樣的)蛉谜,但如果你計(jì)劃部署大量的應(yīng)用,那么看看uWSGI Emperor - 它或多或少是每個(gè)devops工程師的夢(mèng)想凤优。
安全性和可用性
總是 避免以root用戶運(yùn)行你的uWSGI實(shí)例悦陋。你可以使用 uid 和 gid 選項(xiàng)來去除權(quán)限:
[uwsgi]
https = :9090,foobar.crt,foobar.key
uid = foo
gid = bar
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
如果你需要綁定到特許端口 (例如用于HTTPS的443),那么使用共享socket筑辨。它們?cè)谌コ龣?quán)限之前創(chuàng)建俺驶,并且可以通過 =N 語(yǔ)法引用注簿,其中票从,N是socket號(hào) (從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應(yīng)用部署的一個(gè)常見問題是“卡住的請(qǐng)求”。你所有的線程/worker都卡住了 (請(qǐng)求阻塞) 铐望,而你的應(yīng)用無法接收更多的請(qǐng)求楚昭。要避免這個(gè)問題栖袋,你可以設(shè)置一個(gè) harakiri 定時(shí)器。它是一個(gè)監(jiān)控器 (由master進(jìn)程管理)抚太,會(huì)摧毀那些卡住超過指定秒數(shù)的進(jìn)程 (小心選擇 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
除此之外尿贫,自u(píng)WSGI 1.9起电媳,stats服務(wù)器導(dǎo)出了全部請(qǐng)求變量,你可以(實(shí)時(shí))看到你的實(shí)例正在做什么 (對(duì)于每個(gè)worker, thread 或者異步核)庆亡。