必须用gunicorn+systemd+Nginx闭环部署:gunicorn负责WSGI处理,systemd实现开机自启、崩溃自拉起和日志统一管理,Nginx承担反向代理、真实IP透传与超时协同,三者缺一不可。

别用 flask run 上生产环境——它单进程、无崩溃隔离、不控资源,一压就挂。真正能扛住流量的部署,必须由 gunicorn 承担 WSGI 层职责,并搭配进程管理器和反向代理构成闭环。
为什么 gunicorn 不能裸跑?
直接执行 gunicorn app:app 虽然能启动服务,但只要终端断开、SSH 退出或进程被 kill,服务就彻底消失。生产环境要求“开机自启 + 崩溃自拉起 + 日志可追溯”,这些都不是 gunicorn 自身能力范围内的事。
- 没有守护进程机制:
gunicorn默认前台运行,依赖 shell session 存活 - 无法响应系统重启:服务器 reboot 后服务不会自动恢复
- 崩溃后无重试:worker 异常退出后,master 不会补足 worker 数量(除非显式配置
--preload和--max-requests) - 日志分散难排查:stdout/stderr 直接打到终端,没落盘、没轮转、没归档
gunicorn 的关键参数怎么选?
参数不是越多越好,而是要匹配你的应用特性和服务器资源。盲目堆 --workers 反而会因上下文切换拖慢整体吞吐。
-
--workers N:推荐设为2 × CPU核心数 + 1(同步 I/O 场景),若应用含较多阻塞调用(如数据库慢查询、HTTP 外部请求),可适当增加;但超过 8 个后收益递减,且内存占用线性上升 -
--worker-class sync(默认)适合大多数 Flask 场景;若用了asyncio或需高并发长连接,才考虑gevent或eventlet,但需额外安装并验证兼容性 -
--timeout 30:防止慢请求长期占用工作者;若业务中有导出报表等长耗时操作,应单独设路由超时,而非全局调大 -
--keep-alive 5:控制 HTTP keep-alive 连接空闲时间,配合 Nginx 的keepalive_timeout保持一致,避免连接复用失效 - 务必加
--preload:让 master 在 fork worker 前先加载应用代码,避免每个 worker 重复 import,减少内存占用和启动延迟
用 systemd 还是 supervisor 管理进程?
在现代 Linux 发行版(Ubuntu 22.04+/CentOS 8+)上,优先选 systemd。它原生集成、权限模型清晰、依赖管理可靠,而 supervisor 是 Python 实现的第三方工具,多一层抽象、日志路径不统一、升级易出兼容问题。
立即学习“Python免费学习笔记(深入)”;
-
systemd单元文件必须指定Type=simple(非forking),因为gunicorn默认不 daemonize - 要用
Restart=always+RestartSec=5实现崩溃自启,但需配合StartLimitIntervalSec=60和StartLimitBurst=3防止反复失败导致雪崩 -
EnvironmentFile=/path/to/env比硬编码Environment=更安全,尤其涉及密钥、数据库地址等敏感变量 - 日志统一走
journald,用journalctl -u your-app.service -f实时查看,不用再配stdout_logfile
Nginx 反向代理必须做这三件事
只把 Nginx 当“端口转发器”用是危险的。它真正的价值在于卸载客户端压力、加固传输层、隔离网络边界。
- 必须设
proxy_pass http://127.0.0.1:8000(不要用localhost,DNS 解析引入额外延迟) - 必须传真实客户端 IP:
proxy_set_header X-Real-IP $remote_addr和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for,否则 Flask 的request.remote_addr会变成127.0.0.1 - 必须禁用 upstream 缓存:
proxy_buffering off(对流式响应、SSE、WebSocket 等场景至关重要),否则 Gunicorn 返回的 chunked body 会被缓冲、延迟送达
最容易被忽略的是 gunicorn 和 Nginx 的超时协同:Nginx 的 proxy_read_timeout 必须 ≥ gunicorn 的 --timeout,否则 Nginx 会在 Gunicorn 处理完前主动断连,返回 504。这个差值至少留 5 秒余量。


















