CentOS 下部署Nginx+Gunicorn+Supervisor部署Flask項目

本來以前有一部分東西是在Windows Server,可是因爲Gunicorn不支持Windows部署起來頗爲麻煩。最近轉戰CentOS,折騰一段時間,終於簡單部署成功。CentOS新手,做爲一個總結和整理,錯漏不免。
 
首先,須要明確上述幾個模塊各自的 做用(由於之前在windows的IIS就頗爲簡單,只須要在IIS建站,經過fastCGI處理php文件便可,因此對於CentOS部署中的各個package,理解其各自的做用尤其關鍵):
  1. 在Flask框架下,直接運行app便可利用localhost進行訪問,可是該解析器僅僅適用於調試, 能夠說比較簡陋,不能直接在生產環境中使用。由於其實單線程處理,一個請求沒有結束,其餘的請求全都進不來,因此須要Gunicorn的多進程來提高處理能力. 關於Flask的調試,我利用Chrome的Advanced Rest clien進行api調試
         2. Gunicorn:基於Python的WSGI Server(UNIX) 可以處理請求並交給Flask的app去處理而後進行返回。
  1. Nginx: 其實剛看到Gunicorn,已經可以處理http請求了,爲何還須要它呢?一句話:性能和可拓展性。可能你只是一臺server,只是簡單的套一個Nginx而且不進行配置,可能用處真的不大,不過考慮到 (參考:https://www.zhihu.com/question/38528616):
    • 靜態文件: Nginx能夠直接處理靜態文件請求,那麼經過配置讓Nginx直接處理靜態文件。
    • Hold住併發:Nginx可能並不能提高處理能力,可是在一些高併發的場景下,經過nginx保持住請求能夠爲後端處理贏取時間而不是直接掛掉。由Nginx來緩存發起的請求直到請求接收徹底再交給Gunicorn去處理,避免Gunicorn處理請求和響應到客戶端的時間消耗,提高Gunicorn的處理能力。
    • 負載均衡:這個在單服務器下可能用處不大,可是誰保不會拓展到多服務器,那麼利用Nginx對請求進行分發實現反向負載均衡就意義重大了。
    • 多域名處理:經過Nginx的分發,能夠在一臺服務器上掛多個域名,以避免80端口衝突:即Nginx佔用80並分發不一樣的請求到不一樣的內部端口。
    • 訪問控制,限流等,我暫時沒有用到
  2. Supervisor: 利用它,對Gunicorn的進程進行管理。例如CentOS重啓的狀況,咱們天然須要Gunicorn自動開始運行提供服務而不是每次重啓都要手動輸入再去啓動一遍。
 
部署流程:
 
1.根據須要,在CentOS上利用yum安裝你的服務所須要的基本環境而且進行配置, 對我來說:
  • Mysql: 數據持久化和一致性的存儲的關係數據庫
  • Redis:  我只是利用其作cache(以前用的Memcached)
  • Git: 用來作版本控制,同時方便本機開發的東西從hub上clone和pull
  • Anaconda: 我利用conda來進行虛擬環境管理 (能夠利用virtualenv, 總之就是爲了防止你不一樣的項目(不一樣的版本)之間不一樣的python庫的相互干擾,利用conda/virtualenv來隔離不一樣的運行環境)
  • 各類環境的安裝細節我就不表了,善用搜索引擎。
這一步中可能出現的bugs比較多,善用系統反饋給你的提示和搜索引擎。
 
2.從Git中心Clone你本身的項目到服務器本地, 而後利用你的project中的model生成數據庫,同時配置你的配置文件(例如數據庫參數,redis參數等等),同時創建項目的Vitualenv,而後安裝項目的依賴項到該虛擬環境!!(不要全局安裝)
#for conda
source activate my_project_env
conda install -r requirements.txt
 
#for virtualenv
activate my_project_env
pip install -r requirements.txt
安裝完項目須要的環境,配合數據庫和緩存都作好,這個時候應該已經能夠利用 python app.py (激活了my_project_env的狀況下)來經過Flask本身的server提供測試級別的服務,就像在本地測試那樣測試你的項目是否OK.
此步中若是requirements.txt中可能沒有包含的packages, 直接安裝解決衝突就行了。
 
3.安裝Gunicorn (安裝細節不表), 安裝了Gunicorn後,利用
 
gunicorn -w 4 -b 0.0.0.0:8080 run:app
# -w: worker 表示能夠處理的進程數 至關於 --worker=4
# -b: bind 就是綁定到哪一個屁和端口,0.0.0.0表示能夠用服務器的ip訪問
# run: 表示run.py app表示Flask application. 
# 此條命令須要在run.py目錄下執行,不然須要指明run的路徑例如/home/leslie/flask_proj/run:app
 
能夠利用服務器或者Advanced RESTFul  client 進行測試了, your-IP:8080
 
4.Nginx的配置
這一步是比較麻煩的,此前的步驟無非就是package的缺失和衝突。而Nginx的配置就須要手動改一些參數了,須要理解。
Nginx的配置文件,存在CentOS的 /etc/nginx/nginx.conf: (關於CentOS下怎麼改文檔,能夠用VIM( https://coolshell.cn/articles/5426.html)或者nano,若是利用windows生成的文檔可能會形成換行符的錯誤,請利用notepad++先轉成Unix格式文檔再上傳)
server {
    listen       80;     # nginx監聽的端口
    server_name  _;   
    location / {
        proxy_pass http://127.0.0.1:8080;    # 表名nginx接收到請求去哪裏找gunicorn的服務
        proxy_redirect off;
        proxy_set_header Host $host:80;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
注意:該配置文件中有 include:  /etc/nginx/conf.d/default.conf  # 因此配置哪一個應該都是能夠的。
service nginx start/stop/restart/reload/status   

#check status: ps -aux | grep nginx  
Nginx配置完畢並開啓後,應該就能夠直接用ip訪問了,不須要再指定端口8080了,
至關於 request----->nginx------>gunicorn---->Flask
Flask + Gunicorn + Nginx 部署這位同窗的例子很形象:
(nginx收到客戶端發來的請求,根據nginx中配置的路由,將其轉發給WSGI)
 
nginx:"WSGI,找你的來了!"
 
(WSGI服務器根據WSGI協議解析請求,配置好環境變量,調用start_response方法呼叫flask框架)
 
WSGI服務器:"flask,快來接客,客戶資料我都給你準備好了!"
 
(flask根據env環境變量,請求參數和路徑找到對應處理函數,生成html)
 
flask:"!@#$%^......WSGI,html文檔弄好了,拿去吧。"
 
(WSGI拿到html,再組裝根據env變量組裝成一個http響應,發送給nginx)
 
WSGI服務器:"nginx,剛纔誰找我來着?回他個話,!@#$%^....."
 
(nginx再將響應發送給客戶端)
 
5. Supervisor
最後是經過基於python的supervisor來管理gunicorn的開機啓動了(固然nginx也要開機啓動),首先利用conda或者pip安裝Supervisor (注意Supervisor目前不支持python3,可能須要創建2.7的環境來安裝):
Supervisor的配置有幾個步驟:
  • 配置supervisor啓動Gunicorn來提供Flask的服務
  • 配置Supervisor的開機啓動,從而實現重啓後你的web繼續提供服務
具體步驟主要參考這兩篇,不過在個人實踐中,這兩篇各有一些小問題,不過綜合起來就差很少了:
  • 安裝supervisor(pip or conda)
###生成一個supervisord服務的配置文件
echo_supervisord_conf > supervisord.conf
###將配置文件統一放在/etc下
cp  supervisord.conf  /etc/supervisord .conf
 
###  修改配置文件
vi  /etc/supervisord .conf
 
##加入如下配置信息,
[include]
files =  /etc/supervisord .d/*.conf  
 
###爲了避免將全部新增配置信息全寫在一個配置文件裏
### 每一個配置信息新增一個配置文件,都會被上面那個include添加進去
mkdir  /etc/supervisord .d/
 
### 在此目錄下增長本app的conf
sudo vi /etc/supervisord.d/my_flask.conf
 
#加入, 注意其中的路徑和用戶名:
[program:my_flask_app]
directory=/home/leslie/flask-project-directory 
command=/your-gunicorn-whole-path-here/gunicorn app:app -b 127.0.0.1:8080 --workers 8 --max-requests 1000
user=leslie
autostart=true
autorestart=true
redirect_stderr=True
  • 建立supervisor做爲一個service的啓動腳本( 位置/etc/init.d/supervisord ):
#!/bin/sh
#
# /etc/init.d/supervisord
#
# Supervisor is a client/server system that
# allows its users to monitor and control a
# number of processes on UNIX-like operating
# systems.
#
# chkconfig: - 64 36
# description: Supervisor Server
# processname: supervisord
 
# Source init functions
/etc/rc .d /init .d /functions
 
prog= "supervisord"
 
prefix= "/usr/leslie"
exec_prefix= "${prefix}"
prog_bin= "${exec_prefix}/your-path-of-supervisor/bin/supervisord"
# PIDFILE= "/var/run/$prog.pid"  # 博客推薦的這個設置對我來講不行,我檢查本身的supervisord.conf發現pidfile在tmp
PIDFILE="/tmp/$prog.pid"    
CONF= "/etc/supervisord.conf"   # 讓啓動的時候知道去哪裏找配置文件 文章2沒有這個
 
start()
{
        echo  -n $ "Starting $prog: "
        ###注意下面這一行必定得有-c /etc/supervisord.conf 
        daemon $prog_bin -c $CONF  --pidfile $PIDFILE
        [ -f $PIDFILE ] && success $ "$prog startup"  || failure $ "$prog startup"
        echo
}
 
stop()
{
        echo  -n $ "Shutting down $prog: "
        [ -f $PIDFILE ] && killproc $prog || success $ "$prog shutdown"
        echo
}
 
case  "$1"  in
 
  start)
    start
  ;;
 
  stop)
    stop
  ;;
 
  status)
        status $prog
  ;;
 
  restart)
    stop
    start
  ;;
 
  *)
    echo  "Usage: $0 {start|stop|restart|status}"
  ;;
 
esac
  • 而後加入該service到centos的啓動項並啓動
sudo chmod +x /etc/rc.d/init.d/supervisord 
sudo chkconfig --add supervisord
sudo chkconfig supervisord on 
sudo service supervisord start
  • 就能夠查看了:
ps -ef | grep supervisord
## 同時也能夠有service supervisord restart/stop/stats

 

此步中可能出現的問題:
ps -ef | grep supervisord   # 經過這個來得到supervisord的進程pid 而後殺掉以前的進程後再啓動。
kill (-s SIGTERM) pid
---- supervisor須要python2的環境,而項目多是不一樣的環境,因此在較多配置的時候都須要顯式的指定路徑。
---- syntax error near unexpected token 之類的問題極可能是腳本的問題(好比以前提到window上編輯形成的行位標識符的區別)
---- service supervisord start 時候出現 「No such file or directory error」:
   a.請不要太相信你本身的輸入,請多檢查!!我由於吧directory寫成了derectory(對vi不太熟的時候)這個問題困擾了好久。
   b.此問題也可能來源與/etc/init.d/service_script的符號,例如windows建立的問題。。 詳細可見:( Starting service on Linux throws a No such file or directory error  
 
 
 
6 TODO: 
----利用fabric部署?
----http--->https
 
 
更多參考:
相關文章
相關標籤/搜索