寫(xiě)給新手看的Flask+uwsgi+Nginx+Ubuntu部署教程(轉(zhuǎn)載)

學(xué)習(xí) Flask,寫(xiě)完一個(gè) Flask 應(yīng)用需要部署的時(shí)候,就想著折騰自己的服務(wù)器。根據(jù)搜索的教程照做,對(duì)于原理一知半解,磕磕碰碰,只要運(yùn)行起來(lái)了,謝天謝地然后不再折騰了,到下一次還需要部署時(shí),這樣的過(guò)程就會(huì)重復(fù)一次。不知道多少人的膝蓋中箭了呢?我也這樣干過(guò),這么做確實(shí)很蠢,所以我決定寫(xiě)一篇 Flask+uwsgi+Nginx+Ubuntu 的部署教程,解答一些我自己在這個(gè)過(guò)程中的疑問(wèn),從原理到方案,以一個(gè)小白的角度,總結(jié)一下部署、運(yùn)維這件事,應(yīng)該對(duì)初學(xué) Flask 需要部署的同學(xué)有些幫助。

環(huán)境簡(jiǎn)介

Ubuntu

我使用的 Ubuntu 系統(tǒng)版本是 14.04,用過(guò)幾個(gè) Linux 發(fā)行版,現(xiàn)在挑選系統(tǒng)的第一選擇基本就是 Ubuntu 了,因?yàn)?Ubuntu 有商業(yè)公司 Canonical 做開(kāi)發(fā)維護(hù);使用的人多,有龐大的社區(qū)支持;遇到問(wèn)題容易解決。我折騰過(guò)很長(zhǎng)時(shí)間的 Linux 系統(tǒng),我對(duì)新手的建議是,不要把時(shí)間浪費(fèi)在這上面,應(yīng)該以解決實(shí)際問(wèn)題為導(dǎo)向,踏實(shí)點(diǎn)提高編程能力。裝系統(tǒng)、優(yōu)化系統(tǒng)、記各種酷炫的命令對(duì)于提高編程能力并沒(méi)有實(shí)際幫助。所以你問(wèn)我資瓷不資瓷 Ubuntu,我當(dāng)然是資瓷的啦,用 Ubuntu 當(dāng)然也會(huì)遇到坑,但相比于其他系統(tǒng)會(huì)少一些,也會(huì)容易解決一點(diǎn)。事實(shí)上,Ubuntu 已經(jīng)成為了服務(wù)器的首選,AWS 上被選擇最多的 Linux 發(fā)行版就是 Ubuntu。Quora 用的 Linux 發(fā)行版也是 Ubuntu,創(chuàng)始人 Adam D'Angelo 在這個(gè)回答里解釋了原因??偟膩?lái)說(shuō),沒(méi)有特別的理由的話(huà),Ubuntu 理應(yīng)是首選,經(jīng)驗(yàn)多一些之后,如果對(duì)某個(gè)發(fā)行版感興趣,或者想要做一些特別的嘗試,跳出舒適區(qū),試試其他系統(tǒng)也無(wú)妨。

uWSGI

我們知道 Flask 中自帶了 web server,通過(guò) Werkzeug,我們可以搭建 WSGI 服務(wù),運(yùn)行我們的網(wǎng)站,但 Flask 是 Web 框架,并不是 Web 服務(wù)器,盡管 Werkzeug 很強(qiáng)大,但只能用于開(kāi)發(fā),不能用于生產(chǎn),對(duì)于 Web 服務(wù)器,我們有更專(zhuān)業(yè)的選擇,那就是 uWSGI, uWSGI 是一個(gè)全站式的托管服務(wù),它實(shí)現(xiàn)了應(yīng)用服務(wù)器(支持多種編程語(yǔ)言)、代理、進(jìn)程管理器、監(jiān)視器。取名為 uWSGI 是因?yàn)樗钤鐚?shí)現(xiàn)的是 Python 語(yǔ)言的 WSGI。

uWSGI 包括四個(gè)部分:

  • uwsgi協(xié)議
  • web server 內(nèi)置支持協(xié)議模塊
  • application 服務(wù)器協(xié)議支持模塊
  • 進(jìn)程控制程序

uWSGI 是 C 語(yǔ)言寫(xiě)的,性能比較高。

推薦閱讀

WSGI, uWSGI, uwsgi 的區(qū)別

當(dāng)我們部署完一個(gè)應(yīng)用程序,瀏覽網(wǎng)頁(yè)時(shí)具體的過(guò)程是怎樣的呢?首先我們得有一個(gè) Web 服務(wù)器來(lái)處理 HTTP 協(xié)議的內(nèi)容,Web 服務(wù)器獲得客戶(hù)端的請(qǐng)求,交給應(yīng)用程序,應(yīng)用程序處理完,返回給 Web 服務(wù)器,這時(shí) Web 服務(wù)器再返回給客戶(hù)端。Web 服務(wù)器與應(yīng)用程序之間顯然要進(jìn)行交互,這時(shí)就出現(xiàn)了很多 Web 服務(wù)器與應(yīng)用程序之間交互的規(guī)范,最早出現(xiàn)的是 CGI,后來(lái)又出現(xiàn)了改進(jìn) CGI 性能的FasgCGI,Java 專(zhuān)用的 Servlet 規(guī)范,Python 專(zhuān)用的 WSGI 規(guī)范等等。有了統(tǒng)一標(biāo)準(zhǔn),程序的可移植性就大大提高了。這里我們只介紹 WSGI。

WSGI 全稱(chēng)是 Web Server Gateway Interface,也就是 Web 服務(wù)器網(wǎng)關(guān)接口,它是 Python 語(yǔ)言定義出來(lái)的 Web 服務(wù)器和 Web 應(yīng)用程序之間的簡(jiǎn)單而通用的接口,基于現(xiàn)存的 CGI 標(biāo)準(zhǔn)設(shè)計(jì),后來(lái)在很多其他語(yǔ)言中也出現(xiàn)了類(lèi)似的接口。 總的來(lái)說(shuō),WSGI 可以分為服務(wù)器和應(yīng)用程序兩個(gè)部分,實(shí)際上可以將 WSGI 理解為服務(wù)器與應(yīng)用程序之間的一座橋,橋的一邊是服務(wù)器,另一邊是應(yīng)用程序。

按照 web 組件分類(lèi),WSGI 內(nèi)部可以分為三類(lèi),web 應(yīng)用程序,web 服務(wù)器,web 中間件。應(yīng)用程序端的部分通過(guò)Python 語(yǔ)言的各種 Web 框架實(shí)現(xiàn),比如 Flask,Django這些,有了框架,開(kāi)發(fā)者就不需要處理 WSGI,框架會(huì)幫忙解決這些,開(kāi)發(fā)者只需處理 HTTP 請(qǐng)求和響應(yīng),web 服務(wù)器的部分就要復(fù)雜一點(diǎn),可以通過(guò) uWSGI 實(shí)現(xiàn),也可以用最常見(jiàn)的 Web 服務(wù)器,比如 Apache、Nginx,但這些 Web 服務(wù)器沒(méi)有內(nèi)置 WSGI 的實(shí)現(xiàn),是通過(guò)擴(kuò)展完成的。如 Apache,通過(guò)擴(kuò)展模塊 mod_wsgi 來(lái)支持WSGI,Nginx可以通過(guò)代理的方式,將請(qǐng)求封裝好,交給應(yīng)用服務(wù)器,比如 uWSGI。uWSGI 可以完成 WSGI 的服務(wù)端,進(jìn)程管理以及對(duì)應(yīng)用的調(diào)用。WSGI 中間件的部分可以這樣理解:我們把 WSGI 看做橋,這個(gè)橋有兩個(gè)橋墩,一個(gè)是應(yīng)用程序端,另一個(gè)是服務(wù)器端,那么橋面就是 WSGI 中間件,中間件同時(shí)具備服務(wù)器、應(yīng)用程序端兩個(gè)角色,當(dāng)然也需要同時(shí)遵守 WSGI 服務(wù)器和 WSGI 應(yīng)用程序兩邊的限制和需要。更詳細(xì)的內(nèi)容可以看PEP-333 中間件的描述

Flask 依賴(lài)的 Werkzeug 就是一個(gè) WSGI 工具包,官方文檔的定義是 Werkzeug 是為 Python 設(shè)計(jì)的 HTTP和 WSGI 實(shí)用程序庫(kù)。我們需要注意的是,F(xiàn)lask 自帶的 Werkzeug 是用來(lái)開(kāi)發(fā)的,并不能用于生產(chǎn)環(huán)境,F(xiàn)lask 是 Web 框架,而 Werkzeug 不是 Web框架,不是 Web 服務(wù)器,它只是一個(gè) WSGI 工具包,它在 Flask 的作用是作為 Web 框架的底層庫(kù),它方便了我們的開(kāi)發(fā)。

我們將 uwsgi 和 uWSGI 放在一起講解。uWSGI 是一個(gè) Web 服務(wù)器程序,WSGI,上面已經(jīng)談到,是一種協(xié)議,uwsgi 也是一種協(xié)議,uWSGI 實(shí)現(xiàn)了 uwsgi、WSGI、http 等協(xié)議。 uwsgi 的介紹可以看這里,uwsgi 是 uWSGI 使用的一個(gè)自有的協(xié)議,它用4個(gè)字節(jié)來(lái)定義傳輸數(shù)據(jù)類(lèi)型描述。盡管都是協(xié)議,uwsgi 和 WSGI 并沒(méi)有聯(lián)系,我們需要區(qū)分這兩個(gè)詞。

Nginx

Nginx 是高效的 Web 服務(wù)器和反向代理服務(wù)器,可以用作負(fù)載均衡(當(dāng)有 n 個(gè)用戶(hù)訪問(wèn)服務(wù)器時(shí),可以實(shí)現(xiàn)分流,分擔(dān)服務(wù)器的壓力),與 Apache 相比,Nginx 支持高并發(fā),可以支持百萬(wàn)級(jí)的 TCP 連接,十萬(wàn)級(jí)別的并發(fā)連接,部署簡(jiǎn)單,內(nèi)存消耗少,成本低,但 Nginx 的模塊沒(méi)有 Apache 豐富。Nginx 支持 uWSGI 的 uwsgi 協(xié)議,因此我們可以將 Nginx 與 uWSGI 結(jié)合起來(lái),Nginx 通過(guò) uwsgi_pass 將動(dòng)態(tài)內(nèi)容交給 uWSGI 處理。

官方文檔在

最好的 Nginx 教程在

uWSGI 和 Nginx 的關(guān)系

從上面的講解中,我們知道,uWSGI 可以起到 Web 服務(wù)器的作用,那么為什么有了 uWSGI 還需要 Nginx 呢?

最普遍的說(shuō)法是 Nginx 對(duì)于處理靜態(tài)文件更有優(yōu)勢(shì),性能更好。其實(shí)如果是小網(wǎng)站,沒(méi)有靜態(tài)文件需要處理,只用 uWSGI 也是可以的,但加上 Nginx 這一層,優(yōu)勢(shì)可以很具體:

  1. 對(duì)于運(yùn)維來(lái)說(shuō)比較方便,如果服務(wù)器被某個(gè) IP 攻擊,在 Nginx 配置文件黑名單中添加這個(gè) IP 即可,如果只用 uWSGI,那么就需要在代碼中修改了。另一方面,Nginx 是身經(jīng)百戰(zhàn)的 Web 服務(wù)器了,在表現(xiàn)上 uWSGI 顯得更專(zhuān)業(yè),比如說(shuō) uWSGI 在早期版本里是不支持 https 的,可以說(shuō) Nginx 更安全。

  2. Nginx 的特點(diǎn)是能夠做負(fù)載均衡和 HTTP 緩存,如果不止一臺(tái)服務(wù)器,Nginx 基本就是必選項(xiàng)了,通過(guò) Nginx,將資源可以分配給不同的服務(wù)器節(jié)點(diǎn),只有一臺(tái)服務(wù)器,也能很好地提高性能,因?yàn)?Nginx 可以通過(guò) headers 的Expires or E-Tag,gzip 壓縮等方式很好地處理靜態(tài)資源,畢竟是 C 語(yǔ)言寫(xiě)的,調(diào)用的是 native 的函數(shù),針對(duì) I/O做了優(yōu)化,對(duì)于動(dòng)態(tài)資源來(lái)說(shuō),Nginx 還可以實(shí)現(xiàn)緩存的功能,配合 CDN 優(yōu)化(這是 uWSGI 做不到的)。Nginx 支持epoll/kqueue 等高效網(wǎng)絡(luò)庫(kù),能夠很好地處理高并發(fā)短連接請(qǐng)求,性能比 uWSGI 不知道高到哪里去了。

  3. 如果服務(wù)器主機(jī)上運(yùn)行了PHP,Python 等語(yǔ)言寫(xiě)的多個(gè)應(yīng)用,都需要監(jiān)聽(tīng)80端口,這時(shí)候 Nginx 就是必選項(xiàng)了。因?yàn)槲覀冃枰粋€(gè)轉(zhuǎn)發(fā)的服務(wù)。

所以說(shuō),Nginx 基本也是必選項(xiàng)。

部署準(zhǔn)備工作

這里我假設(shè)我們拿到的是一臺(tái)全新的服務(wù)器。 一般來(lái)說(shuō),Linux 系統(tǒng)都會(huì)預(yù)裝 Python 的,但不一定裝了 easy_install 工具,我們可以通過(guò) apt-get install python-setuptools 來(lái)安裝 easy_install,再通過(guò) easy_install 安裝 pip。

搞定 Python 環(huán)境

$ sudo apt-get install python-setuptools
$ sudo easy_install pip

我們也可以直接裝 pip:

$ sudo apt-get install python-pip

這樣,我們就可以通過(guò) pip 安裝 virtualenv,為 flask 項(xiàng)目構(gòu)建虛擬環(huán)境。

$ sudo pip install virtualenv

Nginx

$ sudo apt-get install nginx

啟動(dòng) nginx 的方法:

$ sudo /etc/init.d/nginx start

這時(shí)候在瀏覽器地址欄輸入服務(wù)器的 ip 地址,看到下面的頁(yè)面就表明 Nginx 已經(jīng)啟動(dòng)了:

[圖片上傳失敗...(image-6b2532-1588232932086)]

安裝 uWSGI

在安裝 uWSGI 前,需要解決 uWSGI 的依賴(lài)問(wèn)題,因?yàn)?uWSGI 是一個(gè) C 語(yǔ)言寫(xiě)的應(yīng)用,所以我們需要 C 編譯器,以及 python 開(kāi)發(fā)相關(guān)組件:

$ sudo apt-get install build-essential python-dev
$ sudo pip install uwsgi

到這,我們就安裝好了 uWSGI,

開(kāi)干

首先,我們把應(yīng)用程序上傳到服務(wù)器中,我在用 git 管理項(xiàng)目,所以只需要 git clone 一下就可以了:

$ git clone http://url/of/you/git/repo

如果你需要從本地上傳項(xiàng)目文件,可以用 scp 命令,這里就不啰嗦用法了。總之我們將項(xiàng)目文件放到服務(wù)器,然后就可以用 virtualenv 管理 Python 環(huán)境:

$ virtualenv ENV
$ source ENV/bin/activate         # 激活虛擬環(huán)境
$ pip install -r requirement.txt  # 解決依賴(lài)問(wèn)題
$ deactivate                             # 退出依賴(lài)環(huán)境

這里就用 Flask 的7行代碼做示例吧,我新建了一個(gè)文件夾,名為 helloflask,將下面的內(nèi)容:

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=5001)

保存為hello.py,運(yùn)行試試,在瀏覽器輸入服務(wù)器公網(wǎng) ip 地址,加端口號(hào)5001就可以看到結(jié)果。

好了,現(xiàn)在我們用 Nginx 來(lái)承擔(dān) Web 服務(wù)。

刪掉 Nginx 的默認(rèn)配置文件:

$ sudo rm /etc/nginx/sites-enabled/default

有心的話(huà),其實(shí)可以從 Nginx 默認(rèn)配置中了解一些配置參數(shù),當(dāng)然最靠譜的途徑還是看 Nginx 的文檔。這里只簡(jiǎn)單嘗試 Nginx,下面給出一個(gè)簡(jiǎn)單的配置:

server {
    listen 80;
    server_name your.website.url
    charset utf-8;
    client_max_body_size 75M;
    location / { try_files $uri @yourapplication; }
    location @yourapplication {
        include uwsgi_params;
        uwsgi_pass unix:/home/frank/Documents/helloflask/helloflask_uwsgi.sock;
    }
}

我們可以將上述內(nèi)容保存為 helloflask_nginx.conf,稍作解釋?zhuān)簊erver_name 可以是域名,也可以寫(xiě) ip 地址,uwsgi_pass 是表明 Nginx 與 uwsgi 的交流方式,我這里用的是 sock 文件,當(dāng)然你也可以用指定端口號(hào)的形式,具體可以看這里。將 Nginx 配置文件用軟鏈接鏈接到 Nginx 配置文件夾中:

sudo ln -s /home/frank/Documents/helloflask/helloflask_nginx.conf /etc/nginx/conf.d/

重啟 Nginx:

sudo /etc/init.d/nginx restart

這時(shí)刷新一下之前打開(kāi)的服務(wù)器公網(wǎng) ip(或綁定的域名),這時(shí)看到的就不是「Welcome to Nginx」,而是「502 Bad Way」,因?yàn)槲覀冞€沒(méi)有啟動(dòng) uWSGI,現(xiàn)在我們將下面的內(nèi)容保存為 helloflask_uwsgi.ini(用 xml 的格式也是可以的,具體可以看文檔):

#application's base folder
base = /home/frank/Documents/helloflask

#python module to import
app = hello
module = %(app)

home = %(base)/ENV
pythonpath = %(base)

#socket file's location
socket = /home/frank/Documents/helloflask/%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 = /home/frank/Documents/helloflask/%n.log

稍稍解釋一下,socket 指定的是與 nginx 進(jìn)行通信的端口文件。其他的參數(shù),如線(xiàn)程數(shù),處理器數(shù)等,可以查看文檔后進(jìn)行配置。上面的內(nèi)容都是可以通過(guò) uwsgi 命令的參數(shù)指定的,在命令行中敲入一行命令就可以了,為了「可持續(xù)發(fā)展」,當(dāng)然是用文件保存下來(lái)比較好。

通過(guò) uwsgi 命令,--ini 參數(shù):

$ uwsgi --ini helloflask_uwsgi.ini &

指定配置文件,后臺(tái)運(yùn)行 uwsgi, 這時(shí)再刷新一下之前打開(kāi)的頁(yè)面,就可以看到應(yīng)用正常運(yùn)行了。

我嘗試了在一臺(tái)服務(wù)器上運(yùn)行多個(gè)應(yīng)用,其實(shí)只需要改一下文件名,分別處理 uWSGI 和 Nginx 的配置文件即可(Nginx 的配置,可以寫(xiě)在同一個(gè)文件中,寫(xiě)兩個(gè) server 就行了)

常用命令

nginx 常用命令

啟動(dòng)命令:

$ sudo nginx

$ sudo /usr/sbin/nginx

停止 nginx

$ sudo nginx -s stop

平滑啟動(dòng) nginx

sudo nginx -s reload

所謂平滑啟動(dòng)就是在不停止 nginx 的情況下,重啟 nginx,重新加載配置文件,用新的工作進(jìn)程代替舊的工作進(jìn)程。

總結(jié)

曾經(jīng)玩過(guò) PHP,相比于 PHP 的幾乎一鍵式部署,Python 的部署確實(shí)要繁瑣很多,但 Python 的強(qiáng)大之處在于語(yǔ)言簡(jiǎn)潔優(yōu)雅,畢竟人生苦短,有得便有失,不過(guò)我相信這個(gè)繁瑣是暫時(shí)的。

最后給出的一個(gè)簡(jiǎn)單的示例,其實(shí)是不夠規(guī)范的,比如應(yīng)用文件應(yīng)該放在 /var/www/ 下,log 文件應(yīng)該放到系統(tǒng)的 log 文件夾下等等,這個(gè)只是簡(jiǎn)單示例,更多配置內(nèi)容,我們應(yīng)該通過(guò) uWSGI、Nginx 的文檔學(xué)習(xí)。

參考資料:

  1. Why do I need nginx when I have uWSGI
  2. Web開(kāi)發(fā)技術(shù)發(fā)展歷史
  3. uWSGI 文檔
  4. Web Server Gateway Interface
  5. PEP-3333
  6. digitalocean tutorials
  7. CGI wiki
  8. 啄木鳥(niǎo)社區(qū) wsgi 介紹

原文鏈接:https://www.cnblogs.com/knarfeh/p/5616515.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容