首页
/ Flask 生产部署:Gunicorn WSGI 服务器完整配置指南

Flask 生产部署:Gunicorn WSGI 服务器完整配置指南

2026-09-03 18:44:09作者:庞队千Virginia

Gunicorn(Green Unicorn)是一个纯 Python 实现的 WSGI 服务器,提供简单的配置方式和多种 worker 实现以进行性能调优。本文基于 Flask 官方部署文档 docs/deploying/gunicorn.rst,完整覆盖 Gunicorn 的安装、启动命令、worker 配置、外部绑定以及 gevent 异步 worker 的实战用法,并结合 Flask 仓库中的源码与示例应用,解释 Gunicorn 是如何加载 Flask 应用的、module:app 字符串约定背后的 WSGI 调用链,以及为什么生产环境应当将 Gunicorn 置于反向代理之后。

为什么选择 Gunicorn 作为 Flask 的 WSGI 服务器

Flask 官方部署文档(docs/deploying/index.rst)首先强调:开发服务器(debugger 与 reloader)仅适用于本地开发,不可用于生产环境。Flask 本身是一个 WSGI 应用(application),需要一个 WSGI 服务器(server)来运行它——WSGI 服务器负责把传入的 HTTP 请求转换为标准的 WSGI environ,并把应用返回的 WSGI 响应转换回 HTTP 响应。

docs/deploying/gunicorn.rst 的官方描述看,Gunicorn 在同类 WSGI 服务器中有以下特点:

  • 易于与各类托管平台集成:平台通常只需你提供可导入的 WSGI 应用或启动命令;
  • 不支持 Windows(但可以在 WSL 下运行);
  • 安装简单:不需要额外的外部依赖,也不需要编译;
  • 内置基于 gevent 的异步 worker 支持,可满足高并发长连接场景。

理解这一层分工的关键在于:Gunicorn 只负责 HTTP/WSGI 协议层和进程管理,应用逻辑仍由 Flask 承担。Flask 应用与 WSGI 服务器之间的契约体现在 src/flask/app.pyFlask.wsgi_app 方法:每个请求进来时,Flask 通过 request_context(environ) 创建请求上下文、推送上下文、执行 full_dispatch_request,最终把 Response 对象通过 start_response 交还给服务器。而 Flask.call 只是转发到 wsgi_app,源码 docstring 明确建议中间件以 app.wsgi_app = MyMiddleware(app.wsgi_app) 的方式包装,而不是替换整个 app 对象——这正是后文反向代理场景下使用 ProxyFix 的原理。

安装 Gunicorn

Gunicorn 的安装非常简单:没有外部依赖,也不需要编译。它只能在 WSL 环境下运行于 Windows。推荐的标准流程是创建虚拟环境、安装你的应用、再安装 Gunicorn:

$ cd hello-app
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install .  # install your application
$ pip install gunicorn

这里的 pip install . 表示以可安装包的形式安装当前应用(要求项目提供了 pyproject.toml 等打包配置)。本仓库中的 示例应用 即遵循此结构:examples/tutorial/flaskr/__init__.py 暴露了标准的 create_app() 工厂函数,可被 Gunicorn 直接按 flaskr:create_app() 方式加载。

启动 Gunicorn:module:app 字符串约定

Gunicorn 唯一的必填参数是告诉它如何加载你的 Flask 应用,语法为:

{module_import}:{app_variable}
  • module_import:包含应用的模块的点分导入名;
  • app_variable:存放应用的变量名。如果使用应用工厂模式,它也可以是带任意参数的函数调用

官方文档给出的两种等价写法:

# equivalent to 'from hello import app'
$ gunicorn -w 4 'hello:app'

# equivalent to 'from hello import create_app; create_app()'
$ gunicorn -w 4 'hello:create_app()'

启动后的典型输出:

Starting gunicorn 20.1.0
Listening at: http://127.0.0.1:8000 (x)
Using worker: sync
Booting worker with pid: x
Booting worker with pid: x
Booting worker with pid: x
Booting worker with pid: x

结合仓库示例理解两种加载方式

仓库测试目录中正好提供了一个最小化的 Flask 应用 tests/test_apps/helloworld/hello.py

from flask import Flask

app = Flask(__name__)


@app.route("/")
def hello():
    return "Hello World!"

这就是 hello:app 指向的模块级 app 变量。而 tests/test_apps/helloworld/wsgi.py 只有一行 from hello import app——这正是许多部署平台的约定:WSGI 入口文件只需导入应用对象,服务器即可通过模块名找到它。

对于应用工厂模式,参考 examples/tutorial/flaskr/init.py 中的 create_app(test_config=None):Gunicorn 的 'flaskr:create_app()' 会实际执行 from flaskr import create_app 并调用 create_app() 拿到应用实例。注意每个 worker 进程都会执行一次该调用,这也是官方教程将实例级配置(如 SECRET_KEY、数据库路径)放在 create_app 内部而非模块全局的原因。

-w 选项:worker 进程数

  • -w 指定 Gunicorn 运行的进程数;官方建议的起始值经验公式是 CPU * 2
  • 默认只有 1 个 worker,对于默认的 sync worker 类型,这通常不是你想要的——sync worker 每个进程同一时刻只能处理一个请求,CPU 密集型或常规 IO 场景下多进程是吞吐的关键。

访问日志

默认情况下 Gunicorn 不会打印每个请求的日志,只显示 worker 信息(如上面的 Booting worker)和错误。若要把访问日志输出到 stdout,使用 --access-logfile=- 选项:

$ gunicorn -w 4 --access-logfile=- 'hello:app'

这一点对容器化部署很重要:日志走 stdout/stderr 才能被容器运行时或日志收集器捕获。

外部绑定与安全实践

官方文档明确的安全原则:

Gunicorn 不应以 root 身份运行,否则你的应用代码将以 root 运行,存在安全风险。但这意味着它无法绑定 80 或 443 端口。正确的做法是在 Gunicorn 前面加一个反向代理(如 nginxApache HTTPD),由代理负责监听 80/443、处理 TLS。

没有反向代理的简单场景下,可以用 -b 0.0.0.0 将服务绑定到所有外部 IP 的非特权端口:

$ gunicorn -w 4 -b 0.0.0.0 'hello:create_app()'
Listening at: http://0.0.0.0:8000 (x)

两点注意事项(来自原文档):

  1. 使用反向代理时不要这样绑定,否则流量可以直接绕过代理,代理层的安全策略全部失效;
  2. 0.0.0.0 不是一个可以直接在浏览器中访问的地址,你需要用具体的 IP 地址来访问。

反向代理下的 ProxyFix

当请求经过 nginx/Apache 转发后,从 WSGI 服务器和 Flask 的视角看,请求都变成了"来自本地代理",真实的客户端 IP、协议、Host 等信息丢失。官方方案(docs/deploying/proxy_fix.rst)是应用 Werkzeug 提供的 ProxyFix 中间件,它依赖 HTTP 服务器设置的 X-Forwarded- 系列请求头来还原真实值:

from werkzeug.middleware.proxy_fix import ProxyFix

app.wsgi_app = ProxyFix(
    app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1
)

注意 ProxyFix 的包装对象正是 app.wsgi_app,这与 Flask.wsgi_app docstring 中推荐的中间件挂载方式一致——保留原始应用对象引用,避免 app = MyMiddleware(app) 导致的对象丢失问题。官方文档同时警告:只有在应用确实位于代理之后时才能使用此中间件,并且参数必须设置为链路上实际设置各请求头的代理数量。由于入站请求头可以被伪造,配置错误会成为安全问题。使用大多数托管平台时通常也需要这一步(见 docs/deploying/index.rst 末尾的提示)。

异步场景:gevent worker

对于大多数使用场景,默认的 sync worker 已经足够。如果你需要处理大量、长时运行、并发的连接,Gunicorn 提供了基于 gevent 的异步 worker。官方文档特别澄清:gevent 的协程模型不是 Python 的 async/await,也不是 ASGI 服务器规范——它通过 greenlet 实现任务切换,让协程代码看起来像标准同步 Python。更多关于在 Flask 应用中启用 gevent 的信息可参考 docs/gevent.rst,以及 WSGI 部署侧的对比 docs/deploying/gevent.rst(后者建议优先使用 Gunicorn 或 uWSGI 搭配 gevent worker,而不是直接用 gevent 的 WSGI 服务器)。

依赖要求:

  • 使用 gevent 时需要 greenlet>=1.0
  • 使用 PyPy 时需要 PyPy>=7.3.7

启动命令只需增加 -k gevent 指定 worker 类:

$ gunicorn -k gevent 'hello:create_app()'
Starting gunicorn 20.1.0
Listening at: http://127.0.0.1:8000 (x)
Using worker: gevent
Booting worker with pid: x

gevent worker 在单进程内即可处理大量连接,因此通常不需要像 sync worker 那样用 CPU * 2 的进程数,可配合较少的 worker 进程获得更优的并发表现。

要点小结

主题 关键命令 / 配置 说明
加载应用 gunicorn 'hello:app' module:variable,也支持 module:factory()
worker 数 -w 4 默认 1,sync worker 建议起始值 CPU * 2
访问日志 --access-logfile=- 默认只输出 worker 信息与错误
外部绑定 -b 0.0.0.0 非特权端口;有反向代理时禁止此用法
异步 -k gevent 高并发长连接场景;需 greenlet>=1.0
端口 80/443 反向代理 + ProxyFix 不要用 root 运行 Gunicorn

Gunicorn 的完整功能(如 preload、graceful timeout、TLS 证书等)超出了本篇文档的范围,建议结合 gunicorn --help 与其官方文档进一步学习。对于更完整的部署形态——WSGI 服务器 + 反向代理的组合,可继续参考 docs/deploying/nginx.rstdocs/deploying/proxy_fix.rst 两个文档,它们与本文构成一套完整的 Flask 生产部署链路。

登录后查看全文
热门项目推荐
相关项目推荐