文章目錄
0. 參考
- Gunicorn官方文檔
- Supervisor官方文檔
- Django 部署基礎
- Django 部署(Nginx)
- 從django、flask、tornado的部署說起
- Supervisor使用詳解
- supervisor(一)基礎篇
1. 概述
Python的web框架主要有Django、Flask等,而這兩個都是運行在WSGI協議之上的框架
這些框架部署的鏈路一般是:Nginx -> uWSGI -> Python Web程序,通常還會結合Supervisor工具來進行監聽啓停
先了解以下概念:
- WSGI:Web Server Gateway Interface,WSGI不是服務器,python模塊,框架,API或者任何軟件,只是一種規範,描述web server如何與web application通信的規範。要實現WSGI協議,必須同時實現web server和web application,目前主流的Python web框架都是基於這個規範實現的。
- uwsgi:與WSGI一樣是一種通信協議,是uWSGI服務器的獨佔協議,用於定義傳輸信息的類型,簡潔高效的uwsgi協議是選擇uWSGI作爲部署工具的重要理由之一
- uWSGI:是一個web服務器,實現了WSGI協議、uwsgi協議、http協議等
- Nginx:Nginx是一個Web服務器,是一個反向代理工具,我們通常用它來部署靜態文件。主流的Python Web開發框架都遵循WSGI規範。
- Supervisor:一個進程管理工具。任何人都不能保證程序不異常退出,不被人誤殺,所以一個典型的工程做法就是使用Supervisor看守着你的進程,一旦異常退出它會立馬進程重新啓動起來。
補充:
WSGI協議主要包括server和application兩部分
- WSGI server負責從客戶端接收請求,將request轉發給application,將application返回的response返回給客戶端
- WSGI application接收由server轉發的request,處理請求,並將處理結果返回給server。application中可以包括多個棧式的中間件(middlewares),這些中間件需要同時實現server與application,因此可以在WSGI服務器與WSGI應用之間起調節作用:對服務器來說,中間件扮演應用程序,對應用程序來說,中間件扮演服務器。
WSGI協議其實是定義了一種server與application解耦的規範,即可以有多個實現WSGI server的服務器,也可以有多個實現WSGI application的框架,那麼就可以選擇任意的server和application組合實現自己的web應用。例如uWSGI和Gunicorn都是實現了WSGI server協議的服務器,Django,Flask是實現了WSGI application協議的web框架,可以根據項目實際情況搭配使用
uWSGI通過WSGI規範和我們編寫的服務進程通訊,然後通過自帶的高效的 uwsgi 協議和 Nginx進行通訊,最終Nginx通過HTTP協議將服務對外透出。
當一個訪問進來的時候,首先到 Nginx,Nginx會把請求(HTTP協議)轉換uwsgi協議傳遞給uWSGI,uWSGI通過WSGI和web server進行通訊取到響應結果,再通過uwsgi協議發給Nginx,最終Nginx以HTTP協議發現響應給用戶。
但其實呢,uWSGI本身是支持HTTP協議的,也支持靜態文件部署,這個鏈路是可以不用Nginx的,即uWSGI -> Python Web程序,但是Nginx會更加穩定且支持的併發量更高,所以主流的做法一般都是在前面加一層Nginx來做反向代理,避免併發量過大時候uWSGI直接服務掛掉
如下圖:
Django、Flask都有自己實現的簡單的WSGI server,比如Django啓動的時候使用python manage.py runserver,而在線上的時候一般會使用其他的WSGI server,比如gunicorn
2. Gunicorn
Gunicorn是一個WSGI HTTP Server,是用來替代Django、Flask自帶的Server,自帶的Server只能單線程運行,而Gunicorn能併發多線程,輕量級的資源消耗以及高性能等特點
主流做法是使用Gunicorn來進行託管啓動Python的web項目,另外使用supervisor來進行啓停和狀態的監控,一般會在Gunicorn的前端放置一個HTTP Proxy Server, 譬如Nginx。
如圖:
下面就以Flask爲例子
注意:前提是已經安裝好了Flask以及虛擬環境virtualenv等
A. 安裝
pip install gunicorn
注意:Linux下一般爲pip3 install gunicorn
B. 創建項目
創建項目目錄
mkdir -p /var/www/html/flask_test
cd /var/www/html/flask_test
創建入口文件vim app.py
from flask import Flask
app = Flask(__name__)
@app.route('/demo', methods=['GET'])
def demo():
return "Hello, This is a Demo"
C. 命令行配置
gunicorn支持命令行中定義配置,也支持-c參數從配置文件中讀取配置
先來測試一下命令行配置
執行 gunicorn -b 0.0.0.0:9100 app:app
注意:不指定-b 0.0.0.0:9100的話,默認是隻能本機訪問,並且端口默認爲8000
D. 測試
瀏覽器訪問:IP:9100/demo
E. 文件配置
一般情況下線上的項目都是通過加載配置文件來啓動的,-c參數來加載配置文件
Gunicorn配置文件一般是在項目根目錄下的
在這裏我使用的是項目目錄下的supervisor目錄
創建日誌目錄
mkdir -p /tmp/logs/flask_test
創建配置文件
mkdir -p /var/www/html/flask_test/supervisor
touch /var/www/html/flask_test/supervisor/gunicorn_conf.py
常用的配置信息:
bind:綁定的IP和端口,默認是127.0.0.1:8000
workers:用於處理工作進程的數量,爲正整數,默認爲1。worker推薦的數量爲當前的CPU個數*2 + 1
worker_class:要使用的工作模式,默認爲sync(同步),支持以下模式:
sync:同步
eventlet:異步模式,協程併發,需要eventlet>=0.9.7
gevent:異步模式,需要gevent>=0.13
tornado:異步模式,需要tornado>=0.2
gthread:線程工作模式,利用線程池管理連接
gaiohttp:異步模式,需要aiohttp>=0.21.5
threads:處理請求的工作線程數,使用指定數量的線程運行每個worker。爲正整數,默認爲1,只使用與gevent工作模式
timeout:超過這麼多秒後工作將被殺掉,並重新啓動。一般設定爲30秒
graceful_timeout:接收到重啓信號後,worker可以在graceful_timeout時間內,繼續處理完當前請求
reload:代碼更新時將重啓工作,默認爲False。此設置用於開發環境
spew:是否打印服務器執行過的每一條語句,默認False
chdir:加載應用程序之前,進入到該目錄
daemon:是否守護進程,默認False
raw_env:設置環境變量(key=value),將變量傳遞給執行環境,如:raw_env=["abc=123"]
accesslog:訪問日誌路徑
access_log_format:訪問日誌格式
errorlog:錯誤日誌路徑
loglevel:錯誤日誌輸出等級
proc_name:進程名
user: 指定worker進程的運行用戶名。
group: 指定worker進程運行用戶所在組。
更多配置參考官方文檔
簡單的配置文件gunicorn_conf.py(無日誌切割)
import multiprocessing
bind = '0.0.0.0:9100'
workers = multiprocessing.cpu_count() * 2 + 1 #進程數
reload = True
loglevel = 'info'
timeout = 600
log_path = "/tmp/logs/flask_test"
accesslog = log_path + '/gunicorn.access.log'
errorlog = log_path + '/gunicorn.error.log'
通用的配置文件gunicorn_conf.py(帶日誌切割)
import multiprocessing
bind = '0.0.0.0:9100'
workers = multiprocessing.cpu_count() * 2 + 1 #進程數
reload = True
loglevel = 'info'
timeout = 600
log_path = "/tmp/logs/flask_test"
logconfig_dict = {
'version':1,
'disable_existing_loggers': False,
"root": {
"level": "INFO",
"handlers": ["console"]
},
'loggers':{
"gunicorn.error": {
"level": "INFO",# 打日誌的等級可以換的,下面的同理
"handlers": ["error_file"], # 對應下面的鍵
"propagate": 1,
"qualname": "gunicorn.error"
},
"gunicorn.access": {
"level": "INFO",
"handlers": ["access_file"],
"propagate": 0,
"qualname": "gunicorn.access"
}
},
'handlers':{
"error_file": {
"class": "logging.handlers.RotatingFileHandler",
'maxBytes': 1024*1024*20, # 20 MB
'backupCount': 10,
'encoding': 'utf8',
"formatter": "generic",
"filename": log_path + "/gunicorn.error.log"
},
"access_file": {
"class": "logging.handlers.RotatingFileHandler",
'maxBytes': 1024*1024*20,
'backupCount': 10,
'encoding': 'utf8',
"filename": log_path + "/gunicorn.access.log",
},
'console': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'formatter': 'generic',
},
},
'formatters':{
"generic": {
"format": "%(asctime)s [%(process)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s] %(message)s", # 打日誌的格式
"class": "logging.Formatter"
}
}
}
F. 文件配置啓動
使用以下命令來啓動:
gunicorn -c gunicorn_conf.py app:app
注意:在gunicorn 20.x版本中,如果配置文件中的logconfig_dict中不加以下的代碼:
"root": {
"level": "INFO",
"handlers": ["console"]
},
那麼在使用gunicorn來啓動(gunicorn -c gunicorn_conf.py app:app)的時候可能會報錯,報錯信息如下:
Error: Unable to configure root logger.
參考:https://github.com/benoitc/gunicorn/issues/2250
3. Supervisor
Supervisor是用Python開發的一套通用的進程管理程序,能將一個普通的命令行進程變爲後臺daemon,並監控進程狀態,異常退出時能自動重啓。
它是通過fork/exec的方式把這些被管理的進程當作supervisor的子進程來啓動,這樣只要在supervisor的配置文件中,把要管理的進程的可執行文件的路徑寫進去即可。也實現當子進程掛掉的時候,父進程可以準確獲取子進程掛掉的信息的,可以選擇是否自己啓動和報警。
supervisor還提供了一個功能,可以爲supervisord或者每個子進程,設置一個非root的user,這個user就可以管理它對應的進程。
A. 安裝
有多種方式:
yum install supervisor
pip install supervisor
easy_install supervisor
安裝好了之後,默認的配置文件是在: /etc/supervisord.conf
而子進程配置目錄 /etc/supervisord.d/
B. 配置文件說明
配置文件說明
[unix_http_server]
file=/tmp/supervisor.sock ;UNIX socket 文件,supervisorctl 會使用
;chmod=0700 ;socket文件的mode,默認是0700
;chown=nobody:nogroup ;socket文件的owner,格式:uid:gid
;[inet_http_server] ;HTTP服務器,提供web管理界面
;port=127.0.0.1:9001 ;Web管理後臺運行的IP和端口,如果開放到公網,需要注意安全性
;username=user ;登錄管理後臺的用戶名
;password=123 ;登錄管理後臺的密碼
[supervisord]
logfile=/tmp/supervisord.log ;日誌文件,默認是 $CWD/supervisord.log
logfile_maxbytes=50MB ;日誌文件大小,超出會rotate,默認 50MB,如果設成0,表示不限制大小
logfile_backups=10 ;日誌文件保留備份數量默認10,設爲0表示不備份
loglevel=info ;日誌級別,默認info,其它: debug,warn,trace
pidfile=/tmp/supervisord.pid ;pid 文件
nodaemon=false ;是否在前臺啓動,默認是false,即以 daemon 的方式啓動
minfds=1024 ;可以打開的文件描述符的最小值,默認 1024
minprocs=200 ;可以打開的進程數的最小值,默認 200
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ;通過UNIX socket連接supervisord,路徑與unix_http_server部分的file一致
;serverurl=http://127.0.0.1:9001 ; 通過HTTP的方式連接supervisord
; [program:xx]是被管理的進程配置參數,xx是進程的名稱
[program:xx]
command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run ; 程序啓動命令
autostart=true ; 在supervisord啓動的時候也自動啓動
startsecs=10 ; 啓動10秒後沒有異常退出,就表示進程正常啓動了,默認爲1秒
autorestart=true ; 程序退出後自動重啓,可選值:[unexpected,true,false],默認爲unexpected,表示進程意外殺死後才重啓
startretries=3 ; 啓動失敗自動重試次數,默認是3
user=tomcat ; 用哪個用戶啓動進程,默認是root
priority=999 ; 進程啓動優先級,默認999,值小的優先啓動
redirect_stderr=true ; 把stderr重定向到stdout,默認false
stdout_logfile_maxbytes=20MB ; stdout 日誌文件大小,默認50MB
stdout_logfile_backups = 20 ; stdout 日誌文件備份數,默認是10
; stdout 日誌文件,需要注意當指定目錄不存在時無法正常啓動,所以需要手動創建目錄(supervisord 會自動創建日誌文件)
stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.out
stopasgroup=false ;默認爲false,進程被殺死時,是否向這個進程組發送stop信號,包括子進程
killasgroup=false ;默認爲false,向進程組發送kill信號,包括子進程
;包含其它配置文件
[include]
files = supervisord.d/*.ini ;可以指定一個或多個以.ini結束的配置文件
常用的子進程配置(supervisord.d/*.ini)如下:
user=root:用哪個用戶啓動進程,默認是root
command:程序啓動命令
environment=ENV="dev":設置環境變量的
directory=/var/www/html/flask_test/:進程運行前,會前切換到這個目錄
autorestart=true ; 程序退出後自動重啓,可選值:[unexpected,true,false]
redirect_stderr=true ; 把stderr重定向到stdout,默認false
stdout_logfile=/tmp/logs/flask_test/stdout.log ; stdout 日誌文件,需要注意當指定目錄不存在時無法正常啓動,所以需要手動創建目錄(supervisord 會自動創建日誌文件)
stdout_logfile_maxbytes=50MB ; stdout 日誌文件大小,默認50MB
stdout_logfile_backups = 20 ; stdout 日誌文件備份數,默認是10
C. 創建子進程配置
創建子進程配置實例
vim /etc/supervisord.d/flask_test.ini
[program:flask_test]
user=root
environment=ENV="dev"
directory=/var/www/html/flask_test/
command=/usr/local/python3/bin/gunicorn app:app -c ./supervisor/gunicorn_conf.py
autorestart=true
redirect_stderr=true
stdout_logfile=/tmp/logs/flask_test/stdout.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
相當於執行
export ENV="dev"
gunicorn -c app:app /var/www/html/flask_test/supervisor/gunicorn_conf.py
注意:command中需要使用絕對路徑,直接寫gunicorn有可能會報can’t find command ‘gunicorn’,可以通過whereis gunicorn來獲取路徑
注意:啓動命令的話不同框架的啓動命令不一致,Flask一般是 :
gunicorn app:app -c ./supervisor/gunicorn_conf.py
而Django一般是:
gunicorn 項目名.wsgi:application -c ./supervisor/gunicorn_conf.py
注意:如果這裏的user不爲root的話,那麼對應的日誌目錄的權限也需要加上該user的權限,否則會報無權限
另外,如果該項目有celery的話,還需要再配置兩個program:
vim /etc/supervisord.d/flask_test.ini
[program: flask_test-celery-work]
user=root
environment=ENV="dev"
directory=/var/www/html/flask_test/
command=/usr/local/python3/bin/celery -A flask_test worker -l info
autorestart=true
redirect_stderr=true
stdout_logfile=/tmp/logs/flask_test/celery-work.stdout.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
[program: flask_test-celery-beat]
user=root
environment=ENV="dev"
directory=/var/www/html/flask_test/
command=/usr/local/python3/bin/celery -A flask_test beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
autorestart=true
redirect_stderr=true
stdout_logfile=/tmp/logs/flask_test/celery-beat.stdout.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
注意:celery同樣是需要絕對路徑的,可以通過whereis celery來獲取
注意:子進程的配置文件,同樣也是需要放在項目根目錄下的,我這裏的做法是放在項目根目錄下的supervisor目錄下,該目錄下一般會有三個文件,gunicorn.py、dev.conf、prod.conf,其中dev.conf和prod.conf跟/etc/supervisord.d/xxx.ini的一樣的,主要是不同的環境放不同的配置文件而已
即:
一般的Python項目,根目錄下會新增一個目錄(我這裏是supervisor目錄),用來放置gunicorn和supervisord的配置文件
裏面至少有:
- dev.conf:測試環境下對應 /ect/supervisord.d/*.ini
- prod.conf:生產環境下對應 /ect/supervisord.d/*.ini
- gunicorn_conf.py:gunicorn配置文件
D. 啓動和開機重啓
啓動和開機啓動
systemctl start supervisord.service //啓動supervisor並加載默認配置文件
systemctl enable supervisord.service //將supervisor加入開機啓動項
如果已經是啓動supervisord,只是需要新增子進程項目的話,那麼可以使用以下命令進行加載:
supervisorctl reload
補充:
相關命令
supervisorctl status //查看所有進程的狀態
supervisorctl stop es //停止es
supervisorctl start es //啓動es
supervisorctl restart es //重啓es
supervisorctl start all //啓動全部
supervisorctl stop all //停止全部
supervisorctl restart all //重啓全部
supervisorctl update //配置文件修改後使用該命令加載新的配置
supervisorctl reload //重新啓動配置中的所有程序
注意:命令行直接輸入supervisorctl即可進入supervisorctl的shell交互界面,此時上面的命令不帶supervisorctl可直接使用
E. 掛載Nginx
上面已經能夠通過 IP:9100 進行訪問了,但在線上環境中,爲了更加穩定安全,一般會在前面加一層Nginx
Nginx配置如下:
vim /etc/nginx/conf.d/flask.test.com.conf
server {
server_name flask.test.com;
listen 80;
location / {
proxy_pass http://127.0.0.1:9100;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
保存之後通過以下命令來驗證新的Nginx配置是否有問題:
nginx -t
然後重啓nginx:
systemctl restart nginx
# 或者是nginx -s reload也可以
本地設置host即可訪問flask.test.com/demo