【摘要】
在自动化运维新手村中,我们已经依次讲解了Python的基础知识,函数与面向对象设计,使用了Flask框架作为Web应用的后端,与此同时还学习了数据库的相关知识,最终完成了一个以Flask为后端的资产管理服务。
到目前为止,如果大家可以充分理解并灵活应用所讲的知识点,就已经可以按照自己的实际需求,做出一些基本的运维工具,但如果需要对外提供运维能力,还需要最后一步,那就是将Flask后端部署上线。
【Flask启动】
在讲解Flask框架的第一章节提到,启动Flask可以直接运行如下代码:
if __name__ == "__main__":
app.run()
但启动之后的日志中会包含如下提示:
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
翻译过来的含义就是,当前使用的是开发模式下的服务器,请不要在生产环境使用它,而是要使用一个生成环境下的WSGI服务器
那到底什么是开发模式服务器,什么又是生产环境的WSGI服务器呢?
Web服务的组成
准确来说,一个Flask后端应用,并不等同于一个完整的Web服务,一个完整的Web服务如下图所示:
需要由一个Web服务器接收浏览器发出的HTTP请求,并经由WSGI标准接口与APP进行通信,APP处理完请求之后,再将响应经由WSGI处理,最终由Web服务器发送给前端。
Flask应用就是APP的角色,而Server通常会由另一个组件来实现,当通过app.run()
启动Flask应用时,其实是Flask内置了一个仅用于开发调试的低性能、简易的Server,这也是为什么不建议直接在生产环境使用app.run()
来部署Flask应用(不建议并不是不能)。
WSGI
那什么又是WSGI呢?
百度百科定义如下:
Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。
上文提到,Server需要由单独的组件来充当,那么Server在与APP交互过程中,就需要遵循一种规范,这个规范就是WSGI。
更为通俗的讲,充当WebServer角色的可以有很多组件;也有很多框架可以充当WebApp的角色,但只要它们双方都遵守WSGI规范,那么编程人员就可以用任意一个WebServer组件去和任意一种WebApp对接。
WSGI区分为两个部分:一为“服务器”或“网关”,另一为“应用程序”或“应用框架”。在处理一个WSGI请求时,服务器会为应用程序提供环境信息及一个回调函数(Callback Function)。当应用程序完成处理请求后,透过前述的回调函数,将结果回传给服务器。
所谓的WSGI中间件同时实现了API的两方,因此可以在WSGI服务器和WSGI应用之间起调解作用:从Web服务器的角度来说,中间件扮演应用程序,而从应用程序的角度来说,中间件扮演服务器。“中间件”组件可以执行以下功能:
1.重写环境变量后,根据目标URL,将请求消息路由到不同的应用对象。
2.允许在一个进程中同时运行多个应用程序或应用框架。
3.负载均衡和远程处理,通过在网络上转发请求和响应消息。
4.进行内容后处理。
Flask作为应用程序,它适配实现WSGI规范的源代码大致如下:
def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
ctx = self.request_context(environ)
error: t.Optional[BaseException] = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
这一章节暂时不对源码做深入解读,Flask源码会在后续的Flask进阶内容中进行讲解,感兴趣的读者可以先自行了解。
服务器/网关
目前能够充当Python HTTPServer角色的组件有很多,这一章节主要讲解Gunicorn,该服务器与各种Web框架兼容,实现非常简单,轻量级的资源消耗。可以直接用命令启动,不需要编写配置文件,相对其他的HTTP服务器要简单不少。
【Gunicorn + Flask部署】
部署过程均在Linux环境下进行
安装
# pip install gunicorn
Collecting gunicorn
Downloading gunicorn-20.1.0-py3-none-any.whl (79 kB)
|████████████████████████████████| 79 kB 539 kB/s
Requirement already satisfied: setuptools>=3.0 in ./venv/lib/python3.8/site-packages (from gunicorn) (59.7.0)
Installing collected packages: gunicorn
Successfully installed gunicorn-20.1.0
如果安装较慢可以加上清华源的后缀-i https://pypi.tuna.tsinghua.edu.cn/simple
全局配置
安装完gunicorn后无法直接通过命令行执行其二进制文件,如下:
# gunicorn -h
-bash: gunicorn: command not found
因为安装完成后gunicorn可执行文件会存在于python的bin/文件夹下,如果是使用的系统Python环境,则通常会存在于/usr/local/python3/bin/gunicorn
,如果是使用的Python的虚拟环境,则通常会存在于虚拟环境目录./venv/bin/gunicorn
。需要通过软链接将其链接到/usr/bin
目录下,如下:
# ln -s /usr/local/python/bin/gunicorn /usr/bin/gunicorn
设置完成后,执行如下命令确认:
# gunicorn -v
gunicorn (version 20.1.0)
启动Flask应用
将已经完成的Flask上传到Linux环境的机器下;
先执行python app.py
启动Flask应用,然后执行curl 127.0.0.1:5000/get
确认应用程序可以正常运行;
执行gunicorn app:app
,通过gunicorn启动Flask应用,默认会监听127.0.0.1:8000
,输出如下:
[2022-03-27 15:53:53 +0800] [76948] [INFO] Starting gunicorn 20.1.0
[2022-03-27 15:53:53 +0800] [76948] [INFO] Listening at: http://127.0.0.1:8000 (76948)
[2022-03-27 15:53:53 +0800] [76948] [INFO] Using worker: sync
[2022-03-27 15:53:53 +0800] [76954] [INFO] Booting worker with pid: 76954
[2022-03-27 15:53:55 +0800] [76948] [INFO] Handling signal: int
[2022-03-27 15:53:55 +0800] [76954] [INFO] Worker exiting (pid: 76954)
[2022-03-27 15:53:55 +0800] [76948] [INFO] Shutting down: Master
这时再次执行curl 127.0.0.1:8000/get
确认应用可以正常访问
基本配置
gunicorn可以配置一些额外参数,格式如下:
gunicorn -w 进程数量 -b 监听地址:监听端口 运行文件名称:Flask程序实例名
例如:
gunicorn -w 4 -b 0.0.0.0:8080 app:app -D
-D
表示将gunicorn置于后台运行
执行ps -ef | grep gunicorn
可以查看gunicorn进程信息
# ps -ef | grep gunicorn
501 79794 1 0 3:58PM ?? 0:00.04 PycharmProjects/flaskProject2/venv/bin/gunicorn -w 4 -b 0.0.0.0:8080 openapi:app -D
501 79806 79794 0 3:58PM ?? 0:00.51 PycharmProjects/flaskProject2/venv/bin/gunicorn -w 4 -b 0.0.0.0:8080 openapi:app -D
501 79808 79794 0 3:58PM ?? 0:00.51 PycharmProjects/flaskProject2/venv/bin/gunicorn -w 4 -b 0.0.0.0:8080 openapi:app -D
501 79809 79794 0 3:58PM ?? 0:00.50 PycharmProjects/flaskProject2/venv/bin/gunicorn -w 4 -b 0.0.0.0:8080 openapi:app -D
501 79810 79794 0 3:58PM ?? 0:00.50 PycharmProjects/flaskProject2/venv/bin/gunicorn -w 4 -b 0.0.0.0:8080 openapi:app -D
除此之外如果生产环境,必不可少还需要配置日志信息,如下:
gunicorn -w 4 -b 0.0.0.0:8080 --access-logfile access.log --error-logfile error.log app:app -D
可以通过tail -f access.log
或者tail -f error.log
查看记录的日志信息。
通用配置
执行pip install gevent
安装依赖包。
gunicorn可以通过执行配置文件来完成启动,配置文件如下
# gun.py
import logging
import logging.handlers
import os
import multiprocessing
import gevent.monkey
gevent.monkey.patch_all()
bind = '0.0.0.0:8080' # 绑定的ip已经端口号
chdir = '/home/flaskProject' # gunicorn要切换到的目的工作目录
timeout = 60 # 超时
worker_class = 'gevent' # 使用gevent模式,还可以使用sync 模式,默认的是sync模式
workers = multiprocessing.cpu_count() * 2 + 1 # 启动的进程数
loglevel = "info" # 日志级别,这个日志级别指的是错误日志的级别,而访问日志的级别无法设置
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' # 设置gunicorn访问日志格式,错误日志无法设置
pidfile = "gunicorn.pid"
accesslog = "access.log"
errorlog = "error.log"
daemon = True # 是否后台运行
执行gunicorn -c gun.py app:app
启动应用程序,
启动后项目的目录下会生成access.log
,error.log
和gunicorn.pid
三个文件,gunicorn.pid
中保存了gunicorn的主进程PID号,可以通过cat gunicorn.pid
查看,当想要停止gunicorn时,直接kill 进程号
即可杀死所有gunicorn进程。
【总结】
单纯的Flask 自带的Web服务器做下测试,在压力大的时候出现socket的问题,因为他是单进程单线程的。而使用gunicorn来启动,响应速度和能力提升显著。
配置中workers指定启动的进程数。cpu的损耗是平均到各个进程。workers的值一定不要过大,毕竟多进程对于系统的调度消耗比较大。
这一章节的结束,就标志着自动化运维新手村系列正式完结了,希望大家可以通过这一系列的学习,都可以快速上手Python,并结合自己的场景完成特定的自动化运维小工具,敬请期待自动化运维初级村的更多内容。
欢迎大家添加我的个人公众号【Python玩转自动化运维】加入读者交流群,获取更多干货内容