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文件,就可以访问网站了。可是实际情景中需要处理高并发访问的情况,我们使用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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容