app.run(debug=True) 的服务器(Werkzeug 开发服务器)是为单用户调试设计的:一次只能处理有限请求、没有 worker 进程、调试器还有安全隐患。生产环境需要:多进程并发、稳定进程管理、静态文件高效服务、安全配置。
直觉类比:开发服务器像"自家厨房"——自己做给自己吃没问题;生产环境是"餐厅后厨"——需要多个厨师(worker)、前厅(Nginx)分流、备菜区(静态文件)专人负责。
💡 关键直觉:部署 = 分层。Python 进程只负责动态内容(视图/API),Nginx 负责静态文件与流量分发——各司其职,性能与安全都最优。
Flask 是 WSGI 应用(上一节讲过)。生产环境需要一个WSGI 服务器:
app.wsgi_app)得到响应;Gunicorn(Green Unicorn)是 Python 最流行的 WSGI 服务器之一:启动即用、配置简单、支持多 worker。
pip install gunicorn # 启动:gunicorn [模块]:[app变量] -w [worker数] gunicorn -w 4 -b 0.0.0.0:8000 "app:app"
参数解读:
| 参数 | 含义 | 建议 |
|---|---|---|
-w 4 |
4 个 worker 进程 | 一般 2~4 × CPU 核数 |
-b 0.0.0.0:8000 |
监听地址与端口 | 绑内网地址,由 Nginx 转发 |
--timeout 30 |
请求超时秒数 | 长任务调大 |
--access-logfile - |
访问日志输出到 stdout | 便于收集 |
⚠️ 重要:
app.run()里的debug=True在生产必须为False,否则调试器可能被远程利用。永远不要用debug=True跑生产。
生产部署通常配合"应用工厂"模式:
# wsgi.py(部署入口,与 app 分离) from myapp import create_app app = create_app('production')
启动:gunicorn -w 4 wsgi:app。这样应用逻辑与部署入口解耦,测试、脚本也可以复用工厂。
Gunicorn 监听内网端口(如 8000),Nginx 对外提供 80/443:
# /etc/nginx/sites-available/myapp server { listen 80; server_name example.com; # 静态文件直接由 Nginx 提供(不进 Python) location /static/ { alias /var/www/myapp/static/; expires 30d; } # 动态请求转发给 Gunicorn location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
配置后:sudo nginx -t && sudo systemctl reload nginx。
部署时用环境变量注入敏感信息(绝不写进代码/仓库):
import os class ProductionConfig(Config): DEBUG = False SECRET_KEY = os.environ.get('SECRET_KEY') # 从环境变量读 DATABASE_URL = os.environ.get('DATABASE_URL') SESSION_COOKIE_SECURE = True # HTTPS 才发 Cookie
启动:
export SECRET_KEY="$(openssl rand -hex 32)" export DATABASE_URL="postgresql://user:pass@db-host/appdb" gunicorn -w 4 wsgi:app
配置优先级:默认值 < 配置文件 < 环境变量。敏感值一律走环境变量或密钥管理服务(如云厂商的 KMS/Secrets Manager)。
让 Gunicorn 开机自启、崩溃自动拉起:
# /etc/systemd/system/myapp.service [Unit] Description=My Flask App After=network.target [Service] User=www-data WorkingDirectory=/var/www/myapp EnvironmentFile=/etc/myapp/env # 环境变量文件 ExecStart=/usr/bin/gunicorn -w 4 -b 127.0.0.1:8000 wsgi:app Restart=always [Install] WantedBy=multi-user.target
sudo systemctl enable myapp sudo systemctl start myapp sudo systemctl status myapp
# Dockerfile FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "wsgi:app"]
docker build -t myapp . docker run -d -p 8000:8000 \ -e SECRET_KEY=xxx \ -e DATABASE_URL=postgresql://... \ --name myapp myapp
Docker 的好处:环境一致、依赖隔离、一条命令到处跑。

| 检查项 | 正确姿势 |
|---|---|
| DEBUG 关闭 | DEBUG=False,环境变量控制 |
| 密钥轮换 | SECRET_KEY 随机生成,不入代码库 |
| HTTPS | Nginx 配 SSL 证书(Let's Encrypt 免费) |
| 数据库备份 | 定时备份 + 恢复演练 |
| 日志收集 | Gunicorn/Nginx 日志落地、定期清理 |
| 资源限制 | 设置超时与 worker 数,防耗尽 |
| 健康检查 | /healthz 返回 200,供监控探活 |
| 误区 | 现象 | 正解 |
|---|---|---|
| 生产还用 app.run() | 并发差、请求卡死 | 换 Gunicorn/uWSGI |
| debug=True 上线 | 调试器暴露、信息泄露 | 强制 False |
| 静态文件走 Flask | 响应慢、CPU 高 | 交给 Nginx 服务 |
| 密钥写死在配置 | 泄露即全盘暴露 | 环境变量/KMS |
| Nginx 忘了转发头 | 拿不到真实 IP/协议 | 配 X-Forwarded-* |
gunicorn -w 2 -b 127.0.0.1:8000 wsgi:app 验证能启动;nginx -t 通过后 reload;Restart=always。app.run() 只适合调试;生产用 WSGI 服务器(Gunicorn)承担并发。部署没有"唯一正确方案",只有"当前阶段最合适方案"。理清选型逻辑,部署时就不慌。
第一,三个典型阶段的方案。 个人/学习项目:Gunicorn + Nginx + 单台云服务器,手动部署,够用且能学到全部概念;团队/成长项目:Docker 容器化 + CI/CD 流水线(提交代码自动构建测试发布),环境一致、可回滚;规模/生产项目:Kubernetes 编排 + 云厂商托管服务(数据库、缓存、日志全托管),高可用自动扩缩。选型的核心变量是"团队规模 × 流量规模 × 运维能力"——不要一步到位上 K8s,运维成本会压垮小团队。
第二,部署的"配置与代码分离"纪律。 同一份代码,通过环境变量在不同环境(开发/测试/生产)加载不同配置——这是第二章 2.4 配置章节与部署的接口。部署脚本里 export 环境变量,代码里 os.environ.get 读取。代码里绝不出现生产密钥,这是底线。
第三,健康检查与优雅停机。 给应用加一个 /healthz 接口(返回 200 + 简单状态),部署平台用它判断实例是否健康:不健康就重启、滚动更新时先摘流量再停旧实例。Gunicorn 支持 graceful timeout(优雅停机:等正在处理的请求完成再退出),配合 systemd 的 Restart=always,实现"挂了自动拉起、更新不丢请求"。
第四,日志与监控是部署的一部分。 部署不等于"能访问"——还包括"出问题能发现、能定位"。最低要求:访问日志(谁访问了什么)、错误日志(堆栈)、进程状态(systemd status)。进阶:结构化日志(JSON 格式,便于检索)、指标采集(QPS、错误率、响应时间)、告警(错误率突增通知)。没有监控的部署,等于蒙眼开车。
第五,回滚预案。 每次部署前想好"如果新版本有问题,怎么回到旧版本"。方案:保留上一个版本的构建产物(Docker 镜像 tag、代码 tag),出问题一键切回;数据库迁移(第三章 3.7)支持 downgrade。"能快速回滚"比"部署得完美"更重要——敢发布的前提是能退。
第六,HTTPS 与域名。 生产环境必须 HTTPS。免费方案 Let's Encrypt(certbot 自动续期)足够大多数场景。Nginx 里把 HTTP 全部 301 到 HTTPS,配置 HSTS 头。域名解析到服务器 IP,Nginx server_name 绑定域名——这套流程走一遍,你的应用才算"正式上线"。