2.7 部署 Deployment


2.7 部署 Deployment

问题与直觉:为什么 dev 服务器不能上线

app.run(debug=True) 的服务器(Werkzeug 开发服务器)是为单用户调试设计的:一次只能处理有限请求、没有 worker 进程、调试器还有安全隐患。生产环境需要:多进程并发、稳定进程管理、静态文件高效服务、安全配置

直觉类比:开发服务器像"自家厨房"——自己做给自己吃没问题;生产环境是"餐厅后厨"——需要多个厨师(worker)、前厅(Nginx)分流、备菜区(静态文件)专人负责。

💡 关键直觉:部署 = 分层。Python 进程只负责动态内容(视图/API),Nginx 负责静态文件与流量分发——各司其职,性能与安全都最优。

核心原理:WSGI 服务器做什么

Flask 是 WSGI 应用(上一节讲过)。生产环境需要一个WSGI 服务器

  • 把 HTTP 请求解析成 WSGI 环境变量;
  • 调用 Flask 应用(app.wsgi_app)得到响应;
  • 管理多个 worker 进程/线程,支撑并发。

Gunicorn(Green Unicorn)是 Python 最流行的 WSGI 服务器之一:启动即用、配置简单、支持多 worker

2.1 最小部署:Gunicorn

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 跑生产。

2.2 应用工厂 + 入口文件

生产部署通常配合"应用工厂"模式:

# wsgi.py(部署入口,与 app 分离) from myapp import create_app app = create_app('production')

启动:gunicorn -w 4 wsgi:app。这样应用逻辑与部署入口解耦,测试、脚本也可以复用工厂。

工程实践要点

3.1 Nginx 反向代理

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

3.2 环境变量管理配置

部署时用环境变量注入敏感信息(绝不写进代码/仓库):

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)。

3.3 进程管理:systemd

让 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

3.4 Docker 部署

# 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 的好处:环境一致、依赖隔离、一条命令到处跑

3.5 部署方案速查

常见部署方案对比

常见部署方案对比

上线前的安全检查清单

检查项 正确姿势
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-*

动手演练:完整上线流程

  1. 本地:gunicorn -w 2 -b 127.0.0.1:8000 wsgi:app 验证能启动;
  2. 环境变量:导出 SECRET_KEY、DATABASE_URL;
  3. Nginx:写 server 配置,nginx -t 通过后 reload;
  4. 访问域名验证静态文件与动态请求都正常;
  5. 打健康检查接口,接入监控告警;
  6. 把 Gunicorn 配成 systemd 服务,Restart=always

重点提炼

  • 开发 vs 生产app.run() 只适合调试;生产用 WSGI 服务器(Gunicorn)承担并发。
  • 分层架构:Nginx(静态+反向代理)→ Gunicorn(worker 进程)→ Flask 应用。
  • 配置注入:环境变量承载密钥与数据库地址,配置优先级"默认 < 文件 < 环境变量"。
  • 进程守护:systemd 管理 Gunicorn,崩溃自动重启。
  • 容器化:Docker 保证环境一致,适合团队与 CI。
  • 上线清单:DEBUG 关、HTTPS、备份、日志、健康检查。

深入理解:部署方案的选型与演进

部署没有"唯一正确方案",只有"当前阶段最合适方案"。理清选型逻辑,部署时就不慌。

第一,三个典型阶段的方案。 个人/学习项目: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 绑定域名——这套流程走一遍,你的应用才算"正式上线"。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U