學習 Flask,寫完一個 Flask 應用須要部署的時候,就想着折騰本身的服務器。根據搜索的教程照作,對於原理只知其一;不知其二,磕磕碰碰,只要運行起來了,謝天謝地而後再也不折騰了,到下一次還須要部署時,這樣的過程就會重複一次。不知道多少人的膝蓋中箭了呢?我也這樣幹過,這麼作確實很蠢,因此我決定寫一篇 Flask+uwsgi+Nginx+Ubuntu 的部署教程,解答一些我本身在這個過程當中的疑問,從原理到方案,以一個小白的角度,總結一下部署、運維這件事,應該對初學 Flask 須要部署的同窗有些幫助。html
我使用的 Ubuntu 系統版本是 14.04,用過幾個 Linux 發行版,如今挑選系統的第一選擇基本就是 Ubuntu 了,由於 Ubuntu 有商業公司 Canonical 作開發維護;使用的人多,有龐大的社區支持;遇到問題容易解決。我折騰過很長時間的 Linux 系統,我對新手的建議是,不要把時間浪費在這上面,應該以解決實際問題爲導向,踏實點提升編程能力。裝系統、優化系統、記各類酷炫的命令對於提升編程能力並無實際幫助。因此你問我資瓷不資瓷 Ubuntu,我固然是資瓷的啦,用 Ubuntu 固然也會遇到坑,但相比於其餘系統會少一些,也會容易解決一點。事實上,Ubuntu 已經成爲了服務器的首選,AWS 上被選擇最多的 Linux 發行版就是 Ubuntu。Quora 用的 Linux 發行版也是 Ubuntu,創始人 Adam D'Angelo 在這個回答裏解釋了緣由。總的來講,沒有特別的理由的話,Ubuntu 理應是首選,經驗多一些以後,若是對某個發行版感興趣,或者想要作一些特別的嘗試,跳出溫馨區,試試其餘系統也無妨。python
咱們知道 Flask 中自帶了 web server,經過 Werkzeug,咱們能夠搭建 WSGI 服務,運行咱們的網站,但 Flask 是 Web 框架,並非 Web 服務器,儘管 Werkzeug 很強大,但只能用於開發,不能用於生產,對於 Web 服務器,咱們有更專業的選擇,那就是 uWSGI, uWSGI 是一個全站式的託管服務,它實現了應用服務器(支持多種編程語言)、代理、進程管理器、監視器。取名爲 uWSGI 是由於它最先實現的是 Python 語言的 WSGI。nginx
uWSGI 包括四個部分:git
uWSGI 是 C 語言寫的,性能比較高。github
推薦閱讀web
當咱們部署完一個應用程序,瀏覽網頁時具體的過程是怎樣的呢?首先咱們得有一個 Web 服務器來處理 HTTP 協議的內容,Web 服務器得到客戶端的請求,交給應用程序,應用程序處理完,返回給 Web 服務器,這時 Web 服務器再返回給客戶端。Web 服務器與應用程序之間顯然要進行交互,這時就出現了不少 Web 服務器與應用程序之間交互的規範,最先出現的是 CGI,後來又出現了改進 CGI 性能的FasgCGI,Java 專用的 Servlet 規範,Python 專用的 WSGI 規範等等。有了統一標準,程序的可移植性就大大提升了。這裏咱們只介紹 WSGI。sql
WSGI 全稱是 Web Server Gateway Interface,也就是 Web 服務器網關接口,它是 Python 語言定義出來的 Web 服務器和 Web 應用程序之間的簡單而通用的接口,基於現存的 CGI 標準設計,後來在不少其餘語言中也出現了相似的接口。 總的來講,WSGI 能夠分爲服務器和應用程序兩個部分,實際上能夠將 WSGI 理解爲服務器與應用程序之間的一座橋,橋的一邊是服務器,另外一邊是應用程序。shell
按照 web 組件分類,WSGI 內部能夠分爲三類,web 應用程序,web 服務器,web 中間件。應用程序端的部分經過Python 語言的各類 Web 框架實現,好比 Flask,Django這些,有了框架,開發者就不須要處理 WSGI,框架會幫忙解決這些,開發者只需處理 HTTP 請求和響應,web 服務器的部分就要複雜一點,能夠經過 uWSGI 實現,也能夠用最多見的 Web 服務器,好比 Apache、Nginx,但這些 Web 服務器沒有內置 WSGI 的實現,是經過擴展完成的。如 Apache,經過擴展模塊 mod_wsgi 來支持WSGI,Nginx能夠經過代理的方式,將請求封裝好,交給應用服務器,好比 uWSGI。uWSGI 能夠完成 WSGI 的服務端,進程管理以及對應用的調用。WSGI 中間件的部分能夠這樣理解:咱們把 WSGI 看作橋,這個橋有兩個橋墩,一個是應用程序端,另外一個是服務器端,那麼橋面就是 WSGI 中間件,中間件同時具有服務器、應用程序端兩個角色,固然也須要同時遵照 WSGI 服務器和 WSGI 應用程序兩邊的限制和須要。更詳細的內容能夠看PEP-333 中間件的描述編程
Flask 依賴的 Werkzeug 就是一個 WSGI 工具包,官方文檔的定義是 Werkzeug 是爲 Python 設計的 HTTP和 WSGI 實用程序庫。咱們須要注意的是,Flask 自帶的 Werkzeug 是用來開發的,並不能用於生產環境,Flask 是 Web 框架,而 Werkzeug 不是 Web框架,不是 Web 服務器,它只是一個 WSGI 工具包,它在 Flask 的做用是做爲 Web 框架的底層庫,它方便了咱們的開發。flask
咱們將 uwsgi 和 uWSGI 放在一塊兒講解。uWSGI 是一個 Web 服務器程序,WSGI,上面已經談到,是一種協議,uwsgi 也是一種協議,uWSGI 實現了 uwsgi、WSGI、http 等協議。 uwsgi 的介紹能夠看這裏,uwsgi 是 uWSGI 使用的一個自有的協議,它用4個字節來定義傳輸數據類型描述。儘管都是協議,uwsgi 和 WSGI 並無聯繫,咱們須要區分這兩個詞。
Nginx 是高效的 Web 服務器和反向代理服務器,能夠用做負載均衡(當有 n 個用戶訪問服務器時,能夠實現分流,分擔服務器的壓力),與 Apache 相比,Nginx 支持高併發,能夠支持百萬級的 TCP 鏈接,十萬級別的併發鏈接,部署簡單,內存消耗少,成本低,但 Nginx 的模塊沒有 Apache 豐富。Nginx 支持 uWSGI 的 uwsgi 協議,所以咱們能夠將 Nginx 與 uWSGI 結合起來,Nginx 經過 uwsgi_pass
將動態內容交給 uWSGI 處理。
官方文檔在這
最好的 Nginx 教程在這
從上面的講解中,咱們知道,uWSGI 能夠起到 Web 服務器的做用,那麼爲何有了 uWSGI 還須要 Nginx 呢?
最廣泛的說法是 Nginx 對於處理靜態文件更有優點,性能更好。其實若是是小網站,沒有靜態文件須要處理,只用 uWSGI 也是能夠的,但加上 Nginx 這一層,優點能夠很具體:
對於運維來講比較方便,若是服務器被某個 IP 攻擊,在 Nginx 配置文件黑名單中添加這個 IP 便可,若是隻用 uWSGI,那麼就須要在代碼中修改了。另外一方面,Nginx 是身經百戰的 Web 服務器了,在表現上 uWSGI 顯得更專業,好比說 uWSGI 在早期版本里是不支持 https 的,能夠說 Nginx 更安全。
Nginx 的特色是可以作負載均衡和 HTTP 緩存,若是不止一臺服務器,Nginx 基本就是必選項了,經過 Nginx,將資源能夠分配給不一樣的服務器節點,只有一臺服務器,也能很好地提升性能,由於 Nginx 能夠經過 headers 的Expires or E-Tag,gzip 壓縮等方式很好地處理靜態資源,畢竟是 C 語言寫的,調用的是 native 的函數,針對 I/O作了優化,對於動態資源來講,Nginx 還能夠實現緩存的功能,配合 CDN 優化(這是 uWSGI 作不到的)。Nginx 支持epoll/kqueue 等高效網絡庫,可以很好地處理高併發短鏈接請求,性能比 uWSGI 不知道高到哪裏去了。
若是服務器主機上運行了PHP,Python 等語言寫的多個應用,都須要監聽80端口,這時候 Nginx 就是必選項了。由於咱們須要一個轉發的服務。
因此說,Nginx 基本也是必選項。
這裏我假設咱們拿到的是一臺全新的服務器。 通常來講,Linux 系統都會預裝 Python 的,但不必定裝了 easy_install 工具,咱們能夠經過 apt-get install python-setuptools
來安裝 easy_install,再經過 easy_install 安裝 pip。
$ sudo apt-get install python-setuptools $ sudo easy_install pip
咱們也能夠直接裝 pip:
$ sudo apt-get install python-pip
這樣,咱們就能夠經過 pip 安裝 virtualenv,爲 flask 項目構建虛擬環境。
$ sudo pip install virtualenv
$ sudo apt-get install nginx
啓動 nginx 的方法:
$ sudo /etc/init.d/nginx start
這時候在瀏覽器地址欄輸入服務器的 ip 地址,看到下面的頁面就代表 Nginx 已經啓動了:
在安裝 uWSGI 前,須要解決 uWSGI 的依賴問題,由於 uWSGI 是一個 C 語言寫的應用,因此咱們須要 C 編譯器,以及 python 開發相關組件:
$ sudo apt-get install build-essential python-dev $ sudo pip install uwsgi
到這,咱們就安裝好了 uWSGI,
首先,咱們把應用程序上傳到服務器中,我在用 git 管理項目,因此只須要 git clone 一下就能夠了:
$ git clone http://url/of/you/git/repo
若是你須要從本地上傳項目文件,能夠用 scp 命令,這裏就不囉嗦用法了。總之咱們將項目文件放到服務器,而後就能夠用 virtualenv 管理 Python 環境:
$ virtualenv ENV
$ source ENV/bin/activate # 激活虛擬環境 $ pip install -r requirement.txt # 解決依賴問題 $ deactivate # 退出依賴環境
這裏就用 Flask 的7行代碼作示例吧,我新建了一個文件夾,名爲 helloflask,將下面的內容:
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,運行試試,在瀏覽器輸入服務器公網 ip 地址,加端口號5001就能夠看到結果。
好了,如今咱們用 Nginx 來承擔 Web 服務。
刪掉 Nginx 的默認配置文件:
$ sudo rm /etc/nginx/sites-enabled/default
有心的話,其實能夠從 Nginx 默認配置中瞭解一些配置參數,固然最靠譜的途徑仍是看 Nginx 的文檔。這裏只簡單嘗試 Nginx,下面給出一個簡單的配置:
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; } }
咱們能夠將上述內容保存爲 helloflask_nginx.conf,稍做解釋:server_name 能夠是域名,也能夠寫 ip 地址,uwsgi_pass 是代表 Nginx 與 uwsgi 的交流方式,我這裏用的是 sock 文件,固然你也能夠用指定端口號的形式,具體能夠看這裏。將 Nginx 配置文件用軟連接連接到 Nginx 配置文件夾中:
sudo ln -s /home/frank/Documents/helloflask/helloflask_nginx.conf /etc/nginx/conf.d/
重啓 Nginx:
sudo /etc/init.d/nginx restart
這時刷新一下以前打開的服務器公網 ip(或綁定的域名),這時看到的就不是「Welcome to Nginx」,而是「502 Bad Way」,由於咱們尚未啓動 uWSGI,如今咱們將下面的內容保存爲 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 進行通訊的端口文件。其餘的參數,如線程數,處理器數等,能夠查看文檔後進行配置。上面的內容都是能夠經過 uwsgi 命令的參數指定的,在命令行中敲入一行命令就能夠了,爲了「可持續發展」,固然是用文件保存下來比較好。
經過 uwsgi 命令,--ini 參數:
$ uwsgi --ini helloflask_uwsgi.ini &
指定配置文件,後臺運行 uwsgi, 這時再刷新一下以前打開的頁面,就能夠看到應用正常運行了。
我嘗試了在一臺服務器上運行多個應用,其實只須要改一下文件名,分別處理 uWSGI 和 Nginx 的配置文件便可(Nginx 的配置,能夠寫在同一個文件中,寫兩個 server 就好了)
啓動命令:
$ sudo nginx
或
$ sudo /usr/sbin/nginx
中止 nginx
$ sudo nginx -s stop
平滑啓動 nginx
sudo nginx -s reload
所謂平滑啓動就是在不中止 nginx 的狀況下,重啓 nginx,從新加載配置文件,用新的工做進程代替舊的工做進程。
曾經玩過 PHP,相比於 PHP 的幾乎一鍵式部署,Python 的部署確實要繁瑣不少,但 Python 的強大之處在於語言簡潔優雅,畢竟人生苦短,有得便有失,不過我相信這個繁瑣是暫時的。
最後給出的一個簡單的示例,實際上是不夠規範的,好比應用文件應該放在 /var/www/ 下,log 文件應該放到系統的 log 文件夾下等等,這個只是簡單示例,更多配置內容,咱們應該經過 uWSGI、Nginx 的文檔學習。