部署flask应用的全部流程和相关配置
Python Web 部署结构
对于生产环境,部署Python Web应用基本都要用到 Web server 和WSGI server。那它们之间是如何联系的呢?如下图所示:
这里Web Server用于接受客户端的http请求,然后返回处理后的结果。对于Python Web应用目前主流的是Nginx, 其占有内存少,并发能力强,在同类型的网页服务器中表现较好。
WSGI全称是Python Web Server Gateway Interface,用来连接Web服务器和Python Web开发框架。目前常用的有uWSGI, gunicorn。
Web应用大都基于框架完成,对于python,主流框架有Django, Flask, Tornado,主要用于业务层逻辑的实现,并不关注http层的具体内容。
对于部署Flask应用,比较推荐的方式是:Nginx + Gunicorn + Flask, 下面具体介绍。
Gunicorn
Gunicorn是一个被广泛使用的高性能的Python WSGI HTTP服务器,只支持UNIX环境。Gunicorn采用pre-fork worker模式,能够与各种wsgi web框架协作。
那为何需要gunicorn呢?首先,如果使用Flask自带的WSGI, 由于是单进程运行,其处理请求的能力有限,一般作为测试使用。而gunicorn也支持WSGI服务,同时由于pre-fork worker的工作模式,启动后会fork出指定数量的应用进程,多进程处理请求的数量肯定比单进程多。另外gunicorn接受nginx转发的http请求,收到处理后的响应后转发给nginx。
1) 安装
pip install gunicorn # 安装最新版本
pip install gunicorn==19.9.0 # 安装指定版本
# 如果异步,需要安装对应模块
pip install eventlet # 使用eventlet工作模式
pip install gevent # gevent工作模式
2) 运行gunicorn
gunicorn [options] main_app:app
main_app是flask应用主模块main_app.py,app是应用实例名称, 需要和代码里的app实例命名一致。
options是参数,常用的有:
- -k/--worker-class: 指定工作模式,可选参数:
- -w/--workers:指定应用进程数量, 默认为1
- -b/--bind: 指定客户端地址,格式 HOST:PORT, 如 -b 127.0.0.1:8000
- -t/--threads: 每个进程处理请求的线程数,默认每个worker 一个线程,指定threads工作模式自动变成gthread模式;
- --worker-connections: 工作线程连接数,默认1000,仅在eventlet, gevent工作模式下有效
- -t/--timeout 超时限制,默认是30秒,工作进程在超过设置的超时时间内没有响应将会被杀死并重启。
- --reload=True, 当代码变动时重启gunicorn
- -D,--daemon: 以后台进程的方式运行gunicorn
- --log-level:指定日志输出等级
- --access-logfile: 访问日志输出文件
- --error-logfile: 错误日志输出文件
注意:
a. 参数格式:-p value,--params=value
b. 工作模式指定为gevent或者evenlet时,threads参数无效
常用启动命令,下面main为应用入口模块main.py的名称
# 指定worker数量gunicorn -w 5 -b 127.0.0.1:8080 main:app
# 设置线程
gunicorn -w 5 -t 2 -b 127.0.0.1:8080 main:app
# 设置异步工作模式
gunicorn --worker-class=gevent \
--worker-connections=1000 \
-w 3 main:app
3)工作模式选择
- gunicorn默认是同步模式。该模式下,每个worker进程一次只处理一个请求,处理完成了才能处理下一个,在高并发场景,非常消耗CPU和内存。
- 异步有gevent和evenlet两种模式,异步模式一个worker进程可以同时处理多个请求,不会block其他请求。
- 如果没有阻塞响应的话,使用同步worker,如果阻塞比较多的话选用异步的模式;worker的数量推荐使用(2*CPU)+1。
4) 结束进程
首先使用:ps -ef | grep gunicorn 命令找出gunicorn的主进程ID
然后执行:kill -9 主进程ID,当主进程被Kill, 其他子进程也会自动结束,可以稍后再次执行查找命令确认。
Nginx部署
Nginx是一款轻量级的Web 服务器/反向代理服务器,其用途主要是:
- 做反向代理,客户端请求Nginx,Nginx请求应用服务器,然后将结果返回给客户端
- 作负载均衡,将大量用户请求分配给多台机器处理
- 动静分离,静态请求直接从 nginx 服务器所设定的根路径去取对应的资源,动态请求则转发给后台处理
- 做虚拟主机,将多个网站部署到一台服务器上
1)安装
apt install nginx # ubuntu
yum install nginx # Centos
2)启动/停止Nginx
sudo nginx # 启动
sudo nginx -s reload # 热启动
sudo nginx -s stop # 停止
sudo pkill -9 nginx # 强制停止
3)查看nginx运行状态
运行:systemctl status nginx,如下图为running状态:
4)Nginx配置
以Ubuntu20.04为例,nginx配置在目录/etc/nginx下,如下图:
sites-available为有效站点配置,默认有一个default配置;
sites-enabled是当前使用的站点配置,实际里面是软连接,指向sites-available里的配置。下面举例演示如何配置。
首先,在site-available目录下,拷贝default为flask_demo,然后修改flask_demo配置,如下图:
server {
listen 9000 default_server;
listen [::]:9000 default_server;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
proxy_pass http://127.0.0.1:9001;
}
一个server配置对应一个站点,这里nginx做反向代理,9000是Nginx的端口,proxy_pass是gunicorn服务的绑定地址和端口。
然后进入到sites-enabled目录创建配置软连接:
sudo ln -s /etc/nginx/sites-available/flask_demo flask_demo
下面测试一个简单的flask应用:
模块名称app.py, 内容如下:
from flask import Flask
app = Flask(__name__)
def index():
return "Testing Flask App Deployment"
启动gunicorn:
gunicorn -w 5 -b 127.0.0.1:9001 app:app
在浏览器搜索目标地址,这里192.168.56.106是当前测试虚拟机的IP地址,9000是刚才配置的Nginx端口,内容如下:
5)配置负载均衡
为了分散单台服务器的访问压力或者将业务拆分为微服务,一般采用服务器集群的部署方式。但是如果一个服务器对应一个域名,外部访问肯定不方便,怎么办呢?Nginx负载均衡就可以解决这个问题,如下图为负载均衡结构示意图:
将不同的服务器地址添加到Nginx服务器的配置中,然后做反向代理。对于外部用户,是感知不到后端多个服务器的,访问的域名并没有改变。
另外,负载均衡还可以用于配置主从服务器,当主机出现故障,nginx会将请求发送给备份服务器处理。
下面是实现负载均衡的一个简单示例:
注:由于资源有限,还是在单机上测试,实际每个应用部署在单独的虚拟机或者docker中
- 首先拷贝两份flask应用, 重命名并修改app.py文件的URL地址和返回信息,以方便对比;
- 用gunicorn分别启动这两个新增应用,地址还是127.0.0.1,端口分别用9002, 9003;
- 打开配置文件:sudo vim /etc/nginx/sites-available/flask_demo
- 将应用服务的地址添加到 "upstream domain"配置项,并修改location中的proxy_pass内容, 如下代码所示:
upstream domain {
server localhost:9001;
server localhost:9002;
server localhost:9003;
}
server {
listen 9000 default_server;
listen [::]:9000 default_server;
# 此处省略原注释配置项若干行
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
proxy_pass http://domain;
proxy_set_header Host $host:$server_port;
}
- 保存无误后执行:sudo nginx -s reload,然后在浏览器测试不同服务的URL,结果如下:
测试根路径:
测试 /app1:
测试 /app2:
从内容来看,地址端口都没有变,不同的URL代表不同的服务;反应在后端,实际访问的是不同服务器上的应用,也就实现了负载均衡。
总结
通过上面的内容和示例,我们知道了部署flask应用的全部流程和相关配置。而实际项目中,配置要更加复杂,这里只是入门分享。如果要深入掌握原理,建议查看官方文档或其他专题内容...