Python Web 騰訊云部署:flask+fabric+gunicorn+nginx+supervisor

最近看了《Flask Web開發--基于Python的Web應用開發實戰》,還有廖雪峰老師的Python教程,前者是基于Web框架flask進行開發,后者是自己搭建了一個Web框架。Web框架致力于如何生成HTML代碼,而Web服務器用于處理和響應HTTP請求。Web框架和Web服務器之間的通信,需要一套雙方都遵守的接口協議。WSGI協議就是用來統一這兩者的接口的。

為了部署一個完整的環境,本文將使用flask作為Web框架,Gunicorn作為WSGI容器,Nginx作為Web服務器,同時使用Supervisor進行進程管理,并使用Fabric實現自動化部署。


前期準備

申請一個騰訊云服務器Ubuntu Server 16.04.1 LTS 64位:

賬戶:ubuntu
密碼:**********
內網ip:172.16.0.17
外網ip:139.199.184.183


使用《Flask Web開發》源碼:

$ mkdir web
$ cd web
~/web$ git clone https://github.com/miguelgrinberg/flasky.git

設置并進入虛擬環境:

~/web$ cd flasky
~/web/flasky$ virtualenv venv
~/web/flasky$ source venv/bin/activate

安裝對應的庫:

(venv) ~/web/flasky$ cd requirements
(venv) ~/flasky/requirements$ pip3 install -r common.txt
(venv) ~/flasky/requirements$ pip3 install -r dev.txt
(venv) ~/flasky/requirements$ pip3 install -r heroku.txt
(venv) ~/flasky/requirements$ pip3 install -r prod.txt

創建數據庫:

(venv) ~/flasky/requirements$ cd ..
(venv) ~/flasky/$ python3 manage.py deploy

Fabric

Fabric入門看這里

新開一個終端,遠程登陸服務器:

$ ssh ubuntu@139.199.184.183

在服務器端定義好部署結構:

ubuntu@VM $ mkdir web
ubuntu@VM $ mkdir web/flasky
ubuntu@VM $ mkdir web/log

即:

  • web/    Web App根目錄
    • flasky/  存放python文件
    • log/    存放日志文件

我們使用Fabric將代碼上傳到服務器,首先,安裝Fabric:

(venv) ~/web$ sudo apt-get install fabric

在web目錄下創建fabfile.py(必須這個名字),寫入:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import os, re
from datetime import datetime

# 導入Fabric API:
from fabric.api import *

# 服務器登錄用戶名:
env.user = 'ubuntu'
# sudo用戶為root:
env.sudo_user = 'root'
# 服務器地址,可以有多個,依次部署:
env.hosts = ['139.199.184.183']

_TAR_FILE = 'dist-flasky.tar.gz'

def build():
    includes = ['app', 'migrations', 'requirements', 'tests', 'venv', *.py', '*.sqlite']
    excludes = ['.*', '*.pyc', '*.pyo']
    local('rm -f dist/%s' % _TAR_FILE)
    with lcd(os.path.join(os.path.abspath('.'), 'flasky')):
        cmd = ['tar', '--dereference', '-czvf', '../dist/%s' % _TAR_FILE]
        cmd.extend(['--exclude=\'%s\'' % ex for ex in excludes])
        cmd.extend(includes)
        local(' '.join(cmd))
        
        
_REMOTE_TMP_TAR = '/tmp/%s' % _TAR_FILE
_REMOTE_BASE_DIR = '/home/ubuntu/web/flasky'

def deploy():
    newdir = 'flask-%s' % datetime.now().strftime('%y-%m-%d_%H.%M.%S')
    # 刪除已有的tar文件:
    run('rm -f %s' % _REMOTE_TMP_TAR)
    # 上傳新的tar文件:
    put('dist/%s' % _TAR_FILE, _REMOTE_TMP_TAR)
    # 創建新目錄:
    with cd(_REMOTE_BASE_DIR):
        sudo('mkdir %s' % newdir)
    # 解壓到新目錄:
    with cd('%s/%s' % (_REMOTE_BASE_DIR, newdir)):
        sudo('tar -xzvf %s' % _REMOTE_TMP_TAR)
    # 重置軟鏈接:
    with cd(_REMOTE_BASE_DIR):
        sudo('rm -f flask')
        sudo('ln -s %s flask' % newdir)
        sudo('chown ubuntu:ubuntu flask')
        sudo('chown -R ubuntu:ubuntu %s' % newdir)
    # 重啟Python服務和nginx服務器:
    #with settings(warn_only=True):
    #    sudo('supervisorctl stop flask')
    #    sudo('supervisorctl start flask')
    #    sudo('/etc/init.d/nginx reload')

使用fab命令將代碼打包:

(venv) ~/web$ mkdir dist
(venv) ~/web$ fab build

將代碼傳上服務器:

(venv) ~/web$ fab deploy

Gunicorn部署

gunicorn入門看這里

代碼上傳給服務器后,可以直接運行manage.py文件,就可以訪問網站了??墒菍嶋H情景中需要處理高并發訪問的情況,我們使用Gunicorn來解決這個問題。

在服務器上進入flask所在目錄,激活虛擬環境,并安裝gunicorn:

ubuntu@VM ~/web/flasky/flask$ source venv/bin/activate
(venv) ubuntu@VM ~/web/flasky/flask$  sudo apt-get install gunicorn

使用gunicorn運行web應用(支持4個并發請求):

(venv) ubuntu@VM ~/web/flasky/flask$ gunicorn -w 4 -b 172.16.0.17:5000 manage:app

其中172.16.0.17是服務器內網ip,這樣設置后就可以通過外網ip訪問網站了。在瀏覽器地址欄中輸入139.199.184.183:5000,就可以看到網站了。


Nginx

Nginx入門看這里

使用gunicorn的話,需要將端口暴露給外界,這不利于服務器的安全問題。Nginx是一個高性能的HTTP和反向代理服務器,通過配置一個代理服務器,可以實現反向代理的功能。所謂反向代理,是指一個請求,從互聯網過來,先進入代理服務器,再由代理服務器轉發給局域網的目標服務器。

先安裝nginx:

(venv) ubuntu@VM ~$ sudo apt-get install nginx

設置反向代理服務器:在服務器,進入目錄/etc/nginx/sites-available/,在目錄下的default文件寫入一個server。

首先,先備份default文件:

$ cd /etc/nginx/sites-available/
$ sudo cp default default-backup

在default文件中寫入server:

server {
    listen 80;
    root /home/ubuntu/web/flasky/flask/;
    
    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
    }
}

配置完成后,重啟nginx:

$ sudo nginx -s reload

此時,該反向代理服務器就開始監聽80端口。80端口是為HTTP開放的端口,瀏覽網頁服務默認的端口號都是80 (8080端口跟80端口一樣)。只要訪問服務器,代理服務器就會將請求轉發到127.0.0.1:5000.

回到目錄/home/ubuntu/web/flasky/flask/下,再次使用gunicorn運行web app:

(venv) ubuntu@VM ~/web/flasky/flask$ gunicorn -w 4 -b 127.0.0.1:5000 manage:app

用的ip地址是127.0.0.1

而外界要訪問網站只需要在瀏覽器地址欄輸入139.199.184.183,使用默認端口80。這樣web app實際使用的5000就不用暴露了。


Supervisor

supervisor入門看這里

如果你需要進程一直執行,若該進程因各種原因中斷,也會自動重啟的話,supervisor是一個很好的選擇。

supervisor管理進程,是通過fork/exec的方式將這些被管理的進程當作supervisor的子進程來啟動,所以我們只需要將要管理進程的可執行文件的路徑添加到supervisor的配置文件中就好了。此時被管理進程被視為supervisor的子進程,若該子進程異常終端,則父進程可以準確的獲取子進程異常終端的信息,通過在配置文件中設置autostart=true,可以實現對異常中斷的子進程的自動重啟。

安裝supervisor:

(venv) ubuntu@VM ~$ sudo apt-get install supervisor

在目錄/etc/supervisor/conf.d/下創建flask.conf,并加入:

;/etc/supervisor/conf.d/flask.conf

[program:flask]

command     = /home/ubuntu/.local/bin/gunicorn -w 4 -b 127.0.0.1:5000 manage:app
;這里/home/ubuntu/.local/bin/是我服務器上gunicorn的安裝目錄
directory   = /home/ubuntu/web/flasky/flask
user        = ubuntu
startsecs   = 3
autostart   = true

redirect_stderr         = true
stdout_logfile_maxbytes = 50MB
stdout_logfile_backups  = 10
stdout_logfile          = /home/ubuntu/web/flasky/log/app.log

配置完后,進入目錄/home/ubuntu/web/flasky/, 創建log目錄:

(venv) ubuntu@VM ~/web/flasky$ mkdir log

啟動supervisor:

(venv) ubuntu@VM /etc/supervisor$ sudo supervisord -c supervisor.conf                 

執行服務(運行app.py):

(venv) ubuntu@VM $ sudo supervisorctl start flask

此時,進程flask運行,執行command后邊的語句,即/home/ubuntu/.local/bin/gunicorn -w 4 -b 127.0.0.1:5000 manage:app。

在瀏覽器地址欄輸入139.199.184.183,使用默認端口80,即可訪問網站。

如果supervisor遇到錯誤,可以在/var/log/supervisor/supervisord.log中查看日志;
如果app運行出現問題,可以在 /home/ubuntu/web/flasky/log/app.log中查看日志。


自動化部署

為了實現自動化部署,將上邊fabfile.py文件的最后四個語句的注釋取消,就能在將修改后的代碼傳上服務器后,用supervisor自動重載配置并重啟flask進程。之后便可以直接訪問網站。
因此,每次更改代碼后,只需要執行以下兩個語句,無需登陸服務器,便可以啟動網站:

$ fab build
$ fab deploy

gunicorn配置文件

gunicorn的配置除了可以在命令行中設置,也可以使用配置文件實現更復雜的配置:

$ gunicorn -c gunicorn.py manage:app

gunicorn.py文件如下:

# gunicorn.py
import logging
import logging.handlers
from logging.handlers import WatchedFileHandler
import os
import multiprocessing
bind = '127.0.0.1:5000'       #綁定ip和端口號
backlog = 512                 #監聽隊列
chdir = '/home/test/server/bin'   #gunicorn要切換到的目的工作目錄
timeout = 30      #超時
worker_class = 'gevent' #使用gevent模式,還可以使用sync 模式,默認的是sync模式
workers = multiprocessing.cpu_count() * 2 + 1    #進程數

threads = 2 #指定每個進程開啟的線程數

loglevel = 'info' #日志級別,這個日志級別指的是錯誤日志的級別,而訪問日志的級別無法設置
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"'     #設置gunicorn訪問日志格式,錯誤日志無法設置
"""
其每個選項的含義如下
        h           remote address
        l           '-'
        u           currently '-', may be user name in future releases
        t           date of the request
        r           status line (e.g. ``GET / HTTP/1.1``)
        s           status
        b           response length or '-'
        f           referer
        a           user agent
        T           request time in seconds
        D           request time in microseconds
        L           request time in decimal seconds
        p           process ID
        {Header}i   request header
        {Header}o   response header
"""
accesslog = "/home/ubutnu/web/flasky/log/gunicorn_access.log"      #訪問日志文件
errorlog = "/home/ubutnu/web/flasky/log/gunicorn_error.log"        #錯誤日志文件

在本地將gunicorn.py放在/home/ubutnu/web/flasky/flask/目錄下,在服務器端將/etc/supervisor/conf.d/flask.conf中的command改為:

command     = /home/ubuntu/.local/bin/gunicorn -c /home/ubutnu/web/flasky/flask/gunicorn.py manage:app

然后,在本地執行:

$ fab build
$ fab deploy

便可以訪問網站了。

web
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容