你的博客现在只活在一个特殊地址上:
127.0.0.1------全宇宙只有你自己能访问。今天让它公之于众:真实服务器、真实域名、真实HTTPS。Web专题三部曲,大结局。
🎯 本篇不写新功能 ,上线才是对整个专栏工程化训练的期终考试。
📌 太长不看版(给想快速上线的你)
| 项目信息 | 一句话说明 |
|---|---|
| 本篇目标 | 把本地博客部署到公网,全世界可访问 |
| 难度 | ⭐⭐⭐⭐(运维入门,但跟着做能跑通) |
| 核心工具 | 云服务器 + gunicorn + systemd + nginx + certbot |
| 主线方案 | Plan A:云服务器六步部署(推荐) |
| 备选方案 | Plan B:免费托管平台(零运维,5分钟上线) |
| 预计耗时 | 1~2小时(含买服务器、配域名) |
| 做完你能得到 | 一个全世界都能访问的、带HTTPS的博客! |
一、为什么app.run()不能直接上线
app.run(debug=True)起的是Flask开发服务器------它自己都声明了"别拿我去生产":
| 开发服务器的问题 | 后果 |
|---|---|
| 单线程/单进程 | 两个人同时访问,一人卡全员排队 |
| 无崩溃自愈 | 一个未捕获异常,网站全体下线直到你发现 |
| debug调试页 | 报错时把代码、配置全部展示给访问者 |
| 性能未优化 | 静态文件、并发处理都是教学级实现 |
🍽️ 生产环境的正解是专业分工
延续餐厅比喻:
text
开发服务器 = 老板亲自端盘子(一个人全干,忙不过来还不专业)
生产三件套:
gunicorn = 后厨(真正干活的WSGI服务器,可开多个工人)
systemd = 餐厅经理(崩溃自动拉起、开机自动营业)
nginx = 前台接待(对外迎宾、分发请求、卸载静态文件)
📌 gunicorn是WSGI服务器------它知道怎么高效地把网络请求喂给Flask,这是开发服务器不具备的专业能力。
二、Plan A:云服务器部署六步(主线)
第0步:买一台服务器
各大云平台(阿里云/腾讯云等)的轻量应用服务器,最低配1核1G足够跑这个博客,系统选Ubuntu 22.04/24.04。
买完得到两样东西:公网IP和登录密码(或密钥)。
第1步:SSH------人生第一次登录千里之外的电脑
Windows 10/11自带ssh(PowerShell里直接用),Mac/Linux终端自带:
bash
ssh root@你的服务器IP
输入密码(输入时不显示,盲打后回车)。
看到欢迎信息,你就站在了那台服务器上------接下来所有命令都在这台机器上执行。
第2步:装环境 + 拉代码
bash
apt update && apt install -y python3-venv python3-pip git
git clone https://github.com/你的用户名/flask-blog.git # git托管的意义在此爆发
cd flask-blog
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
pip install gunicorn
🎯 部署的代码搬运,就是一条
git clone------你的GitHub仓库就是部署源。SECRET_KEY按军规用环境变量,稍后写进服务配置。
第3步:gunicorn试跑------先让它转起来
bash
gunicorn -w 2 -b 0.0.0.0:8000 "main:app"
| 参数 | 含义 |
|---|---|
-w 2 |
开2个工人进程(一般CPU核数×2+1) |
-b 0.0.0.0:8000 |
监听所有网卡的8000端口(试跑阶段先直连) |
"main:app" |
模块名:应用变量 ------去main.py里找app这个对象 |
此时浏览器访问http://服务器IP:8000------博客上线了(半成品,往下走)。
⚠️ 看不到?先查第六节③防火墙。
第4步:systemd常驻------崩了自动拉起,开机自动营业
手动gunicorn的问题:SSH一断它就死了。
交给系统管家systemd(文件放/etc/systemd/system/blog.service):
ini
[Unit]
Description=Flask Blog
After=network.target
[Service]
User=root
WorkingDirectory=/root/flask-blog
Environment="SECRET_KEY=换成一段随机长字符串"
ExecStart=/root/flask-blog/venv/bin/gunicorn -w 2 -b 127.0.0.1:8000 "main:app"
Restart=always
[Install]
WantedBy=multi-user.target
bash
systemctl daemon-reload
systemctl enable --now blog
systemctl status blog # 看到 active (running) 即成功
🎯 三个能力一次到位
| 能力 | 配置 | 说明 |
|---|---|---|
| 崩溃自愈 | Restart=always |
进程挂了自动拉起 |
| 开机自启 | enable --now |
服务器重启后自动营业 |
| 日志可查 | journalctl -u blog -f |
实时看日志------logging的服务器版 |
📌 注意此时gunicorn改听
127.0.0.1:8000------只给本机访问,外网流量交给前台nginx。
第5步:安全小件------关好门窗
bash
ufw allow 22 # SSH,别把自己锁外面
ufw allow 80 # HTTP
ufw allow 443 # HTTPS
ufw enable
🔒 再进阶两件事(列清单不展开):登录改用SSH密钥、禁用root密码登录------服务器暴露公网的第一天,就有机器人在尝试爆破它。
三、域名与HTTPS:从IP到门牌
🌐 域名
在域名平台买一个(几十元/年),控制台里加一条A记录指向你的服务器IP。
📌 DNS就是互联网的电话簿:把好记的名字翻译成IP。
🔐 HTTPS
为什么要它?
| # | 原因 | 说明 |
|---|---|---|
| ① | HTTP是明文快递 | 内容路上可被偷看篡改 |
| ② | 浏览器标记"不安全" | 直接吓跑访客 |
| ③ | HTTPS的TLS证书 | 让通信加密 + 证明"你就是你" |
好消息是证书免费(Let's Encrypt)且配置只需三条命令(前置:域名已解析到本服务器):
bash
apt install -y certbot python3-certbot-nginx
apt install -y nginx
# 写一个最简nginx反代配置(见仓库 19-deploy/nginx-blog.conf)
systemctl reload nginx
certbot --nginx -d 你的域名 # 自动申请证书 + 自动改nginx配HTTPS + 自动续期
完成后浏览器地址栏出现锁标志🔒------你的博客从此和商业网站用同一套安全标准。
⚠️ 国内服务器特别提醒 :绑定域名需完成ICP备案(几天流程);嫌备案麻烦,可用境外服务器,或先用IP访问、或选下面的Plan B。
四、Plan B:免费托管平台(零运维上线)
没有服务器预算?PythonAnywhere(免费档对Flask支持极好):
| 步骤 | 操作 |
|---|---|
| ① | 注册账号 |
| ② | 上传或clone代码 |
| ③ | Web面板里指到main.py的app |
| ④ | 5分钟拿到一个你的用户名.pythonanywhere.com网址 |
💡 Render、Railway等平台同理。
代价:免费档有休眠/资源限制,定制空间小。
📌 先用它找"上线了"的感觉,再回来学Plan A的真功夫------两条路都通向同一个终点。
五、上线检查清单(全部来自前18篇的债)
| # | 检查项 |
|---|--------|------|
| ① | debug关闭 :生产代码里没有debug=True |
| ② | SECRET_KEY用环境变量 :不进代码仓库 |
| ③ | requirements.txt是最新 :服务器pip install不缺包 |
| ④ | 日志可查 :journalctl -u blog随时翻案发现场 |
| ⑤ | 数据库有备份意识 :定期拷贝blog.db |
| ⑥ | git仓库干净:没有密钥、没有调试残留 |
✅ 六项全绿,你的博客才算真正"毕业"。
六、常见报错:这6个,上线第一晚的高发区(重点!)
① 服务器上ModuleNotFoundError: No module named 'flask'
🔍 原因:gunicorn跑在系统Python里,没进venv。
✅ 解法:
- service文件的
ExecStart写venv里的gunicorn的绝对路径(第4节写法); - 或者你pip装的时候没
activate。
② 浏览器访问IP:8000转圈连不上
🔍 原因 :云平台的安全组/防火墙 没放行8000端口------服务器外面还有一道云平台的门。
✅ 解法:云控制台 → 安全组/防火墙 → 放行对应端口。
⚠️ 国内云100%要做这步。
③ 配了nginx后访问域名显示502 Bad Gateway
🔍 原因:nginx是前台,后厨gunicorn没起来或端口对不上。
✅ 解法:
bash
systemctl status blog # 看后厨状态
journalctl -u blog -e # 看报错
📌 502的意思就是"前台在,后厨不在"。
④ git clone提示无权限
🔍 原因:仓库是private。
✅ 解法:
- 改public;
- 或给服务器配部署密钥(GitHub官方文档搜deploy key)。
⑤ 上线后用户登录全部失效
🔍 原因:SECRET_KEY和开发时不同/没固定。
✅ 解法 :service文件里Environment固定密钥,重启一次后永久稳定。
⑥ sqlite3.OperationalError: unable to open database file
🔍 原因:systemd启动时的工作目录和权限不对,SQLite找不到/写不了库文件。
✅ 解法:
- service里设好
WorkingDirectory; - 确认运行用户对目录有写权限。
七、课后练习
| # | 练习 | 难度 | 提示 |
|---|---|---|---|
| 1 | 自杀式测试 :kill掉gunicorn进程,几秒后看systemd把它拉活 |
⭐⭐ | 亲手验证Restart=always |
| 2 | 健康检查 :加路由/healthz返回"ok",上线后用浏览器/命令行随时探活 |
⭐⭐ | 一行代码 |
| 3 | 数据库备份 :一条cp命令备份blog.db,练习用cron每天凌晨自动备份 |
⭐⭐⭐ | crontab -e |
| 4(选做) | HTTPS实操:有域名的话完整走一遍Certbot流程,观察它自动改了nginx的哪些配置 | ⭐⭐⭐ | 知原理,更安心 |
🚀 Web三部曲,完。你的博客此刻正跑在一台真实服务器上,全世界都能访问。