我職業生涯的大部分都在使用微軟的架構,最近我決定走出技術的舒適區,步入開源軟體世界。我現在日常工作的專案是一個RESTful服務,這個服務需要在主流硬體上執行,且能夠按照需要進行水平擴充。為完成這項工作我決定使用Flask和Nginx。Flask是一個輕量級的Python Web框架,Nginx是一個非常穩定的Web伺服器,它們在廉價硬體平臺上工作良好。
在這篇文章中我將指導你完成使用Nginx伺服器託管Flask應用的安裝、配置過程。我所使用的作業系統是Ubuntu 13.04。
前提條件
在我們開始安裝Nginx及其他所需軟體之前先安裝一些前提軟體。首先,我們需要PIP與virtualenv:
1 2 3 |
sudo apt-get install python-setuptools sudo easy_install pip sudo pip install virtualenv |
使用apt-get安裝Nginx的話,我們需要新增Nginx庫到apt-get source中:
1 |
sudo add-apt-repository ppa:nginx/stable |
注意:如果“add-apt-repository”命令在你的Ubuntu版本中不存在的話,你需要安裝“software-properties-common”包,使用命令:sudo apt-get software-properties-common(感謝get_with_it在評論中提到)
升級已有的包,確保系統上有uWSGI所需的編譯器和工具:
1 |
sudo apt-get update |
Nginx
安裝並執行Nginx:
1 2 |
sudo apt-get install nginx sudo /etc/init.d/nginx start |
Nginx是一個提供靜態檔案訪問的web服務,然而,它不能直接執行託管Python應用程式,而uWSGI解決了這個問題。讓我們先安裝uWSGI,稍候再配置Nginx和uWSGI之間的互動。
1 |
sudo pip install uwsgi |
里程碑 #1
開啟瀏覽器訪問你的伺服器,你應該能看到Nginx歡迎頁:
示例應用
我們將託管的應用是經典的“Hello, world!”。這個應用只有一個頁面,已經猜到頁面上將有什麼內容了吧。將所有應用相關的檔案存放在/var/www/demoapp資料夾中。下面建立這個資料夾並在其中初始化一個虛擬環境:
1 2 |
sudo mkdir /var/www sudo mkdir /var/www/demoapp |
由於我們使用root許可權建立了這個資料夾,它目前歸root使用者所有,讓我們更改它的所有權給你登入的使用者(我的例子中是ubuntu)
1 |
sudo chown -R ubuntu:ubuntu /var/www/demoapp/ |
建立並啟用一個虛擬環境,在其中安裝Flask:
1 2 3 4 |
cd /var/www/demoapp virtualenv venv . venv/bin/activate pip install flask |
使用下面的程式碼建立hello.py檔案:
1 2 3 4 5 6 7 8 9 |
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!" if __name__ == "__main__": app.run(host='0.0.0.0', port=8080) |
里程碑 #2
讓我們執行我們剛建立的指令碼:
1 |
python hello.py |
現在你可以通過瀏覽器訪問你伺服器的8080埠,看,應用生效了:
注意:因為80埠已被Nginx使用,這裡我使用8080埠。
現在應用是由Flask內建的web服務託管的,對於開發和除錯這確實是個不錯的工具,但不推薦在生產環境中使用。讓我們配置Nginx來挑起這個重擔吧。
配置Nginx
首先刪除掉Nginx的預設配置檔案:
1 |
sudo rm /etc/nginx/sites-enabled/default |
注意:如果你安裝了其他版本的Nginx,預設配置檔案可能在/etc/nginx/conf.d資料夾下。
建立一個我們應用使用的新配置檔案/var/www/demoapp/demoapp_nginx.conf:
1 2 3 4 5 6 7 8 9 10 11 12 |
server { listen 80; server_name localhost; charset utf-8; client_max_body_size 75M; location / { try_files $uri @yourapplication; } location @yourapplication { include uwsgi_params; uwsgi_pass unix:/var/www/demoapp/demoapp_uwsgi.sock; } } |
將剛建立的配置檔案使用符號連結到Nginx配置檔案資料夾中,重啟Nginx:
1 2 |
sudo ln -s /var/www/demoapp/demoapp_nginx.conf /etc/nginx/conf.d/ sudo /etc/init.d/nginx restart |
里程碑 #3
訪問伺服器的公共ip地址,你會看到一個錯誤:
別擔心,這個錯誤是正常的,它代表Nginx已經使用了我們新建立的配置檔案,但在連結到我們的Python應用閘道器uWSGI時遇到了問題。到uWSGI的連結在Nginx配置檔案的第10行定義:
1 |
uwsgi_pass unix:/var/www/demoapp/demoapp_uwsgi.sock; |
這代表Nginx和uWSGI之間的連結是通過一個socket檔案,這個檔案位於/var/www/demoapp/demoapp_uwsgi.sock。因為我們還沒有配置uWSGI,所以這個檔案還不存在,因此Nginx返回“bad gateway”錯誤,讓我們馬上修正它吧。
配置uWSGI
建立一個新的uWSGI配置檔案/var/www/demoapp/demoapp_uwsgi.ini:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
[uwsgi] #application's base folder base = /var/www/demoapp #python module to import app = hello module = %(app) home = %(base)/venv pythonpath = %(base) #socket file's location socket = /var/www/demoapp/%n.sock #permissions for the socket file chmod-socket = 666 #the variable that holds a flask application inside the module imported at line #6 callable = app #location of log files logto = /var/log/uwsgi/%n.log |
建立一個新資料夾存放uWSGI日誌,更改資料夾的所有權:
1 2 |
sudo mkdir -p /var/log/uwsgi sudo chown -R ubuntu:ubuntu /var/log/uwsgi |
里程碑 #4
執行uWSGI,用新建立的配置檔案作為引數:
1 |
uwsgi --ini /var/www/demoapp/demoapp_uwsgi.ini |
接下來訪問你的伺服器,現在Nginx可以連線到uWSGI程式了:
我們現在基本完成了,唯一剩下的事情是配置uWSGI在後臺執行,這是uWSGI Emperor的職責。
uWSGI Emperor
uWSGI Emperor (很拉風的名字,是不?) 負責讀取配置檔案並且生成uWSGI程式來執行它們。建立一個初始配置來執行emperor – /etc/init/uwsgi.conf:
1 2 3 4 5 6 7 8 9 |
description "uWSGI" start on runlevel [2345] stop on runlevel [06] respawn env UWSGI=/usr/local/bin/uwsgi env LOGTO=/var/log/uwsgi/emperor.log exec $UWSGI --master --emperor /etc/uwsgi/vassals --die-on-term --uid www-data --gid www-data --logto $LOGTO |
最後一行執行uWSGI守護程式並讓它到/etc/uwsgi/vassals資料夾查詢配置檔案。建立這個資料夾,在其中建立一個到鏈到我們剛建立配置檔案的符號連結。
1 |
sudo mkdir /etc/uwsgi |
同時,最後一行說明用來執行守護程式的使用者是www-data。為簡單起見,將這個使用者設定成應用和日誌資料夾的所有者。
1 2 |
sudo chown -R www-data:www-data /var/www/demoapp/ sudo chown -R www-data:www-data /var/log/uwsgi/ |
注意:我們先前安裝的Nginx版本使用“www-data”這個使用者來執行Nginx,其他Nginx版本的可能使用“Nginx”這個替代使用者。
由於Nginx和uWSGI都由同一個使用者執行,我們可以在uWSGI配置中新增一個安全提升項。開啟uWSGI配置檔案,將chmod-socket值由666更改為644:
1 2 3 |
... #permissions for the socket file chmod-socket = 644 |
現在我們可以執行uWSGI了:
1 |
sudo start uwsgi |
最後,Nginx和uWSGI被配置成啟動後立即對外提供我們的應用服務。
問題解決
如果出現錯誤的話,第一個檢查的地方是日誌檔案。Nginx預設將錯誤資訊寫到/var/log/nginx/errors.log檔案。
我們已經配置了uWSGI emperor將日誌寫到/var/log/uwsgi/emperor.log。這個資料夾還包含著每個配置應用的單獨日誌。我們的例子是 – /var/log/uwsgi/demoapp_uwsgi.log。
靜態檔案
如果你的應用提供靜態檔案的話,將下面的規則新增到demoapp_nginx.conf檔案:
1 2 3 |
location /static { root /var/www/demoapp/; } |
上面配置的結果就是所有在/var/www/demoapp/static資料夾中的檔案將由提供Nginx對外服務(謝謝Bastianh指出)
託管多個應用
如果你想在一臺伺服器上託管多個Flask應用,為每個應用建立一個單獨的資料夾,像我們前面所做的一樣,建立Nginx及uWSGI配置檔案到應用資料夾的符號連結。
使用Distribute部署應用
使用distribute部署Flask應用的話,首先,按照Flask文件裡的步驟將應用轉化成package,然後複製distribute通用安裝包到伺服器上,使用虛擬環境中的Python來安裝它。如下:
1 |
python setup.py install |
最後且同樣重要的是,uwsgi配置裡應用屬性的值要設定成包含Flask應用的包的名稱。