AI畫家第五彈——從0到1部署你的RESTful API

上一篇文章中咱們已經利用Flask製做了一個RESTful API,然而文中末尾咱們咱們將代碼運行起來的時候確發現是localhost:xxxx,這也就意味着咱們無法經過外網訪問咱們提供的API,因此這樣作出來的程序即便部署到服務器上也沒有任何用處。這一篇咱們就來詳細的說下怎麼部署到服務器上而後實現外網訪問吧。python

環境準備

工欲善其事,必先利其器。仍是老樣子,既然要選擇部署到服務器上,咱們首先確定得要有一個服務器,這裏我選擇XX雲的學生機(別問爲何,問就是便宜)。系統選擇Ubuntu18.04,須要安裝的東西有:linux

  • Python 3.6
  • Nginx
  • pyenv
  • pipenv
  • Flask + Gunicorn

這裏咱們用pyenv管理不一樣的python版本,這裏可能就有人要問了,Ubuntu不是自帶python2和python3嗎,爲何還要用pyenv來管理,以前的文章咱們也說過,用pyenv管理python會很是的方便,方便到只須要一行代碼就能夠安裝切換系統的Python版本,廢話很少說,接下來就開始咱們的操做吧。android

鏈接到遠程服務器

如何購買XX雲的雲主機我就很少說了,當你買好以後咱們在後臺查看到咱們購買的雲主機的外網IP,好比個人服務器的外網IP地址就是94.191.9.43(但願各位大佬手下留情,別弄他):ios

接下來就是鏈接到服務器了,工具備不少,xshell、putty或者terminal均可以,這裏我推薦一個工具Termius,推薦他的緣由有兩個:顏值,做爲一個顏控,Termius是我見過的最好看的SSH客戶端,沒有之一。同時仍是跨平臺的,win/mac/linux/android/ios都支持,這點不知道比xshell高到哪裏去了。nginx

5c7eb7590c28be6ee5c8afdb_termius

鏈接也很簡單,咱們在termius中添加須要鏈接的host,而後輸入用戶名和密碼便可登陸了,出現以下字樣說明登陸成功。git

利用pyenv安裝制定版本的python

以前的文章介紹過如何在mac下安裝pyenv,雖然linux下的安裝方法相似,但畢竟本文的題目是是從0到1部署,仍是再詳細說一下吧。github

  • 下載安裝pyenv
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash 
複製代碼
  • 先查看是否存在~/.bash_profile文件
cat ~/.bash_profile
複製代碼
  • 若是沒有就新建該文件
touch ~/.bash_profile
複製代碼
  • 而後執行如下命令將pyenv添加到系統變量中
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile  
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile 
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
複製代碼

接下來執行pyenv命令,若是出現下圖則說明安裝成功。web

接下來咱們利用pyenv來安裝不一樣版本的python,只須要一行命令:sql

pyenv install xxx(版本號)
複製代碼

好比說咱們想安裝python 3.6.7,只須要執行pyenv install 3.6.7便可。這一步可能有點慢,主要看服務器的網速和性能了。shell

若是出現安裝失敗的話,能夠嘗試執行下面代碼安裝一些依賴庫後再從新安裝python:

sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
xz-utils tk-dev libffi-dev liblzma-dev
複製代碼

出現以下字樣說明安裝成功。

image-20190527155431084

接下來咱們執行pyenv versions查看咱們系統中目前已有的python

* system (set by /home/ubuntu/.pyenv/version)
  3.6.7
複製代碼

執行pyenv global 3.6.7將3.6.7做爲系統默認的python環境,執行下python命令查看系統python版本,以下圖所示咱們已經將系統的默認python版本切換爲3.6.7了。

image-20190527155707337

安裝pipenv管理項目的虛擬環境

  • 先升級pip
pip install --upgrade pip
複製代碼
  • 而後安裝pipenv
pip install pipenv
複製代碼

最後執行pipenv查看是否安裝成功。

他的具體用法參考我以前的一篇文章👉《Python 管理哪家強?》

將項目上傳至服務器

其實到這裏項目環境基本算是搭建完成了,不過也只是剛剛能用,離咱們最終須要搭建的環境還差點東西,這個後期再說,先來把項目上傳到咱們的服務器上,這裏我選擇的工具是FileZilla,一個開源免費的FTP客戶端(並且顏值也不錯),新建站點後添加服務器IP,輸入用戶名和密碼後點擊鏈接便可鏈接到遠程服務器,以下圖所示。

鏈接成功後咱們能夠在工具右側看到服務器的文件列表,我我的比較喜歡在/home/ubuntu下新建一個dev文件夾專門用來存放項目的代碼,方便管理,以下所示。

image-20190527162745456

咱們在工具左側選擇要上傳的文件,拖到指定文件夾中便可實現文件上傳。這裏我講代碼存放到/home/ubuntu/dev/python/stylize路徑下了,咱們也能夠經過terminal查看到文件。

這個代碼在上篇文章中有提到,可在微信公衆號「01二進制」後臺回覆「風格遷移API」得到

實現外網訪問

最原始的辦法

咱們已經將項目代碼上傳至服務器了,命令行進入該文件夾,而後執行pipenv install建立虛擬環境並下載所需依賴。接下來再執行pipenv shell進入虛擬環境,這時咱們能夠看見路徑前會有一個括號,裏面是虛擬環境的名字,以下所示。

(stylize) ubuntu@VM-0-10-ubuntu:~/dev/python/stylize$ 
複製代碼

對上一節提供的main.py代碼,咱們須要對main函數進行一些修改:

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=8080, debug=app.config['DEBUG']
複製代碼

相對於上一節內容,這次修改添加了host='0.0.0.0'這一參數,這一參數的的含義就是實現WSGI服務器的外網訪問,若是不添加這個host,默認只能內網訪問,這時咱們再執行python main.py啓動整個項目,出現以下效果說明服務已經啓動了。

image-20190527191041654

這時候咱們在瀏覽器中輸入ip:8080便可看到效果,例如我這的輸入的是http://94.191.9.43:8080,出現的結果以下:

⭐️須要注意的是,你須要在你購買的雲服務的控制檯設置安全組,以便給你的服務器開放相應的端口

至此咱們就實現了flask的應用在服務端的部署以及實現外網訪問了。

這樣就結束了嗎?

可是這樣就結束了嗎?固然不是,若是咱們把鏈接斷掉,這個服務就沒辦法執行。這時可能就有人會說了,那在執行python main.py這一命令前加一個nohup不就能夠實現後臺運行了嗎?沒錯,這樣確實能夠實現後臺運行,可是在實際的生產環境中是不會有人使用這樣的方式部署的,緣由很簡單,Flask 是一個web框架,而非web server,直接用Flask拉起的web服務僅限於開發環境使用,生產環境不夠穩定,也沒法承受大量請求的併發,在生產環境下須要使用專業的服務器軟件來處理各類請求。既然flask自帶的服務器性能不好,那咱們天然要清楚專業的服務器,這裏的可選項有Gunicorn、 Nginx或Apache,一般狀況下,咱們選擇Gunicorn+Nginx這樣的組合。

Gunicorn是什麼?

這裏簡單回答兩個問題,Gunicorn是什麼以及爲何是Gunicorn。

第一個問題,Gunicorn是什麼?

Gunicorn中文名"獨角獸",一個開源Python WSGI UNIX的HTTP服務器。

第二個問題,爲何是Gunicorn?

效率高,使用方便。

爲何是Gunicorn+Nginx?

《AI畫家第三彈——畢業設計大殺器之Flask》這篇文章中咱們曾提過web服務器應用服務器這兩種服務器,其中應用服務器既能夠解析靜態資源,也能夠解析動態資源,可是解析靜態資源的能力不如web服務器。而Nginx就是web服務器,Gunicorn是應用服務器,採用這樣的組合,一方面基於Nginx轉發Gunicorn服務,在生產環境下能補充Gunicorn服務在某些狀況下的不足,另外一方面,若是作一個Web網站,除了服務外,還有不少靜態文件須要被託管,這是Nginx的強項,也是Gunicorn不適合作的事情。因此,基於Flask開發的網站,部署時用Gunicorn和Nginx,是一個很好的選擇。

那爲何須要Nginx轉發Gunicorn服務?

按照上面的解釋,其實直接使用Gunicorn就徹底能夠實現外網訪問了,爲何非要加一層Nginx呢?

緣由其實也很簡單,Nginx功能強大,用Nginx轉發Gunicorn服務,重點是解決「慢客戶端行爲」給服務器帶來的性能下降問題;另外,在互聯網上部署HTTP服務時,還要考慮的「快客戶端響應」、SSL處理和高併發等問題,而這些問題在Nginx上一併能搞定,因此在Gunicorn服務之上加一層Nginx反向代理,是個一舉多得的部署方案。

若是想深刻了解Nginx/Gunnicorn在項目中扮演的角色,推薦閱讀👇

Nginx、Gunicorn在服務器中分別起什麼做用? - 知乎

www.zhihu.com/question/38…

開始實現

啓動Gunicorn

上面說了那麼多基礎知識和原理,趕忙來實際操做下吧,首先安裝Gunicorn,安裝方法很簡單,在pipenv虛擬環境中,咱們直接運行如下代碼就能夠了。

pipenv install gunicorn
複製代碼

而後運行

gunicorn -D -w 3 -b 127.0.0.1:8080 main:app
複製代碼

這樣就能夠啓動一個web服務,這裏咱們來解釋下這些參數。

  1. gunicorn:命令的名稱,很少解釋
  2. -D 表示後臺運行
  3. -w 表示線程
  4. -b 指定ip和端口(建議使用本地端口,方便nginx進行代理。)
  5. main是項目的啓動文件(main.py)
  6. app 是全局變量 (app = Flask(__name__)

雖說上述命令也不是很難,可是每次都輸這麼長的命令不免會出錯,有沒有什麼簡單的方法呢?答案天然是有的,咱們能夠將這些命令的參數以配置文件的形式保存到本地。

首先在項目根目錄下新建一個gunicorn.conf,而後寫入下面的信息:

# 並行工做線程數
workers = 4
# 監聽內網端口8080【按須要更改】
bind = '127.0.0.1:8080'
# 設置守護進程【關閉鏈接時,程序仍在運行】
daemon = True
# 設置超時時間120s,默認爲30s。按本身的需求進行設置
timeout = 120
# 設置訪問日誌和錯誤信息日誌路徑
accesslog = './logs/acess.log'
errorlog = './logs/error.log'
複製代碼

做用就如註釋所言。這時咱們再啓動項目只須要執行下面的命令便可

gunicorn -c gunicorn.conf main:app
複製代碼

記得提早建立好logs這個文件夾哦😊

關閉Gunicorn

經歷過上述操做後咱們就能夠啓動一個項目了,那該如何關閉呢?

很簡單,分兩步

step1:

pstree -ap|grep gunicorn
複製代碼

顯示結果:

(stylize) ubuntu@VM-0-10-ubuntu:~/dev/python/stylize$ pstree -ap|grep gunicorn
  |-gunicorn,9349/home/ubuntu/.local/share/virtualenvs/stylize-rKP1K2-f/bin/gun
  |   |-gunicorn,9352/home/ubuntu/.local/share/virtualenvs/stylize-rKP1K2-f/bin/gun
  |   |-gunicorn,9353/home/ubuntu/.local/share/virtualenvs/stylize-rKP1K2-f/bin/gun
  |   `-gunicorn,9354/home/ubuntu/.local/share/virtualenvs/stylize-rKP1K2-f/bin/gun
  |                       |-grep,9365 --color=auto gunicorn
複製代碼

step2:

kill -9 9349
複製代碼

-9表示強制關閉,9349表示運行的進程號(根據你本身的服務器狀況來定)

安裝配置Nginx

在Ubuntu上安裝Nginx很簡單,一條命令就能夠了:

sudo apt-get install nginx
複製代碼

接下來就是配置了,nginx的默認配置文件在/etc/nginx/sites-enabled/default中,執行sudo vim default,修改下面部分的代碼

server {
    listen 80;
    server_name localhost;
    location /{
        proxy_pass http://127.0.0.1:8080;
    }
}
複製代碼

這裏咱們解釋下,修改上述內容的做用是:將外部經過80端口發來的請求,代理給127.0.0.1:8080端口。還記得咱們在gunicorn.conf中設置了flask的啓動地址爲127.0.0.1:8080,到這裏終於派上了用場。

而後執行如下命令重啓nginx

sudo nginx -s reload
複製代碼

這時咱們再訪問http://94.191.9.43,就不用添加端口號了。

image-20190528014523423

若是你有域名的話,在後臺把域名解析到這個ip上就能夠實現域名訪問了,是否是頗有趣呢?

固然在Gunicorn上再加一層Nginx服務,除了實現反向代理,另外一個緣由就是用它配置HTTPS很是方便,這裏我就很少說了,若是讀者有興趣的話能夠查閱相關資料,或者在評論區/後臺留言我,若是想了解的人數多的話我就找個機會挑出來單獨寫下。

日誌!日誌!日誌!

除了上述HTTPS的問題,不知道大家有沒有發覺還少了點東西。沒錯,就是日誌!重要的事情說三遍!做爲一個後臺程序,怎麼能夠少了日誌呢?既然咱們已經使用Gunicorn接管了Flask項目,那怎麼才能得到Flask的日誌信息呢?別急,Gunicorn早就幫你想好了,很簡單,只須要在啓動文件也就是main.py中添加一段代碼便可。

import logging
if __name__ != '__main__':
    gunicorn_logger = logging.getLogger('gunicorn.error')
    app.logger.handlers = gunicorn_logger.handlers
    app.logger.setLevel(gunicorn_logger.level)
複製代碼

這樣的寫法主要作了4件事:

  1. if __name__ != '__main__'這種寫法不多見,意思是僅當該文件做爲模塊被導入時內部的代碼纔會被執行,有個好處,避免了硬編碼,當執行gunicorn main:app跑的時候main.py會被當成模塊導入到gunicorn內部, 由於gunicorn實際上就是純pyhton寫的
  2. 獲取gunicorn的logger
  3. 將gunicorn的logger和flask app的logger綁定在一塊兒
  4. 將綁定的logger 的level設置成gunicorn logger的level, 由於最終輸出的log level是在gunicorn中配置的

而後從新啓動gunicorn就能夠了,日誌存放的路徑咱們以前也已經在gunicorn.conf中定義過了,用cat命令查看就OK了。

最後

不知不覺已經碼了將近4000字,此次寫這麼多一方面是由於最近用這東西的時候發現能夠擴展的東西太多了,另外一方面也是但願能夠和其餘博客的內容有所區別,不只僅把實現過程表現出來,還能夠把本身使用過程當中的一些習慣、心得體會分享出來和你們交流。固然部署這裏能挖的還有不少,好比如何獲取來訪者的IP地址並加以分析,如何用Nginx配置HTTPS等等等等,有興趣的能夠相關資料,或者在留言區/後臺和我討論,大家的支持纔是我前進的最大動力!

相關文章
相關標籤/搜索