在 Ubuntu 上使用 Nginx 部署 Flask 應用
來自: http://python.jobbole.com/84286/
我職業生涯的大部分都在使用微軟的架構,最近我決定走出技術的舒適區,步入開源軟件世界。我現在日常工作的項目是一個RESTful服務,這個服務需要在主流硬件上運行,且能夠按照需要進行水平拓展。為完成這項工作我決定使用Flask和Nginx。Flask是一個輕量級的Python Web框架,Nginx是一個非常穩定的Web服務器,它們在廉價硬件平臺上工作良好。
在這篇文章中我將指導你完成使用Nginx服務器托管Flask應用的安裝、配置過程。我所使用的操作系統是Ubuntu 13.04。
前提條件
在我們開始安裝Nginx及其他所需軟件之前先安裝一些前提軟件。首先,我們需要PIP與virtualenv:
sudo apt-get install python-setuptools sudo easy_install pip sudo pip install virtualenv
sudoapt-getinstallpython-setuptools sudoeasy_installpip sudopipinstallvirtualenv
使用apt-get安裝Nginx的話,我們需要添加Nginx庫到apt-get source中:
sudo add-apt-repository ppa:nginx/stable
sudoadd-apt-repositoryppa:nginx/stable
注意:如果“add-apt-repository”命令在你的Ubuntu版本中不存在的話,你需要安裝“ software-properties-common ”包,使用命令:sudo apt-get software-properties-common (感謝get_with_it在評論中提到)
升級已有的包,確保系統上有uWSGI所需的編譯器和工具:
sudo apt-get update
sudoapt-getupdate
Nginx
安裝并運行Nginx:
sudo apt-get install nginx sudo /etc/init.d/nginx start
sudoapt-getinstallnginx sudo /etc/init.d/nginxstart
Nginx是一個提供靜態文件訪問的web服務,然而,它不能直接執行托管Python應用程序,而uWSGI解決了這個問題。讓我們先安裝uWSGI,稍候再配置Nginx和uWSGI之間的交互。
sudo pip install uwsgi
sudopipinstalluwsgi
里程碑 #1
打開瀏覽器訪問你的服務器,你應該能看到Nginx歡迎頁:
示例應用
我們將托管的應用是經典的“Hello, world!”。這個應用只有一個頁面,已經猜到頁面上將有什么內容了吧。將所有應用相關的文件存放在/var/www/demoapp文件夾中。下面創建這個文件夾并在其中初始化一個虛擬環境:
sudo mkdir /var/www sudo mkdir /var/www/demoapp
sudomkdir /var/www sudomkdir /var/www/demoapp
由于我們使用root權限創建了這個文件夾,它目前歸root用戶所有,讓我們更改它的所有權給你登錄的用戶(我的例子中是ubuntu)
sudo chown -R ubuntu:ubuntu /var/www/demoapp/
sudochown -R ubuntu:ubuntu /var/www/demoapp/
創建并激活一個虛擬環境,在其中安裝Flask:
cd /var/www/demoapp virtualenv venv . venv/bin/activate pip install flask
cd /var/www/demoapp virtualenvvenv . venv/bin/activate pipinstallflask
使用下面的代碼創建hello.py文件:
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)
fromflaskimportFlask app = Flask(__name__) @app.route("/") defhello(): return "Hello World!" if __name__ == "__main__": app.run(host='0.0.0.0', port=8080)
里程碑 #2
讓我們執行我們剛創建的腳本:
python hello.py
pythonhello.py
現在你可以通過瀏覽器訪問你服務器的8080端口,看,應用生效了:
注意:因為80端口已被Nginx使用,這里我使用8080端口。
現在應用是由Flask內置的web服務托管的,對于開發和調試這確實是個不錯的工具,但不推薦在生產環境中使用。讓我們配置Nginx來挑起這個重擔吧。
配置Nginx
首先刪除掉Nginx的默認配置文件:
sudo rm /etc/nginx/sites-enabled/default
sudorm /etc/nginx/sites-enabled/default
注意:如果你安裝了其他版本的Nginx,默認配置文件可能在/etc/nginx/conf.d文件夾下 。
創建一個我們應用使用的新配置文件 /var/www/demoapp/demoapp_nginx.conf :
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; } }
server { listen 80; server_namelocalhost; charset utf-8; client_max_body_size 75M; location / { try_files $uri @yourapplication; } location @yourapplication { includeuwsgi_params; uwsgi_passunix:/var/www/demoapp/demoapp_uwsgi.sock; } }
將剛建立的配置文件使用符號鏈接到Nginx配置文件文件夾中,重啟Nginx:
sudo ln -s /var/www/demoapp/demoapp_nginx.conf /etc/nginx/conf.d/ sudo /etc/init.d/nginx restart
sudoln -s /var/www/demoapp/demoapp_nginx.conf /etc/nginx/conf.d/ sudo /etc/init.d/nginxrestart
里程碑 #3
訪問服務器的公共ip地址,你會看到一個錯誤:
別擔心,這個錯誤是正常的,它代表Nginx已經使用了我們新創建的配置文件,但在鏈接到我們的Python應用網關uWSGI時遇到了問題。到uWSGI的鏈接在Nginx配置文件的第10行定義:
uwsgi_pass unix:/var/www/demoapp/demoapp_uwsgi.sock;
uwsgi_passunix:/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 :
[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] #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日志,更改文件夾的所有權:
sudo mkdir -p /var/log/uwsgi sudo chown -R ubuntu:ubuntu /var/log/uwsgi
sudomkdir -p /var/log/uwsgi sudochown -R ubuntu:ubuntu /var/log/uwsgi
里程碑 #4
執行uWSGI,用新創建的配置文件作為參數:
uwsgi --ini /var/www/demoapp/demoapp_uwsgi.ini
uwsgi --ini /var/www/demoapp/demoapp_uwsgi.ini
接下來訪問你的服務器,現在Nginx可以連接到uWSGI進程了:
我們現在基本完成了,唯一剩下的事情是配置uWSGI在后臺運行,這是uWSGI Emperor的職責。
uWSGI Emperor
uWSGI Emperor (很拉風的名字,是不?) 負責讀取配置文件并且生成uWSGI進程來執行它們。創建一個初始配置來運行emperor - /etc/init/uwsgi.conf :
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
description "uWSGI" startonrunlevel [2345] stoponrunlevel [06] respawn envUWSGI=/usr/local/bin/uwsgi envLOGTO=/var/log/uwsgi/emperor.log exec $UWSGI --master --emperor /etc/uwsgi/vassals --die-on-term --uidwww-data --gidwww-data --logto $LOGTO
最后一行運行uWSGI守護進程并讓它到 /etc/uwsgi/vassals 文件夾查找配置文件。創建這個文件夾,在其中建立一個到鏈到我們剛創建配置文件的符號鏈接。
sudo mkdir /etc/uwsgi
sudomkdir /etc/uwsgi
同時,最后一行說明用來運行守護進程的用戶是www-data。為簡單起見,將這個用戶設置成應用和日志文件夾的所有者。
sudo chown -R www-data:www-data /var/www/demoapp/ sudo chown -R www-data:www-data /var/log/uwsgi/
sudochown -R www-data:www-data /var/www/demoapp/ sudochown -R www-data:www-data /var/log/uwsgi/
注意:我們先前安裝的Nginx版本使用“www-data”這個用戶來運行Nginx,其他Nginx版本的可能使用“Nginx ”這個替代用戶 。
由于Nginx和uWSGI都由同一個用戶運行,我們可以在uWSGI配置中添加一個安全提升項。打開uWSGI配置文件,將chmod-socket值由 666 更改為 644 :
... #permissions for the socket file chmod-socket = 644
... #permissions for the socket file chmod-socket = 644
現在我們可以運行uWSGI了:
sudo start uwsgi
sudostartuwsgi
最后,Nginx和uWSGI被配置成啟動后立即對外提供我們的應用服務。
問題解決
如果出現錯誤的話,第一個檢查的地方是日志文件。Nginx默認將錯誤信息寫到 /var/log/nginx/errors.log 文件。
我們已經配置了uWSGI emperor將日志寫到 /var/log/uwsgi/emperor.log 。這個文件夾還包含著每個配置應用的單獨日志。我們的例子是 - /var/log/uwsgi/demoapp_uwsgi.log 。
靜態文件
如果你的應用提供靜態文件的話,將下面的規則添加到 demoapp_nginx.conf 文件:
location /static { root /var/www/demoapp/; }
location /static { root /var/www/demoapp/; }
上面配置的結果就是所有在 /var/www/demoapp/static 文件夾中的文件將由 提供 Nginx對外服務 (謝謝Bastianh指出)
托管多個應用
如果你想在一臺服務器上托管多個Flask應用,為每個應用創建一個單獨的文件夾,像我們前面所做的一樣,創建Nginx及uWSGI配置文件到應用文件夾的符號鏈接。
使用Distribute部署應用
使用 distribute 部署Flask應用的話,首先,按照 Flask文檔 里的步驟將應用轉化成package,然后復制distribute通用安裝包到服務器上,使用虛擬環境中的Python來安裝它。如下:
python setup.py install
pythonsetup.pyinstall
最后且同樣重要的是,uwsgi配置里應用屬性的值要設置成包含Flask應用的包的名稱。
</div>