如何使用Nginx和uWSGI或Gunicorn在Ubuntu上部署Flask Web應用
我在很多的博客中都看過有關Flask
應用的部署,也有很多博主在開博後都記錄了部署的教程,因為其中的坑可以說不少。一開始我在網上看到相比較與Ubuntu
,CentOS
因為更新少作為服務器的操作系統會更加穩定。所以在第一次購買雲服務器時,我選擇了CentOS
,後來由於CentOS
不同發行版的Nginx
緣故,我又換成了Ubuntu
的鏡像
首先呢,我們先來了解下關於Web服務器與Web應用還有WSGI之間的聯系
一、介紹
WSGI
(Web Server Gateway Interface),翻譯為Python web
服務器網關接口,即Python
的Web
應用程序(如Flask
)和Web
服務器(如Nginx
Web
應用在任何服務器上運行,就必須遵循這個協議。
那麽實現WSGI
協議的web服務器有哪些呢?就比如uWSGI
與gunicorn
。兩者都可以作為Web服務器。可能你在許多地方看到的都是采用Nginx
+ uWSGI
(或gunicorn
)的部署方式。實際上,直接通過uWSGI
或gunicorn
直接部署也是可以讓外網訪問的,那你可能會說,那要Nginx
何用?別急,那麽接來下介紹另一個Web服務器——Nginx
Nginx
作為一個高性能Web服務器,具有負載均衡、攔截靜態請求、高並發...等等許多功能,你可能要問了,這些功能和使用Nginx
+ WSGI
容器的部署方式有什麽關系?
首先是負載均衡,如果你了解過OSI模型的話,其實負載均衡器就是該模型中4~7層交換機中的一種,它的作用是能夠僅通過一個前端唯一的URL訪問分發到後臺的多個服務器,這對於並發量非常大的企業級Web站點非常有效。在實際應用中我們通常會讓Nginx
監聽(綁定)80
端口,通過多域名或者多個location分發到不同的後端應用。
其次是攔截靜態請求,簡單來說,Nginx
會攔截到靜態請求(靜態文件,如圖片),並交給自己處理。而動態請求內容將會通過WSGI
容器交給Web
應用處理;
Nginx
還有其他很多的功能,這裏便不一一介紹。那麽前面說了,直接通過uWSGI
或gunicorn
也可以讓外網訪問到的,但是鑒於Nginx
Nginx
是很有必要的;
這裏可以說明,如果你選擇的架構是:Nginx + WSGI容器 + web應用,WSGI容器相當於一個中間件;如果選擇的架構是uWSGI + web應用,WSGI容器則為一個web服務器
二、實際部署:
該篇部署的教程是在你已經購買好虛擬主機,並且已經搭建好開發環境的前提下進行的,如果你還沒有搭建好開發環境,可以參考我寫的文檔:
阿裏雲Ubuntu雲服務器上搭建Python和Flask的開發環境
普遍的部署方式都是通過讓Nginx
綁定80
端口,並接受客戶端的請求將動態內容的請求反向代理給運行在本地端口的uWSGI
或者Gunicorn
,所以既可以通過Nginx
+ uWSGI
也可以通過Nginx
+ Gunicorn
來部署Flask
應用,這篇教程中都將一一介紹這兩種方法
當然采用不同的WSGI
容器,Nginx
中的配置也會有所不同
1. Nginx + uWSGI:
1.1 配置uWSGI:
我們現在虛擬環境下安裝好uWSGI
:
(venv) $ pip install uwsgi
安裝完成之後我們在項目的目錄下(即你實際創建的Flask項目目錄,在本文所指的項目目錄都假設為/www/demo)創建以.ini
為擴展名的配置文件。在設置與Nginx
交互的時候有兩種方式:
第一種是通過配置網絡地址,第二種是通過本地的.socket
文件進行通信。需要註意的是,不同的交互方式下,Nginx
中的配置也會有所不同
如果采用的是第一種網絡地址的方式,則將之前創建uwsgi.ini
配置文件添加如下的配置內容:
[uwsgi]
socket = 127.0.0.1:8001 //與nginx通信的端口
chdir = /www/demo/ //你的Flask項目目錄
wsgi-file = run.py
callable = app //run.py文件中flask實例化的對象名
processes = 4 //處理器個數
threads = 2 //線程個數
stats = 127.0.0.1:9191 //獲取uwsgi統計信息的服務地址
這裏的wsgi-file
參數所指的run.py
其實是啟動文件,你也可以使用manage.py
。不過我通常習慣創建一個這樣的文件,可以直接運行該文件來啟動項目:
from app import app
if __name__ == ‘__main__‘:
app.run()
保存好配置文件後,就可以通過如下的命令來啟動應用了:
(venv) $ uwsgi uwsig.ini
如果你采用的是第二種本地socket
文件的方式,則添加如下的配置內容:
[uwsgi]
socket = /www/demo/socket/nginx_uwsgi.socket //與nginx通信的socket文件
chdir = /www/demo/
wsgi-file = run.py
callable = app
processes = 4
threads = 2
stats = 127.0.0.1:9191
可以看到,其實與網絡地址的配置方式只有socket
參數的配置不同,在這裏填寫好路徑名和文件名並啟動uWSGI
後,將會自動在改目錄下生成nginx_uwsgi.socket
文件,這個文件就是用來與Nginx
交互的。
1.2 配置Nginx
首先我們來通過apt
安裝Nginx
:
$ sudo apt-get install nginx
安裝完成之後,我們cd
到/etc/nginx/
的目錄下(可能由於不同系統導致不同的Nginx發行版緣故,目錄有所差別,在此只針對Ubuntu
中的發行版的Nginx),可以看到Nginx
的所有配置文件。
其中nginx.conf
文件為主配置文件,可以用來修改其全局配置;sites-available
存放你的配置文件,但是在這裏添加配置是不會應用到Nginx
的配置當中,需要軟連接到同目錄下的sites-enabled
當中。但是在我實際操作的過程中中,當我在sites-available
修改好配置文件後,會自動更新到sites-enabled
。如果沒有的話,則需要像上述的操作那樣,將修改好的配置文件軟鏈接到sites-enabled
當中
在上邊說到,配置uWSGI
有兩種與Nginx
交互的方式,那麽選擇不同的方式的話在Nginx
的配置也會有所不同:
第一種:網絡配置方式:
這裏的proxy_set_header
設置的三個參數的作用都是能夠直接獲得到客戶端的IP
,如果你感興趣可以參考:Nginx中proxy_set_header 理解
用include uwsgi_params
導入uWSGI
所引用的參數,通過uwsgi_pass
反向代理給在localhost:8001
運行的uWSGI
:
server {
listen 80; # 監聽的端口號
root /www/demo; #Flask的項目目錄
server_name xxx.xx.xxx.xxx; # 你的公網ip或者域名
location / {
proxy_set_header x-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
include uwsgi_params;
uwsgi_pass localhost:8001;
}
# 配置static的靜態文件:
location ~ ^\/static\/.*$ {
root /www/demo; # 註意!這裏不需要再加/static了
}
}
在每次完Nginx配置文件內容後,需要通過如下的命令來重啟Nginx:
$ nginx -s reload
第二種:socket文件方式:
與上邊的配置內容大體相同,只是在配置uwsgi_pass
不是反向代理給網絡地址,而是通過socket
文件進行交互,我們只需要指定之前設置的路徑和文件名即可:
server {
listen 80;
root /www/demo;
server_name xxx.xx.xxx.xxx;
location / {
proxy_set_header x-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
include uwsgi_params;
uwsgi_pass unix:/www/demo/socket/nginx_uwsgi.socket;
}
location ~ ^\/static\/.*$ {
root /www/demo;
}
}
2. Nginx + Gunicorn
2.1 配置Gunicorn:
首先先在虛擬環境下安裝Gunicorn
:
(venv) $ pip install gunicorn
安裝完成後,我們來創建以.py
結尾的配置文件,這裏我參考了Jiyuankai的GitHub關於Gunicorn
的配置文件內容:
from gevent import monkey
monkey.patch_all()
import multiprocessing
debug = True
loglevel = ‘debug‘
bind = ‘127.0.0.1:5000‘ //綁定與Nginx通信的端口
pidfile = ‘log/gunicorn.pid‘
logfile = ‘log/debug.log‘
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = ‘gevent‘ //默認為阻塞模式,最好選擇gevent模式
需要註意的是要在配置文件的同層目錄下創建log
文件,否則運行gunicorn
將報錯。添加完配置內容並保存為gconfig.py
文件後,我們就也可以通過gunicorn
來運行Flask
應用了:
(venv)$ gunicorn -c /www/demo/gconfig.py run:app
2.2 配置Nginx:
和uWSGI
的任意一種配置方法類似,只是在location
中的配置有所不同:
server {
listen 80;
root /www/demo;
server_name xxx.xx.xxx.xxx;
location / {
proxy_set_header x-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://localhost:5000/; # gunicorn綁定的端口號
}
# 配置static的靜態文件:
location ~ ^\/static\/.*$ {
root /www/demo;
}
}
通過Gunicorn的Nginx配置中,我們只需要通過proxy_pass
參數反向代理給運行在http://localhost:5000/
上的Gunicorn
三、守護進程
如果你采取如上的任意一種部署方式,在Nginx與uWSGI或Gunicorn同時運行,並且配置無誤的狀態下,那麽你現在應該是可以通過你的公網ip
或者域名訪問到你的網站了。
但是還有一個問題,到目前為止,uWSGI和gunicorn都是直接通過命令行運行,並不能夠在後臺運行,也是當我們關閉了xShell(或者你使用的是Putty及其他SSH連接的軟件),將無法再訪問到你的應用。所以我們需要讓uWSGI或gunicorn在後臺運行,也就是所謂的daemon(守護進程)。
1. nohup:
如果你熟悉Linux命令,你應該知道在Linux中後臺運行可以通過nohup
命令,例如我們要讓gunicorn在後臺運行,我們只需要運行nohup
命令:
(venv) $ nohup gunicorn -c gconfig.py run:app &
運行後你可以通過ps -e | grep gunicorn
指令來查看到當前gunicorn的運行狀態:
如果你選擇的是uWSGI,同樣也可以通過nohup
命令來實現守護進程:
(venv) $ nohup uwsgi uwsgi.ini &
這樣你就可以關閉連接服務器的終端,還能通過你的瀏覽器訪問到你的Flask
應用了!
2. supervisor
但是nohup
運行的後臺程序並不能夠可靠的在後臺運行,我們最好讓我們的後臺程序能夠監控進程狀態,還能在意外結束時自動重啟,這就可以使用一個使用Python開發的進程管理程序supervisor。
首先我們通過apt
來安裝supervisor:
$ sudo apt-get install supervisor
安裝完成後,我們在/etc/supervisor/conf.d/
目錄下創建我們控制進程的配置文件,並以.conf結尾,這樣將會自動應用到主配置文件當中,創建後添加如下的配置內容:
[program:demo]
command=/www/demo/venv/bin/gunicorn -c /pushy/blog/gconfig.py run:app
directory=/www/demo //項目目錄
user=root
autorestart=true //設置自動重啟
startretires=3 //重啟失敗3次
在上面的配置文件中,[program:demo]
設置了進程名,這與之後操作進程的狀態名稱有關,為demo
;command
為進程運行的命令,必須使用絕對路徑,並且使用虛擬環境下的gunicorn
命令;user
指定了運行進程的用戶,這裏設置為root
保存配置文件之後,我們需要通過命令來更新配置文件:
$ supervisorctl update
命令行將顯示:demo: added process group
,然後我們來啟動這個demo
進程:
$ supervisorctl start demo
當然你也直接在命令行輸入supervisorctl
進入supevisor的客戶端,查看到當前的進程狀態:
demo RUNNING pid 17278, uptime 0:08:51
通過stop
命令便可以方便的停止該進程:
supervisor> stop demo
參考資料:
Linux後臺進程管理利器:supervisor
如何使用Nginx和uWSGI或Gunicorn在Ubuntu上部署Flask Web應用