# 部署
在生产环境中,不应该再使用 flask 自带的 werkzueg 进行服务的部署,而应该要使用性能更好的 web 服务器。在模板中默认使用 gunicorn 来进行服务的部署。
注意,对于 WSGI web server,模板设立了 STRONG 的等级,虽然还有其他很多可选项,如 uwsgi,但都不做推荐。
这是因为后续考虑到要使用链路追踪 (opens new window),为了能够统一的对服务器软件进行追踪,必须要统一 web 服务器的选型。
# 启动命令
由于当前服务的启动命令比较简单,因此被直接写在 Dockerfile 中,命令如下:
gunicorn -c deploy/gunicorn_conf.py
WARNING
这个命令不能修改,也不能包装成一个 bash 脚本,入口必须是一个 python 程序。
因为基础镜像中添加了对 CMD 的增强 (opens new window)以进行链路追踪,要求以 python 程序的方式进行启动。
主要注意的是,如果在本机直接运行可能会报错,需要再运行前指定一下下面两个环境变量。
export DG_SDK_RDB_AUTO_INIT=False
export DG_SDK_RDB_APP_CTX_TYPE=flask
镜像中已经设置好了这两个变量
如果还是启动失败,并显示类似下面的报错:
ImportError: 没有安装 loguru 或者 dglogging,无法对日志进行持久化,请选择安装上述两个包,或者手动修改 LOG_DICT 的内容
那是因为在正式环境需要 loguru
或者 dg-logging
对日志进行持久化,请将其中之一添加到项目的依赖中,详见 logger handler。
# gunicorn_conf.py
deploy 文件夹的 gunicorn_conf.py
文件预置了一些 gunicorn 的配置 (opens new window)。
开发者可以对 gunicorn 配置进行调整,但有几项请保持现状,否则可能会存在问题。
bind
: 默认跑在容器 5000 端口上,内部端口号最好不变,可以修改映射出来的端口号。worker_class
: worker 尽量使用 sync,gevent 的 worker 有着各种各样蜜汁问题。我这里也有一个简单的 benchmark 来展示到底 gevent worker 提升有没有那么的巨大;logconfig_dict
: 对于 gunicorn 日志的配置尽量保持原样,请参考gunicorn_logging.py来获得更多信息;
# gunicorn_logging.py
gunicorn_logging.py 是一个专门用来配置 logger 的文件。
该文件一共配置了三个logger,分别是:
gunicorn.access
: gunicorn 的 access 日志,会记录所有的客户端的请求信息gunicorn.error
: gunicorn 的运行日志,会记录 worker 的一些状态,WSGI 的一些错误等等{flask-app-name}
: 名字为 Flaskimport_name
的 logger,和Flask.logger
是同一个logger,同时也是logger_process.py
中装配的 logger,开发者可以用来打印一切业务日志
该文件主要是对这三个 logger 的 handler 和 formatter 进行了配置。也正因为如此,在 gunicorn_conf.py
中不要再出现和 logging 相关的其他配置 (opens new window)了,直接修改本文件就行。
# gunicorn_conf_loader.py
这个文件允许在 gunicorn 启动时从 flask-boot 的统一配置
中加载额外的配置项。
如此一来,项目上只需要挂载 app.yaml 文件就可以对 gunicorn 行为进行控制,更加方便!
注意,这个文件会默认读取 app.yaml
下的 gunicorn
section,如:
gunicorn:
timeout: 60
reload: true
# 也可以使用统一配置从环境变量注入的功能
max_requests: ${G_MAX_REQUESTS:100}
flask:
...
还请注意,不是所有配置项都支持通过统一配置进行注入的。
由于日志收集 是 STRONG 级别的约束,gunicorn logging 的配置不支持通过统一配置进行修改,目前只通过 gunicorn_logging.py
文件进行控制。
另外,gunicorn 的一些 hook 配置是直接定义 python 函数的,这部分的配置也无法直接通过配置文件进行注入,比如以下几个 hook:
def on_starting(server):
pass
def on_reload(server):
pass
def when_ready(server):
pass
def pre_fork(server, worker):
pass
def post_fork(server, worker):
pass
def post_worker_init(worker):
pass
def worker_int(worker):
pass
def worker_abort(worker):
pass
def pre_exec(server):
pass
def pre_request(worker, req):
worker.log.debug("%s %s" % (req.method, req.path))
def post_request(worker, req, environ, resp):
pass
def child_exit(server, worker):
pass
def worker_exit(server, worker):
pass
def nworkers_changed(server, new_value, old_value):
pass
def on_exit(server):
pass
如果需要使用上述 Hook 对 gunicorn 进行高级控制,请直接修改 gunicorn_conf.py
文件。
# 其他文件
deploy 下可以放一切和部署相关的文件,比如执行数据库迁移脚本、启动其他 sidecar 服务。
建议这些脚本都通过基础镜像s6-overlay进行启动,详见基础镜像s6-overlay文档 (opens new window)。