介紹
在這個指南中,我們將建立一個由uWSGI提供服務的簡單的WSGI應用程序。我們將使用Nginx Web服務器作為反向代理到應用程序服務器,以提供更強大的連接處理。我們將在Ubuntu 14.04服務器上安裝和配置這些組件。
定義和概念
澄清一些術語
在我們深入研究之前,我們應該解釋一些與我們將要處理的相關概念相關的混淆術語。這三個單獨的術語似乎是可以互換使用的,但實際上具有不同的含義:
- WSGI:一個定義了應用程序或框架與應用程序/ Web服務器之間通信的標准接口的Python規範。這是為了簡化和標準化這些組件之間的通信以實現一致性和可互換性而創建的。這基本上定義了一個可以在其他協議上使用的API接口。
- uWSGI:一個旨在提供開發和部署網絡應用和服務的完整堆棧的應用服務器容器。其主要組件是一個能夠處理不同語言應用的應用服務器。它通過WSGI規範定義的方法與應用程序通信,並與其他網絡服務器通過各種其他協議進行通信。這是將傳統網絡服務器的請求轉換為應用程序可以處理的格式的部分。
- uwsgi:由uWSGI服務器實現的一個快速的二進制協議,用於與更全功能的網絡服務器通信。這是一種線路協議,而不是傳輸協議。這是與代理請求到uWSGI的網絡服務器通信的首選方式。
WSGI應用要求
WSGI規範定義了堆棧中網絡服務器和應用程序部分之間的接口。在這個上下文中,“網絡服務器”指的是uWSGI服務器,它負責使用WSGI規範將客戶端請求轉換為應用程序。這簡化了通信,並創建了鬆散耦合的組件,因此您可以輕鬆地替換任何一側而不會遇到太多麻煩。
Web服务器(uWSGI)必须具有通过触发定义的“可调用”向应用程序发送请求的能力。 可调用只是应用程序的入口点,在这里,Web服务器可以调用带有一些参数的函数。 预期的参数是环境变量字典和Web服务器(uWSGI)组件提供的可调用。
作为响应,应用程序返回一个可迭代对象,该对象将用于生成客户端响应的主体。 它还将调用作为参数接收的Web服务器组件可调用项。 触发Web服务器可调用项时的第一个参数将是HTTP状态代码,第二个将是元组列表,每个元组定义要发送回客户端的响应头和值。
通过在这种情况下由uWSGI提供的“web服务器”组件,我们只需要确保我们的应用程序具有上述描述的特性。 我们还将设置Nginx来处理实际的客户端请求并将它们代理到uWSGI服务器。
安装组件
要开始,我们需要在Ubuntu 14.04服务器上安装必要的组件。 我们主要可以使用apt
和pip
来完成这项工作。
首先,刷新您的apt
包索引,然后安装Python开发库和标头,pip
Python软件包管理器以及Nginx Web服务器和反向代理:
sudo apt-get update
sudo apt-get install python-dev python-pip nginx
一旦套件安裝完成,您將可以訪問pip
Python套件管理器。我們可以使用此工具安裝virtualenv
套件,用於將我們的應用程式Python環境與系統上可能存在的其他環境隔離開來:
sudo pip install virtualenv
完成後,我們可以開始為應用程式創建一般結構。我們將創建上述虛擬環境並在此環境中安裝uWSGI應用程式伺服器。
設置應用程式目錄和虛擬環境
我們將首先創建一個用於我們應用程式的文件夾。這可以包含一個包含實際應用程式代碼的嵌套文件夾,在更完整的應用程式中。 對於我們的目的,此目錄將僅包含我們的虛擬環境和我們的WSGI入口點:
mkdir ~/myapp/
接下來,移動到該目錄,以便我們可以為應用程式設置環境:
cd ~/myapp
使用virtualenv
命令創建虛擬環境。我們將其簡單地稱為myappenv
:
virtualenv myappenv
A new Python environment will be set up under a directory called myappenv
. We can activate this environment by typing:
source myappenv/bin/activate
您的提示應該更改以指示您現在正在虛擬環境中操作。它會顯示類似於以下內容:
(myappenv)username@host:~/my_app$
如果您希望隨時離開此環境,只需輸入:
deactivate
如果您已經停用了您的環境,請重新啟用它以繼續本指南。
啟用此環境後,安裝的任何Python套件都將被包含在此目錄層次結構中。 它們不會干擾系統的Python環境。 有了這個想法,我們現在可以使用pip
將uWSGI伺服器安裝到我們的環境中。 此封包稱為uwsgi
(這仍然是uWSGI伺服器,而不是uwsgi
協議):
pip install uwsgi
您可以通過輸入以下命令來驗證其是否可用:
uwsgi --version
如果返回一個版本號,則uWSGI伺服器可供使用。
創建一個WSGI應用程式
接下來,我們將使用我們之前討論過的WSGI規範要求來創建一個極其簡單的WSGI應用程式。 重申一下,我們必須提供的應用程式組件應具有以下特性:
- 它必須通過可調用的接口(可調用的函數或其他語言結構)提供。
- 可調用物件必須以字典形式接收類似於環境變數的鍵值對和可在伺服器(uWSGI)上訪問的可調用物件作為參數。
- 應用程式的可調用物件應返回一個可迭代對象,該對象將產生要發送給客戶端的內容。
- 應用程式應調用Web伺服器的可調用物件,並提供HTTP狀態和請求標頭。
我們將在我們的應用程式目錄中的一個名為wsgi.py
的文件中編寫我們的應用程式:
nano ~/myapp/wsgi.py
在這個文件中,我們將創建我們能夠的最簡單的 WSGI 兼容應用程序。與所有 Python 代碼一樣,請注意縮進:
以上代碼構成了一個完整的 WSGI 應用程序。默認情況下,uWSGI 將尋找一個名為application
的可調用函數,這就是為什麼我們將我們的函數稱為application
的原因。如您所見,它接受兩個參數。
我們將第一個參數稱為environ
,因為它將是一個類似於環境變量的鍵-值字典。第二個被稱為start_response
,並且是應用程序內部用於引用發送的 Web 服務器(uWSGI)可調用的名稱。這兩個參數名稱僅僅是因為它們在定義了 WSGI 交互的PEP 333規範中的示例中的用法而被選擇。
我們的應用程序必須利用此信息並執行兩個操作。首先,它必須使用 HTTP 狀態碼和要發送的任何標頭來調用收到的可調用函數。在這種情況下,我們發送了一個“200 OK”響應並將Content-Type
標頭設置為text/html
。
其次,它需要返回一個可迭代對象作為響應主體。在這裡,我們只是使用包含一個 HTML 字符串的列表。字符串也是可迭代的,但在列表內,uWSGI 將能夠使用一次迭代處理整個字符串。
在現實世界的情況下,這個文件可能會被用作連接到應用程序代碼的鏈接。例如,Django項目默認包含一個wsgi.py
文件,該文件將來自Web服務器(uWSGI)的請求轉換為應用程序(Django)。簡化的WSGI接口保持不變,無論實際應用程序代碼有多復雜。這是接口的一個優勢。
完成後保存並關閉文件。
要測試代碼,我們可以啟動uWSGI。暫時我們將告訴它使用HTTP,並在端口8080
上進行監聽。我們將向它傳遞腳本的名稱(後綴已刪除):
uwsgi --socket 0.0.0.0:8080 --protocol=http -w wsgi
現在,如果您在Web瀏覽器中訪問您的服務器IP地址或域名,然後加上:8080
,您應該看到我們在wsgi.py
文件中作為正文傳遞的一級標題文本:
當您驗證這一點時,使用CTRL-C停止服務器。
在這一點上,我們已經完成了設計我們實際的應用程序。如果您希望,您可以停用我們的虛擬環境:
deactivate
配置uWSGI配置文件
在上面的示例中,我們手動啟動了uWSGI服務器並在命令行上傳遞了一些參數。我們可以通過創建一個配置文件來避免這種情況。uWSGI服務器可以讀取各種格式的配置,但為了簡單起見,我們將使用.ini
格式。
為了繼續使用我們迄今所使用的命名,我們將稱這個文件為myapp.ini
,並將其放置在我們的應用程序文件夾中:
nano ~/myapp/myapp.ini
在其中,我們需要建立一個名為[uwsgi]
的部分。這個部分是我們所有配置項目的所在之處。我們將從識別我們的應用程序開始。uWSGI伺服器需要知道應用程序的可調用位置。我們可以給出文件和其中的函數:
[uwsgi]
module = wsgi:application
我們希望將初始的uwsgi
進程標記為主進程,然後再生成一些工作進程。我們將從五個工作進程開始:
[uwsgi]
module = wsgi:application
master = true
processes = 5
我們實際上將更改uWSGI與外界通信的協議。當我們測試應用程序時,我們指定了--protocol=http
,以便我們可以從網絡瀏覽器中查看它。由於我們將在uWSGI前面配置Nginx作為反向代理,因此我們可以更改這個設定。Nginx實現了一種uwsgi
代理機制,這是一種快速的二進制協議,uWSGI可以使用它與其他服務器通信。實際上,uwsgi
協議是uWSGI的默認協議,因此僅需省略協議規範,它將回退到uwsgi
。
由於我們設計這個配置是為了在Nginx中使用,我們還將改用Unix套接字而不是使用網絡端口。這樣更安全更快。如果使用相對路徑,套接字將在當前目錄中創建。我們將其命名為myapp.sock
。我們將更改權限為“664”,以便Nginx可以寫入它(我們將使用Nginx使用的www-data
組啟動uWSGI)。我們還將添加vacuum
選項,這將在進程停止時刪除套接字:
[uwsgi]
module = wsgi:application
master = true
processes = 5
socket = myapp.sock
chmod-socket = 664
vacuum = true
由於我們將創建一個Upstart文件來在啟動時啟動應用程序,我們需要最後一個選項。Upstart和uWSGI對於SIGTERM信號應該如何影響應用程序有不同的想法。為了解決這個不一致,以便進程可以按照預期與Upstart一樣處理,我們只需要添加一個名為die-on-term
的選項,這樣uWSGI就會殺死進程而不是重新加載它:
[uwsgi]
module = wsgi:application
master = true
processes = 5
socket = myapp.sock
chmod-socket = 664
vacuum = true
die-on-term = true
完成後保存並關閉文件。現在,此配置文件已設置為與Upstart腳本一起使用。
創建一個Upstart文件來管理應用程序
我們可以在啟動時啟動一個uWSGI實例,以便我們的應用程序始終可用。我們將把它放在Upstart檢查的/etc/init
目錄中。我們將其命名為myapp.conf
:
sudo nano /etc/init/myapp.conf
首先,我們可以開始描述服務並選擇系統運行級別,該服務應該在其中自動運行。標準用戶運行級別為2到5。我們將告訴 Upstart 在此組以外的任何運行級別上停止服務(例如當系統正在重新啟動或處於單用戶模式時):
description "uWSGI instance to serve myapp"
start on runlevel [2345]
stop on runlevel [!2345]
接下來,我們將告訴 Upstart 要運行進程的用戶和組。我們想要在我們自己的帳戶下運行應用程序(在本指南中我們使用demo
,但您應該替換為您自己的用戶)。但是,我們希望將組設置為 Nginx 使用的www-data
用戶。這是必要的,因為 Web 服務器需要能夠讀取並寫入我們的 .ini
文件將創建的套接字:
description "uWSGI instance to serve myapp"
start on runlevel [2345]
stop on runlevel [!2345]
setuid demo
setgid www-data
接下來,我們將運行實際的命令來啟動 uWSGI。由於我們將 uWSGI 安裝到虛擬環境中,我們有一些額外的工作要做。我們可以只提供 uWSGI 可執行文件的完整路徑,但相反,我們將激活虛擬環境。如果我們依賴於在環境中安裝的其他軟件,這將變得更加容易。
為此,我們將使用一個script
塊。在其中,我們將切換到我們的應用程序目錄,激活虛擬環境(在腳本中,我們必須使用.
而不是source
),並啟動指向我們的.ini
文件的 uWSGI 實例:
description "uWSGI instance to serve myapp"
start on runlevel [2345]
stop on runlevel [!2345]
setuid demo
setgid www-data
script
cd /home/demo/myapp
. myappenv/bin/activate
uwsgi --ini myapp.ini
end script
有了這個,我們的 Upstart 腳本就完成了。完成後保存並關閉文件。
現在,我們可以通過鍵入以下命令來啟動服務:
sudo start myapp
我們可以通過輸入以下命令來驗證它是否已啟動:
ps aux | grep myapp
demo 14618 0.0 0.5 35868 5996 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14619 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14620 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14621 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14622 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14623 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 15520 0.0 0.0 11740 936 pts/0 S+ 15:53 0:00 grep --color=auto myapp
這將在開機時自動啟動。您可以隨時停止服務,方法是輸入:
sudo stop myapp
配置Nginx代理到uWSGI
到目前為止,我們有一個WSGI應用程序,並已驗證uWSGI可以讀取並提供它。我們已經創建了一個配置文件和一個Upstart腳本。我們的uWSGI進程將聽取一個套接字並使用uwsgi
協議通信。
我們現在已經到了配置Nginx作為反向代理的階段。Nginx有能力使用uwsgi
協議與uWSGI通信進行代理。這是一種比HTTP更快且性能更好的協議。
我們將設置的Nginx配置非常簡單。在Nginx的配置層級中的myapp
以匹配我們一直在使用的應用程序名:
sudo nano /etc/nginx/sites-available/myapp
在此文件中,我們可以指定該服務器塊應該回應的端口號和域名。在我們的情況下,我們將使用默認端口80:
server {
listen 80;
server_name server_domain_or_IP;
}
由於我們希望將此域名或 IP 地址上的所有請求發送到我們的 WSGI 應用程序,我們將創建一個單獨的位置區塊,用於以/
開頭的所有請求,這應該匹配所有內容。 在內部,我們將使用include
指令從我們的 Nginx 配置目錄中的文件中包含一些帶有合理默認值的參數。 包含這些參數的文件名為uwsgi_params
。 之後,我們將通過uwsgi
協議將流量傳遞給我們的 uWSGI 實例。 我們將使用之前配置的 Unix 套接字:
server {
listen 80;
server_name server_domain_or_IP;
location / {
include uwsgi_params;
uwsgi_pass unix:/home/demo/myapp/myapp.sock;
}
}
這實際上是我們一個簡單應用所需的全部。 對於更完整的應用,可以進行一些改進。 例如,我們可以在此區塊之外定義多個上游 uWSGI 服務器,然後將它們傳遞給該服務器。 我們可能還會包含一些更多的 uWSGI 參數。 我們也可以直接從 Nginx 處理任何靜態文件,並僅將動態請求傳遞給 uWSGI 實例。
我們在我們的三行應用程序中不需要這些功能,因此我們可以保存並關閉文件。
通過將其鏈接到sites-enabled
目錄來啟用我們剛剛創建的服務器配置:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled
檢查語法錯誤的配置文件:
sudo service nginx configtest
如果報告沒有檢測到問題,請重新啟動服務器以實施更改:
sudo service nginx restart
Nginx重新啟動後,您應該能夠訪問您的服務器的域名或 IP 地址(不帶端口號),並看到您配置的應用程序:
結論
如果您已經走到了這一步,那麼您已經創建了一個簡單的WSGI應用程序,並且對於設計更複雜應用程序有了一些見解。我們已經將uWSGI應用程序容器/伺服器安裝到了一個特定的虛擬環境中,以提供我們的應用程序。我們創建了一個配置文件和一個Upstart腳本來自動化此過程。在uWSGI伺服器之前,我們設置了一個Nginx反向代理,它可以使用uwsgi
線協議與uWSGI進程通信。
您可以輕鬆看出在設置實際生產環境時如何擴展此功能。例如,uWSGI具有使用稱為“emperor模式”的功能來管理多個應用程序。您可以擴展Nginx配置以在uWSGI實例之間進行負載平衡,或者處理應用程序的靜態文件。在提供多個應用程序時,根據您的需求,在全局安裝uWSGI可能比在虛擬環境中安裝更為合適。所有組件都相當靈活,因此您應該能夠調整其配置以滿足許多不同的情況。