上线全景:从代码到公网可访问
本地 npm run dev 能打开页面,和用户在手机上打开 https://your-domain.com 是两件事。中间隔着域名解析、备案、安全组、防火墙、运行时、进程守护、反向代理、证书、备份与监控十来个环节,任何一个环节漏掉,用户看到的就是超时或 502。本章先把这些环节串成一条链路,再给出上线前的准备清单和三种部署形态的取舍。
上线链路上到底有哪些组件
| 层 | 组件 | 作用 | 国内常见选择 |
|---|---|---|---|
| 入口 | 域名 + DNS 解析 | 把域名指向公网 IP | 阿里云 DNS、腾讯云 DNSPod、Cloudflare |
| 合规 | ICP 备案 | 国内节点用域名对外提供服务的前置条件 | 阿里云/腾讯云备案系统 |
| 加速 | CDN / 对象存储 | 静态资源就近分发、证书卸载 | 阿里云 CDN + OSS、腾讯云 CDN + COS |
| 网络 | 云安全组 + 主机防火墙 | 两层准入控制,决定谁能连哪个端口 | 安全组 + ufw / firewalld |
| 主机 | 云服务器(ECS/CVM) | 跑应用的计算与存储资源 | 阿里云 ECS、腾讯云 CVM、轻量应用服务器 |
| 运行时 | Python / Node / Go / Java | 应用运行所需的语言环境 | pyenv、nvm、官方 tar、SDKMAN |
| 进程 | Docker / systemd / Supervisor | 拉起进程、崩溃重启、开机自启 | 优先 systemd,容器化优先 Docker |
| 代理 | Nginx | 反向代理、静态站点、HTTPS 终止、gzip | Nginx 官方源或发行版包 |
| 证书 | TLS 证书 | 加密传输,浏览器不报不安全 | 阿里云/腾讯云免费证书、Let's Encrypt |
| 数据 | MySQL / Redis / 对象存储 | 业务数据与缓存 | 云数据库 RDS、自建容器 |
| 保障 | 备份 + 监控 + 日志 | 出事能恢复、出事能提前知道 | 定时 dump 到 OSS、Uptime 探活、日志轮转 |
自顶向下的链路图
用户浏览器 / App
↓ ① DNS 解析:example.com → 47.x.x.x(TTL 决定生效快慢)
↓ ② CDN(可选):静态资源命中边缘节点,回源到 Nginx
↓ ③ 云安全组:放行 80 / 443,其余端口默认拒绝
↓ ④ 主机防火墙(ufw / firewalld):只放行 22 / 80 / 443
Nginx(HTTPS 终止 + 反向代理 + 静态站点)
↓ ⑤ proxy_pass 到上游
应用进程 127.0.0.1:8000(Docker 容器 / systemd 服务)
↓ ⑥ 读写数据
MySQL 127.0.0.1:3306 / Redis 127.0.0.1:6379(绝不监听 0.0.0.0)
↓ ⑦ 备份与监控
每日 dump 上传对象存储 + 探活告警 + 日志轮转
排障时倒着看这张图:浏览器打不开就先查 ① 解析,curl 通但浏览器红字就查证书,Nginx 返回 502 就往 ⑤ 后面的应用进程查,几乎不会错。
三种部署形态对比
| 维度 | 裸机 + systemd/Supervisor | Docker / Compose | Kubernetes |
|---|---|---|---|
| 适用规模 | 1~3 台、单体应用 | 1~10 台、多服务单体集群 | 多集群、多团队、需要弹性伸缩 |
| 环境一致性 | 靠文档和自律,容易漂移 | 镜像即环境,一致性好 | 同 Docker,且声明式编排 |
| 首次上手成本 | 最低,会写 unit 文件即可 | 中,需要懂镜像与网络 | 高,集群、Ingress、存储一堆概念 |
| 回滚速度 | 换目录 + reload,秒级 | 换镜像 tag,秒级 | 换镜像 tag,秒级 |
| 资源开销 | 最小,无额外常驻进程 | 每容器几十 MB 开销 | 控制面自身就要几 GB |
| 典型痛点 | 依赖冲突、机器多了难同步 | 数据卷与网络要提前规划 | 复杂度过剩,小团队维护不动 |
选择建议很直接:单机部署用 systemd 或 Compose 就够;只有当服务数量、机器数量、发布频率三者同时上升,K8s 的复杂度才换得回收益。先跑通再优化,一开始就上 K8s 的项目,多数时间花在运维平台而不是业务上。
上线前的准备清单
| 检查项 | 完成标准 | 漏掉会怎样 |
|---|---|---|
| 域名与解析 | dig +short 域名 返回目标 IP | 用户根本找不到你 |
| ICP 备案 | 备案号已下发且接入商正确 | 国内节点 80/443 被拦截,域名无法访问 |
| TLS 证书 | 浏览器无警告,curl -I https:// 返回 200 | 被标记不安全,转化率直线下降 |
| 端口规划 | 只开 22/80/443,应用端口仅监听 127.0.0.1 | 数据库或调试端口被扫,数据泄露 |
| 运行时版本 | python -V/node -v/go version 与开发一致 | “本地能跑线上报错”的经典事故 |
| 配置与密钥 | 走环境变量或密钥文件,权限 600,未提交 Git | 密钥泄露,被刷账单或被盗数据 |
| 数据备份 | 已做一次手工全量 dump 并验证可恢复 | 迁移或升级失败时无路可退 |
| 健康检查 | 提供一个不需要鉴权的 /healthz | 无法自动判断新版本是否真的起来了 |
| 日志与轮转 | 日志落盘并配置轮转,不写进容器 stdout 无限增长 | 磁盘写满导致整机不可用 |
| 回滚方案 | 保留上一个可用的镜像 tag 或代码目录 | 出问题时只能现场改代码,越改越乱 |
关键概念逐条解释
- 弹性公网 IP:与实例解绑后还能绑定到另一台机器,换机器时不用改 DNS,是比“直接绑公网 IP”更值钱的选择。
- 云安全组 vs 主机防火墙:安全组在实例外面,由云平台执行,实例没起来也生效;主机防火墙在系统里面,规则随系统走。两层都留,别只做一层。
- 反向代理:Nginx 代表后端接收请求再转发,后端只监听
127.0.0.1,公网永远连不到应用端口。 - 健康检查:返回 200 且带上依赖状态(数据库能否连通)的接口,是滚动更新时判断“新版本能不能接流量”的唯一依据。
- TTL:解析记录的缓存时间。切 IP 前先把 TTL 调到 60 秒,等旧 TTL 过期再切,用户才不会分散打到两台机器上。
验证方法
# 1) 解析是否正确
dig +short example.com
# 2) 端口是否按预期监听(0.0.0.0 表示对外,127.0.0.1 表示仅本机)
sudo ss -lntp
# 3) HTTP 与 HTTPS 是否通,响应头是否符合预期
curl -I http://example.com
curl -I https://example.com
# 4) 应用进程与容器状态
systemctl status myapp --no-pager
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
# 5) 本机自测上游,确认不是 Nginx 的问题
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8000/healthz
五条命令全绿,说明“域名 → 网络 → 代理 → 应用”这条链路是通的;哪一条红,问题就锁定在哪一层。
常见坑
| 坑 | 现象 | 正确做法 |
|---|---|---|
| 只放行安全组,忘了主机防火墙 | 本机 curl 通,外部超时 | 两层规则同时放行,或明确只用一层 |
应用监听 0.0.0.0:3306 | 数据库被外网扫描爆破 | 监听 127.0.0.1,公网只留 80/443 |
| 没备案就配 443 | 国内节点域名访问被重置 | 先备案,或临时用海外节点与 IP 访问 |
| 容器日志无轮转 | 磁盘 100% 后系统拒绝写入 | 配置 log-opts max-size 与应用日志轮转 |
| 直接在服务器上改代码 | 无法回滚,改动去向不明 | 只部署产物,代码走 Git 与构建流程 |
| 没留健康检查接口 | 新版本起来但连不上库,流量已经切过去 | 健康检查里真连一次数据库或缓存 |
小结:上线是一条从 DNS 到数据的完整链路,先把组件、端口、检查项在纸上列清再动手;单机用 systemd 或 Compose,规模不大就别上 Kubernetes;每一层都要有可验证的命令和可回退的方案,跑通之后再去谈性能与高可用。