menu E4b9a6's blog
rss_feed
E4b9a6's blog
有善始者实繁,能克终者盖寡。

Linux下生产环境部署Flask应用实践

作者:E4b9a6, 创建:2019-07-13, 字数:4648, 已阅:59, 最后更新:2019-07-13

这篇文章更新于 1958 天前,文中部分信息可能失效,请自行甄别无效内容。

博客的后端程序是用Flask写的,之前是采用Gunicorn+Nginx进行部署的,最近打算换成uwsgi的方式

1. 基础概念

1.1. HTTP协议

HTTP是超文本传输协议(英语:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议

http协议是基本的协议之一,底层使用TCP协议,通过浏览器等工具,发起一个HTTP请求到指定服务器的指定端口,一旦收到请求,服务器会立刻返回一个状态,这就是HTTP协议

关于HTTP协议的更多细节原理可以参考超文本传输协议 - 维基百科

1.2. WSGI协议

WSGI是Web服务器网关接口(Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口,自从WSGI被开发出来以后,许多其它语言中也出现了类似接口

WSGI是基于现存的CGI标准而设计的,WSGI其实是一种中间件,实现了API交互双方所需要的数据规范,因此可以在WSGI服务器与WSGI应用起调节作用

用Python语言写一个符合WSGI规范的Hello world程序如下所示

Python
def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    yield "Hello world!\n"

关于WSGI协议的更多底层细节可以参考Web服务器网关接口 - 维基百科

1.3. 开发与生产环境

无论是Django还是Flask,他们都自带一个实现WSGI协议简易Server以方便开发者专心写Web服务

这个内置服务器并不是为了在生产环境中运行而设计的,它的性能和稳定性可能无法满足生产环境的要求

因此,开发服务器在生产环境中不被推荐使用

在调试开发时会有相应的提示(WARNING: This is a development server. Do not use it in a production deployment.)

与开发环境相对比,生产环境部署要注意以下的点

  • 与高性能的Web服务器对接
  • 更高的并发性能要求
  • 更高的安全保障要求
  • 灾备与容错处理
  • 日志与监控

这些都是在开发环境阶段无需考虑的,在部署到生产环境之后,每一次请求中断或请求响应缓慢都可能对产品造成不可估量的伤害

所以需要选择更有保障的方案

2. 实践

Web服务器采用Nginx,操作系统是Ubuntu1804 LTS

这里介绍2个wsgi应用gunicornuwsgi,任选其一

2.1. Flask应用

为了测试方便,这里创建一个最小的Flask Web App

编辑app.py

Python
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'>Hello There!</h1>"

if __name__ == "__main__":
    app.run()

尝试启动下应用python app.py

Bash
* Serving Flask app "hello" (lazy loading)
* Environment: production
    WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

访问下 http://127.0.0.1:5000,可以看到蓝色的Hello world标题

这就是Flask自带的简易WSGI环境

2.2. gunicorn

Gunicorn(Green Unicorn)是一个用于运行Python web应用程序的WSGI(Web Server Gateway Interface)HTTP服务器,他的优点包括

  • 高性能
  • 多进程管理
  • 热加载
  • 负载均衡
  • 配置灵活
  • 安全稳定

我们使用gunicorn来部署一下Flask应用,首先安装gunicorn

Bash
pip install gunicorn

使用gunicorn运行app.py

Bash
# app:app,前一个app是指app.py的文件名,后一个app是指代码中的app变量
gunicorn --bind 127.0.0.1:5000 app:app

访问http://127.0.0.1:5000可以看到蓝色的Hello World标题

最后将这个程序注册为systemd后台程序,编辑 /etc/systemd/system/gunicorn.service

INI
[Unit]
After=network.target

[Service]
User=apps
Group=apps
# 程序目录
WorkingDirectory=flaskr 
# 程序环境变量,如设置了venv则需要填写此项
Environment="PATH=flaskr/venv/bin" 
 # 启动命令,这里不再暴露端口,采用sock文件形式来与web网关交互
ExecStart=flaskr/venv/bin/gunicorn --workers 1 --bind unix:flask.sock -m 007 app:app

[Install]
WantedBy=multi-user.target

启动gunicorn后台进程并设置开机自启

Bash
sudo systemctl daemon-reload
sudo systemctl start gunicorn
sudo systemctl enable gunicorn

systemd是Linux计算机操作系统之下的一套中央化系统及设置管理程序(init),包括有守护进程、程序库以及应用软件,由Lennart Poettering带头开发

2.3. Uwsgi

UWSGI是一个用于运行和部署Web应用程序的通用服务器软件,它最初是为Python编写的,但现在已经扩展到支持多种编程语言和Web框架,他的优点包括

  • 高性能
  • 负载均衡
  • 缓存和加速
  • 安全稳定
  • 配置灵活

gunicorn相比,uwsgi通用性更强,性能更强,更多配置选项

安装uwsgi

Bash
pip install uwsgi

编辑uwsgi.ini文件

INI
[uwsgi]
module = app:app
master = true
processes = 1
socket = myapp.sock
chmod-socket = 660
vacuum = true
die-on-term = true

启动uwsgi

Bash
uwsgi uwsgi.ini

同样的,我们可以将其注册为systemd程序,编辑 /etc/systemd/system/uwsgi.service

INI
[Unit]
After=network.target

[Service]
User=apps
Group=apps
# 程序目录
WorkingDirectory=flaskr 
# 程序环境变量,如设置了venv则需要填写此项
Environment="PATH=flaskr/venv/bin" 
 # 启动命令
ExecStart=flaskr/venv/bin/uwsgi uwsgi.ini

[Install]
WantedBy=multi-user.target
Bash
sudo systemctl daemon-reload
sudo systemctl start uwsgi
sudo systemctl enable uwsgi

3. Nginx

安装Nginx

Bash
sudo apt install nginx

设置开机启动

Bash
sudo systemctl start nginx
sudo systemctl enable nginx

Nginx的默认配置文件位于 /etc/nginx/nginx.conf,此外,一般还会默认导入/etc/nginx/conf.d/的所有conf配置文件,用户一般编辑conf.d下的文件即可

编辑/etc/nginx/conf.d/uwsgi.conf,一个简单的示例如下

TEXT
server {
    listen 80;

    access_log  /var/log/nginx/flaskr-access.log;
    error_log   /var/log/nginx/flaskr-error.log;

    location / {
        include uwsgi_params;
        uwsgi_param    Host             $host;
        uwsgi_param    X-Real-IP        $remote_addr;
        uwsgi_param    X-Forwarded-For  $proxy_add_x_forwarded_for;
        uwsgi_param    HTTP_X_FORWARDED_FOR $remote_addr;
        uwsgi_pass_request_headers on;
        uwsgi_pass unix:uwsgi.sock;
    }
}

[[replyMessage== null?"发表评论":"发表评论 @ " + replyMessage.m_author]]

account_circle
email
web_asset
textsms

评论列表([[messageResponse.total]])

还没有可以显示的留言...
gravatar
[[messageItem.m_author]] [[messageItem.m_author]]
[[messageItem.create_time]]
[[getEnviron(messageItem.m_environ)]]
[[subMessage.m_author]] [[subMessage.m_author]] @ [[subMessage.parent_message.m_author]] [[subMessage.parent_message.m_author]]
[[subMessage.create_time]]
[[getEnviron(messageItem.m_environ)]]