天天看點

Supervisor和Gunicorn部署Flask項目

基本環境

最簡單的Flask Web應用:

建立檔案夾myproject,裡面就一個檔案:myapp.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hi! It works!'

if __name__=='__main__':
    app.run(host='0.0.0.0')


           

想啟動該應用,隻要運作python myapp.py就可以在浏覽器http://localhost:5000看到運作結果了。開心!

然鵝,這裡的問題是:這個應用是單程序單線程的,如果在多個使用者(高并發)的場景,這個就要跪了

為什麼使用Gunicorn

Flask 作為一個 Web 架構,内置了一個 webserver, 但這自帶的 Server 到底能不能用?

官網的介紹:

While lightweight and easy to use, Flask’s built-in server is not suitable for production as it doesn’t scale well. Some of the options available for properly running Flask in production are documented here.

很顯然,内置的 webserver 是能用的。但不适合放在生産環境。這個 server 本來就是給開發者用的。架構本身并不提供生産環境的 web 伺服器,SpringBoot 這種内置 Tomcat 生産級伺服器 是例外。

假設我們使用的是 Nginx+Flask Run 來當作生産環境,全部部署在一台機器上。劣勢如下:

  • 單 Worker。

    隻有一個程序在跑所有的請求,而由于實作的簡陋性,内置 webserver 很容易卡死。并且隻有一個 Worker 在跑請求。在多核 CPU 下,僅僅占用一核。當然,其實也可以多起幾個程序。

  • 缺乏 Worker 的管理。

    接上,加入負載量上來了,Gunicorn 可以調節 Worker 的數量。而這個東西,内置的 Webserver 是不适合做這種事情的。一言以蔽之,太弱,幾個請求就打滿了。

Gunicorn(綠色獨角獸)是一個Python WSGI的HTTP伺服器。從Ruby的獨角獸(Unicorn )項目移植。該Gunicorn伺服器與各種Web架構相容,實作非常簡單,輕量級的資源消耗。Gunicorn直接用指令啟動,不需要編寫配置檔案,相對uWSGI要容易很多。

Gunicorn 作為 Server 相對而言可以有什麼提升?

  • gunicorn 的優點如下, 幫我 scale worker, 程序挂了幫我重新開機
  • 用 python 的架構 flask/django/webpy 配置起來都差不多。
  • 還有信号機制。可以支援多種配置。
  • 其他:在管理 worker 上,使用了 pre-fork 模型,即一個 master 程序管理多個 worker 程序,所有請求和響應均由 Worker 處理。Master 程序是一個簡單的 loop, 監聽 worker 不同程序信号并且作出響應。 比如接受到 TTIN 提升 worker 數量,TTOU 降低運作 Worker 數量。如果 worker 挂了,發出 CHLD, 則重新開機失敗的 worker, 同步的 Worker 一次處理一個請求。 PS: 如果沒有靜态資源并且無需反向代理的話,抛棄 Nginx 直接使用 Gunicorn 和 Flask app 也能搞定。采用多程序時,隻需要占用 1 個端口即可,收到的http請求自動分發到各個worker程序。同時 worker_class 選用 gevent 異步協程方式,測試發現性能會有很大提升。

架構:

nginx(反向代理)-> gunicorn(with gevent)-> flask server -> supervisor

nginx 隻使用反向代理功能,不再作負載均衡。負載均衡在 gunicorn 側實作,采用輪詢方式。

Gunicorn

這時候Gunicorn登場了,它就是來解決這個高并發的問題的。它全名叫Green Unicorn,是一個被廣泛使用的高性能的Python WSGI Unix HTTP伺服器。

Gunicorn 是python中的WSGI容器,pre-fork worker模式,優點就是配置簡單,輕量級的資源消耗,以及高性能。

安裝

$ pip install gunicorn
           

相關配置:

bind = '0.0.0.0:17123'      # 監聽位址和端口
backlog = 2048              # 最大挂起連接配接數
timeout = 30                # 逾時時間
worker_class = 'gevent'     # worker 程序的工作方式。目前選用 gevent 異步協程方式
worker_connections = 65535  # 用戶端最大同時連接配接數
workers = 1                 # 處理請求的 worker 程序數量,多程序
threads = 1                 # 每個 worker 程序的線程數。采用 gevent 協程方式時,配置 1 即可
           

使用

在指令行下,鍵入:

$ gunicorn -w 4 myapp:app
           

可以看到輸出:

[2018-10-25 20:21:12 +0800] [28597] [INFO] Starting gunicorn 19.9.0
[2018-10-25 20:21:12 +0800] [28597] [INFO] Listening at: http://127.0.0.1:8000 (28597)
[2018-10-25 20:21:12 +0800] [28597] [INFO] Using worker: sync
[2018-10-25 20:21:12 +0800] [28600] [INFO] Booting worker with pid: 28600
[2018-10-25 20:21:12 +0800] [28601] [INFO] Booting worker with pid: 28601
[2018-10-25 20:21:12 +0800] [28602] [INFO] Booting worker with pid: 28602
[2018-10-25 20:21:12 +0800] [28603] [INFO] Booting worker with pid: 28603

           

在浏覽器鍵入

http://localhost:8000

就可以通路。

其中-w表示啟動的程序數,myapp是Python檔案名,app是檔案中的變量名或者函數名。可以看到,預設是綁定8000端口的,也就是說Python檔案裡那個main函數可以忽略了。

多程序高并發的問題解決了,開心!

然鵝,使用這個指令時,當我們退出終端時,這個應用就跪了,不能繼續在背景運作,當然我們可以用

nohup gunicorn -w 4 myapp:app &這

個指令讓它在背景運作,但是後續的狀态監控呀什麼的,可能就要用什麼ps、kill這種原生态指令了,這真是太TM煩了。

實際環境:

啟動指令:

gunicorn_conf_MEO.py -> gunicorn 配置檔案

MEO : app -> MEO.py檔案名 : flask 中的app = Flask(name)

gunicorn -c gunicorn_conf_MEO.py MEO:app
           

Supervisor

這時候Supervisor閃亮登場!

Supervisor是一個程序管理系統,它通過fork/exec的方式将這些被管理的程序當作它的子程序來啟動,若該子程序異常中斷,則父程序可以準确地擷取子程序異常中斷的資訊。

supervisor是一個制作守護程序的工具,使用者可以在UNIX系統中監控、管理程序。

常用于管理與某個使用者或項目相關的程序。

去幫我們維護各種伺服器的程序,即使有軟體崩了也能幫我們自動重新開機。

用于在生産環境中,控制項目涉及的軟體的程序。

如果不使用supervisor,重新開機gunicorn的步驟是:

指定程序和端口号:

-w

: 表示程序(

worker

)。

-b

:表示綁定ip位址和端口号(bind)。

$gunicorn -w 4 -b 127.0.0.1:5001 運作檔案名稱:Flask程式執行個體名
           
pstree -ap|grep gunicorn
kill -9(gunicorn pid)
gunicorn -w6 -b 127.0.0.1:80 demo:app
           

主要功能:

  1. 通過指令檢視程序的狀态,停止和啟動程序
  2. 程序異常退出時,自動重新開機
  3. 程序啟動失敗自動重試
  4. 配置采用Linux哪個使用者啟動程序
  5. 設定程序啟動優先級
  6. supervisor 啟動時可自動拉起管理的各程序

安裝

sudo apt install supervisor
           

使用

在項目檔案夾下建立supervisor_app.conf檔案:

[include]
files=/etc/supervisord.conf

[program:awesome_app]
directory=/home/gld/myproject
command=gunicorn -w 4 myapp:app
           

生成配置檔案:echo_supervisord_conf > /etc/supervisord.conf(預設是沒有配置檔案的)

例如在配置檔案/etc/supervisord.conf中添加:

[program:gunicorn]
command=gunicorn manage:app	;supervisor啟動指令
directory=/home/hanquan/testPython/xxx	;項目的檔案夾路徑
startsecs=0	;啟動時間
stopwaitsecs=0	;終止等待時間
autostart=true	;是否自動啟動
autorestart=true	;是否自動重新開機
stdout_logfile=/home/hanquan/testPython/xxx/log/gunicorn.log	;log日志
stderr_logfile=/home/hanquan/testPython/xxx/log/gunicorn.err	;error日志

[program:nginx]
command=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf -g 'daemon off;' ;前台運作
directory=/home/hanquan/testPython/xxx	;項目的檔案夾路徑
startsecs=0	;啟動時間
stopwaitsecs=0	;終止等待時間
autostart=true	;是否自動啟動
autorestart=true	;是否自動重新開機
stdout_logfile=/home/hanquan/testPython/xxx/log/nginx.log	;log日志
stderr_logfile=/home/hanquan/testPython/xxx/log/nginx.err	;error日志

           

實際環境:

[program:meo_zwj]              z'w            ; 被管理的程序配置參數,後面是程序的名稱
directory=/home/vnfm/zwj/meo/MEO/MEO   ; 腳本目錄
command=gunicorn -c configure/gunicorn_conf_MEO.py MEO:app  ; 腳本執行指令
priority=999                        ; 程序啟動優先級,預設999,值小的優先啟動
autostart=true                      ; 在supervisord啟動的時候也自動啟動
startsecs=10                        ; 啟動10秒後沒有異常退出,就表示程序正常啟動了,預設為1秒
startretries=3                      ; 啟動失敗自動重試次數,預設是3
autorestart=true                    ; 程式退出後自動重新開機,可選值:[unexpected,true,false]
user=vnfm                           ; 用哪個使用者啟動程序,預設是root
redirect_stderr=true                ; 把stderr重定向到stdout,預設false
stdout_logfile=/tmp/meo_zwj_stdout.out
stderr_logfile=/tmp/meo_zwj_stderr.out
           

啟動supervisord:

sudo supervisord -c myproject/supervisor_app.conf
           

啟動我們的應用:

$ sudo supervisorctl start awesome_app
           

常用管理指令:

$sudo supervisorctl start [program_name]
$sudo supervisorctl stop [program_name]
$sudo supervisorctl restart [program_name] # 重新開機服務,注意這裡不會重新加載配置檔案

           

更新項目檔案後,重新開機supervisor指令:

supervisorctl restart all
           

重新加載配置檔案,重新啟動正在運作的服務:

$sudo supervisorctl reload
           

重新加載修改過的配置并重新開機該服務:

$sudo supervisorctl reread
$sudo supervisorctl update

           

supervisor 相關操作:

systemctl start supervisord.service     //啟動 supervisor 并加載預設配置檔案
systemctl stop supervisord.service     //停止 supervisor
           

管理各服務程序:

supervisorctl status                  //檢視所有程序的狀态
supervisorctl stop meo_vimproxy       //停止 meo_vimproxy 程序
supervisorctl start meo_vimproxy      //啟動 meo_vimproxy 程序
supervisorctl restart meo_vimproxy   //重新開機 meo_vimproxy 程序
supervisorctl restart all            //重新開機所有程序
supervisorctl update                 //配置檔案修改後使用該指令加載新的配置
supervisorctl reload                //重新啟動配置中的所有程式

------------------------------------------------------------------------------------------
supervisor的伺服器端部分啟動指令:
sudo unlink /var/run/supervisor.sock(執行第二行産生檔案後執行)
supervisord -c /etc/supervisord.conf
此時預設開啟了所有服務

supervisor的用戶端部分指令:
supervisorctl status 			檢視程序運作狀态
supervisorctl start 程序名 		啟動程序
supervisorctl stop 程序名 		關閉程序
supervisorctl restart 程序名 	重新開機程序
supervisorctl update 			重新載入配置檔案
supervisorctl shutdown 			關閉supervisord
supervisorctl clear 程序名 		清空程序日志
supervisorctl 					進入到互動模式下。使用help檢視所有指令。

例如:	supervisorctl start all		啟動所有程序
		supervisorctl restart all	重新開機所有程序
		supervisorctl stop all		停止所有程序

           

進階

Gunicorn

常用參數:

-w:程序數,如-w 4

-b:綁定位址和端口,如-b 0.0.0.0:5000

Supervisor

Supervisor隻能監控前台程式, 如果你的程式是通過fork方式實作的daemon服務,則不能用它監控,否則會提示:BACKOFF Exited too quickly (process log may have details)。 是以像Apache、Tomcat服務預設啟動都是按daemon方式啟動的,則不能通過Supervisor直接運作啟動腳本(service httpd start),相反要通過一個包裝過的啟停腳本來完成。(參考部落格)

日志

Logging, Flask, and Gunicorn … the Manageable Way

常見問題

CentOS 7.4預設的日志列印在/var/log/supervisor/supervisord.log檔案下。

Permission權限問題

有時候提示往/tmp檔案夾寫入檔案提示PermissionErr,檢查supervisor配置檔案,可以設定為user=root。

exit status 127; not expected

127表示指令沒找到,首先可能是directory寫錯,用cd指令看一下,然後可能是command指令寫錯,複制指令在控制台試試是否gunicorn無法使用。

supervisor: couldn’t exec celery: EACCES

CentOS

CentOS Linux release 7.6.1810
           

安裝Python3:

yum install python36u
           

安裝pip3:

yum install python36u-pip
           

安裝gunicorn:

pip3 install gunicorn
           

安裝supervisor:

yum install supervisor
           

頭條項目關于supervisor 另一種配置方法:

1. 配置

運作

echo_supervisord_conf

指令輸出預設的配置項,可以如下操作将預設配置儲存到檔案中

echo_supervisord_conf > supervisord.conf
           

vim 打開編輯supervisord.conf檔案,修改

[include]
files = relative/directory/*.ini
           

[include]
files = /etc/supervisor/*.conf
           

include

選項指明包含的其他配置檔案。

将編輯後的supervisord.conf檔案複制到/etc/目錄下

然後我們在/etc目錄下建立子目錄supervisor(與配置檔案裡的選項相同),并在/etc/supervisor/中建立tuotiao管理的配置檔案

toutiao.conf

[group:toutiao]
programs=toutiao-app

[program:toutiao-app]
command=/home/python/scripts/toutiao_app.sh
directory=/home/python/toutiao-backend
user=python
autorestart=true
redirect_stderr=false
loglevel=info
stopsignal=KILL
stopasgroup=true
killasgroup=true

[program:im]
command=/home/python/scripts/im.sh
directory=/home/python/toutiao-backend
user=python
autorestart=true
redirect_stderr=false
loglevel=info
stopsignal=KILL
stopasgroup=true
killasgroup=true
           

啟動

supervisord -c /etc/supervisord.conf
           

檢視 supervisord 是否在運作:

ps aux | grep supervisord
           
supervisorctl

我們可以利用

supervisorctl

來管理supervisor。

supervisorctl     // 執行supervisorctl,進去指令行編輯器
> status    # 檢視程式狀态
> start apscheduler  # 啟動 apscheduler 單一程式
> stop toutiao:*   # 關閉 toutiao組 程式
> start toutiao:*  # 啟動 toutiao組 程式
> restart toutiao:*    # 重新開機 toutiao組 程式
> update    # 重新開機配置檔案修改過的程式
           

執行status指令時,顯示如下資訊說明程式運作正常:

supervisor> status
toutiao:toutiao-app RUNNING pid 32091, uptime 00:00:02