上一篇我们加了 JSON API,本地用 python manage.py 或 flask run 就能跑。自己开发没问题,但 不能直接把这套方式原封不动丢到公网服务器。
这一篇做一件事:用 Gunicorn 把 Flask 应用跑成「生产模式」,并知道 Nginx 大概怎么接在前面。
例子仍是通用的 Note 备忘录项目,不涉及任何真实业务。
1. 学完后你能做什么
- 说清楚
app.run()和 Gunicorn 的区别 - 用一条命令启动:
gunicorn -w 4 -b 127.0.0.1:5000 app:app - 知道 worker 数量 怎么估
- 上线前检查
FLASK_DEBUG=0、SECRET_KEY、数据库 - 理解 Nginx 反代 在整体架构里干什么
- 用 systemd 让进程挂了自动拉起(入门版)
2. 为什么开发用的 app.run() 不适合生产
manage.py 里常见写法:
from app import app
if name == "main":
app.run()
Flask 自带的开发服务器:
| 特点 | 开发 | 生产 |
|---|---|---|
| 并发 | 弱,单进程为主 | 需要多 worker |
| 安全 | 有调试便利,不适合暴露公网 | 要关 DEBUG |
| 稳定性 | 改代码自动重载 | 要常驻、崩溃重启 |
| 性能 | 够用 | 扛不住真实流量 |
官方文档也写明:不要用 Werkzeug 开发服务器部署生产。
开发:app.run(debug=True) 方便。
上线:换 Gunicorn(或 uWSGI、Waitress 等),Flask 只负责「应用对象」,不管进程模型。
3. Gunicorn 是什么
Gunicorn(Green Unicorn)是 Python 常用的 WSGI HTTP 服务器:
浏览器 / curl
│
▼
Nginx(可选,443/80)
│
▼
Gunicorn(多 worker 进程)
│
▼
Flask app 对象(你的路由、视图)
你写的 @app.route 不用改;换的是 最外层怎么接 HTTP。
4. 安装与第一条启动命令
虚拟环境里:
pip install gunicorn
在项目根目录(有 app/ 包、manage.py 的那层):
先加载第十二篇的环境变量
export FLASK_DEBUG=0
export SECRET_KEY="生产环境随机长字符串"
export DATABASE_URL=...
gunicorn -w 4 -b 127.0.0.1:5000 "app:app"
参数含义:
| 参数 | 含义 |
|---|---|
-w 4 |
4 个 worker 进程 |
-b 127.0.0.1:5000 |
监听本机 5000 端口 |
app:app |
模块 app 里的变量 app(即 app/__init__.py 里的 Flask 实例) |
浏览器访问 http://127.0.0.1:5000/notes/(或你的首页路由)应能打开。
app:app 怎么记?
app:app
│ └── Flask 实例的名字(通常也叫 app)
└── Python 包名(app/ 文件夹)
若实例叫 application,就写 app:application。
5. worker 数量怎么选
经验公式(CPU 密集型偏少、I/O 多可略多):
workers = 2 × CPU 核数 + 1
例如 2 核 → -w 5 左右。入门项目 2~4 个 往往够用,别一上来开几十个。
每个 worker 是 独立进程,占一份内存。内存紧张时 worker 要减。
6. 上线前配置清单(接第十二篇)
服务器上的环境变量(如 /etc/myapp.env)至少确认:
FLASK_DEBUG=0
SECRET_KEY=一串足够长的随机值
DATABASE_URL=mysql+pymysql://user:pass@127.0.0.1/dbname
或 SQLite:sqlite:////var/www/myapp/instance/app.db
务必关闭调试:
FLASK_DEBUG=1时,错误页可能 泄露代码和变量- 部分场景下还有 远程执行风险(视 Flask/Werkzeug 版本而定)
加载方式:
set -a && source /etc/myapp.env && set +a
gunicorn -w 4 -b 127.0.0.1:5000 app:app
HTTPS 站点若用 Session Cookie,还可加:
SESSION_COOKIE_SECURE=1
(仅 HTTPS 下浏览器才带 Cookie。)
7. 数据库迁移别忘了
第七篇用过 Flask-Migrate。部署新版本代码后:
cd /var/www/myapp
source .venv/bin/activate
set -a && source /etc/myapp.env && set +a
export FLASK_APP=app
flask db upgrade
先迁移、再重启 Gunicorn,避免代码要新字段、表还没改。
8. 静态文件怎么办
备忘录项目的 CSS、JS 在 app/static/,Flask 开发时会自动提供。
生产常见两种做法:
| 方式 | 说明 |
|---|---|
| Nginx 直接托管 static | 快,减轻 Python 压力(推荐) |
| 仍由 Flask/Gunicorn 提供 | 小项目、流量低可以先这样 |
Nginx 示例片段(静态走文件,其余走 Gunicorn):
location /static/ {
alias /var/www/myapp/app/static/;
}
location / {
proxy_pass http://127.0.0.1:5000;
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;
}
入门阶段:先全部 proxy_pass 到 Gunicorn 也能跑;访问量上来再拆 static。
9. 为什么要 Nginx 挡在前面
Gunicorn 监听 127.0.0.1:5000,不直接暴露公网 时:
- Nginx 处理 80/443、HTTPS 证书
- 可配 限流、gzip、上传大小
- 多站点同一台机器时更好管
架构:
用户 → https://notes.example.com
│
▼
Nginx :443
│
▼
Gunicorn 127.0.0.1:5000
│
▼
Flask app
暂时没域名时,可 -b 0.0.0.0:5000 先验证,记得防火墙只开必要端口,不要长期裸奔。
10. 用 systemd 常驻(进程挂了自动起来)
手动 SSH 里跑 Gunicorn,断开 SSH 进程可能就没了。用 systemd 托管:
/etc/systemd/system/myapp.service:
Unit
Description=Note Flask (Gunicorn)
After=network.target
Service
User=www-data
Group=www-data
WorkingDirectory=/var/www/myapp
EnvironmentFile=/etc/myapp.env
ExecStart=/var/www/myapp/.venv/bin/gunicorn -w 4 -b 127.0.0.1:5000 app:app
Restart=always
Install
WantedBy=multi-user.target
启用:
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp
sudo systemctl status myapp
改代码后:
git pull
flask db upgrade # 若有表结构变更
sudo systemctl restart myapp
11. 日志与排错
Gunicorn 默认日志在终端。生产可指定文件:
gunicorn -w 4 -b 127.0.0.1:5000 app:app \
--access-logfile /var/log/myapp/access.log \
--error-logfile /var/log/myapp/error.log
常见问题:
| 现象 | 可能原因 |
|---|---|
| 502 Bad Gateway | Gunicorn 没起来、端口不对 |
| 静态 404 | Nginx alias 路径错 |
| 改代码不生效 | 没 restart systemd |
| 数据库连不上 | DATABASE_URL、MySQL 权限、防火墙 |
| Session 登录丢 | SECRET_KEY 变了、Cookie Secure 与 HTTP 混用 |
12. 开发 vs 生产对照
| 本地开发 | 生产 | |
|---|---|---|
| 启动 | python manage.py / flask run |
gunicorn app:app |
| DEBUG | 可开 | 必须关 |
| 进程 | 1 个 | 多 worker |
| 端口 | 5000 本机 | 127.0.0.1 + Nginx |
| 配置 | .env |
/etc/myapp.env |
| 迁移 | 随手 flask db upgrade |
部署流程里固定一步 |
13. 新手常踩的 5 个坑
- 生产仍用
app.run(debug=True)--- 换 Gunicorn,关 DEBUG。 gunicorn manage:app与app:app混用 --- 以你项目里 Flask 实例在哪定义 为准;常见是from app import app→app:app。- 没做
flask db upgrade就重启 --- 新代码访问新字段会 500。 - SECRET_KEY 每次部署随机变 --- 所有用户 Session 失效;要固定存环境变量。
- worker 开太多 --- 内存爆掉,反而更慢。
14. 小结
记住五件事:
app.run()只用于开发- 生产用 Gunicorn:
gunicorn -w 4 -b 127.0.0.1:5000 app:app FLASK_DEBUG=0+ 强SECRET_KEY- 前面加 Nginx 做 HTTPS 和反代(入门可先全量 proxy)
- systemd +
flask db upgrade组成最小部署闭环
十四篇下来,你已经走完:写功能 → 分层 → 配置外置 → JSON API → 上线跑起来。