【Python】Flask、Django項目的部署【原創】

0. 參考


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直接服務掛掉


如下圖:

83g8wd.png

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。

如圖:

123

下面就以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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章