第 18、19 篇搭好了 CI/CD 与 K8s 底座,这一篇是走到最后一步:按什么清单放行一个版本进生产。这份 checklist 来自我们四次大版本上线(含一次微服务拆分上线)的复盘沉淀,每次上线周会用它逐项打勾——没打完勾不发版。表格可直接抄走改造。
为什么需要 Checklist
上线事故的共性不是”技术难题”,而是”忘了”。第 29 篇会展开我们那次默认账号没禁用被扫描器盯上的事故——每一项 checklist 背后最好都站着一个真实教训。Checklist 的价值在于把记忆外化:凌晨两点发版的人不需要想起所有事,只需要照单执行。
一、安全
| # | 检查项 | 标准/做法 | 相关篇目 |
|---|---|---|---|
| S1 | JWT secret 强度与来源 | 86+ 位随机 Base64,K8s Secret 注入,禁止入库 | 第 21 篇 |
| S2 | 默认账号处置 | admin 改强密 + 禁用 user 演示账号,或首启强制改密 | — |
| S3 | HTTPS 全链 | TLS 终止在网关/Ingress,内部流量按需 mTLS | 第 19 篇 |
| S4 | 依赖 CVE 扫描 | dependency-check / Snyk 在 CI 阻断 High 以上 | 第 18 篇 |
| S5 | Swagger/管理端点收敛 | prod 关 Swagger;actuator 走独立管理端口 + NetworkPolicy | 第 20 篇 |
| S6 | CORS 与 CSP | 白名单域名;安全响应头过 securityheaders.com 检查 | — |
| S7 | 数据库账号最小权限 | 应用账号无 DDL 权限(Liquibase 用独立迁移账号) | 第 7 篇 |
| S8 | 敏感信息泄漏复查 | git 历史扫 secret;日志不打印令牌与密码 | 第 21 篇 |
二、数据库
| # | 检查项 | 标准/做法 |
|---|---|---|
| D1 | Liquibase 空库演练 | 镜像里跑 liquibase update,从零建库成功 |
| D2 | 生产副本演练 | 用生产量级快照跑全部 changeSet,记录耗时 |
| D3 | 大表变更锁评估 | 加索引用 CREATE INDEX CONCURRENTLY;改列类型评估锁与重写 |
| D4 | 前滚兼容设计 | 见下文”回滚预案”小节,双写/过渡期列 |
| D5 | 备份恢复演练 | 恢复一次备份到隔离环境并抽样验证,而不是只看备份”成功”日志 |
| D6 | 连接池与超时 | 池参数 = 压测值;statement timeout 开启防慢查询拖死连接 |
| D7 | 迁移账号分离 | 应用账号与 Liquibase 账号分权,迁移窗口外不可 DDL |
三、性能
| # | 检查项 | 标准/做法 |
|---|---|---|
| P1 | 压测基线 | staging 以 1.5 倍峰值流量压过,P99 达 SLA |
| P2 | 慢 SQL 清零 | 慢日志阈值内无未评估条目 |
| P3 | 缓存预热与命中率 | 热点数据预热脚本;hit ratio 看板就位 |
| P4 | JVM 与容器对齐 | MaxRAMPercentage=70;limit 与请求值合理差 |
| P5 | 前端首屏指标 | LCP/打包体积对比上一版无劣化 |
| P6 | 压测后资源回落 | 压测完池/CPU/GC 指标回落,无慢泄漏迹象 |
四、可观测
| # | 检查项 | 标准/做法 |
|---|---|---|
| O1 | 日志聚合 | JSON 格式落 Loki/ELK,traceId 贯穿 |
| O2 | 指标与看板 | Micrometer → Prometheus,RED 看板每服务一张 |
| O3 | 告警规则 | 错误率、P99、池占用、Pod 重启四条最低配,值班路由确认 |
| O4 | 链路追踪 | 采样率设定并验证跨服务串联 |
| O5 | 日志级别预案 | 运行时动态调级的方法演练过(actuator loggers) |
| O6 | 业务指标 | 订单量/支付成功率等业务大盘,故障时判断影响面的第一入口 |
五、K8s
| # | 检查项 | 标准/做法 |
|---|---|---|
| K1 | 镜像 tag | 禁 latest,用 git SHA/版本号,可追溯可回滚 |
| K2 | 探针 | readiness 指向真实依赖就绪(含 Liquibase 完成);liveness 不查外部依赖防误杀 |
| K3 | 资源限制 | requests/limits 显式设置,与 JVM 对齐 |
| K4 | HPA | 指标选 CPU 或自定义 QPS;min/max 与压测结论一致 |
| K5 | PDB | minAvailable 保证滚动更新与节点维护时容量 |
| K6 | 优雅停机 | server.shutdown: graceful + preStop sleep,配合 terminationGracePeriod |
| K7 | Ingress/TLS 证书 | 证书有效期监控,自动续期验证 |
| K8 | Config/Secret 与版本 | 配置变更走 GitOps PR,与镜像版本对应 |
六、回滚预案
上线方案的另一半是”怎么退回来”。两条腿:
镜像回滚(分钟级):
kubectl rollout undo deployment/order-service
# 或显式回退到版本 tag
kubectl set image deployment/order-service order=registry.mycompany.com/order-service:v2.3.1
前提是 K1 的版本化 tag 与流控放行。
Liquibase 前滚兼容(真正的难点):镜像回退了,数据库却回不去——新版本可能已执行 changeSet。所以数据库变更必须设计成”前滚兼容”:老代码在新 schema 上仍能正确运行。约定三则:
| 变更类型 | 前滚兼容做法 | 回滚时表现 |
|---|---|---|
| 加列 | 加 nullable 列,老代码忽略之 | 无影响 |
| 删列/改类型 | 分两个版本:先停用(代码不再读写),下个版本再删 | 删除动作发生在确认稳定后 |
| 改约束/重命名 | 双写过渡:新旧并存一个发布周期 | 老代码走旧列 |
核心思想:每一个发布版本,数据库 schema 只做”加法”或”无破坏”的变更,破坏性变更永远延后一个版本。这让”回滚镜像”成为永远安全的操作。执行顺序也有讲究:Liquibase 在 K8s 里作为 init container 或 Job 先跑(第 19 篇的部署结构),新 Pod readiness 通过后再接流量。
另准备一份”回滚决策单”:谁有权拍板回滚(值班 leader)、多长时间内异常自动触发讨论(错误率 > 2% 持续 5 分钟)、回滚后的数据补账流程(写了新 schema 字段的数据怎么处理)。上线前预演一次回滚,我们第一次预演就发现 preStop 缺失导致滚动更新丢请求,这种问题不能在生产首演。
Checklist 的工程化落地
清单贴在 wiki 上会死,工程化才能活。我们的三个做法:
第一,checklist 进 issue 模板。 发布单本身就是模板,六块清单作为 checkbox 出现,放行 = 所有框打完:
<!-- .github/ISSUE_TEMPLATE/release.md -->
## 发布单:vX.Y.Z
- [ ] S1 JWT secret 来源与强度复核
- [ ] S2 默认账号处置(改密/禁用)
- [ ] D1 Liquibase 空库演练通过
- [ ] D2 生产量级副本演练,耗时记录:___
- [ ] P1 staging 压测 1.5x 峰值,P99:___
- [ ] O3 告警规则验证(人为触发一条)
- [ ] K2 探针检查(liveness 无外部依赖)
- [ ] R 回滚预演完成,决策单签署:___
留空填数字的项(P99、耗时)逼着执行者真跑一遍,而不是凭记忆打勾。
第二,能自动化的项移进 CI。 人会忘,pipeline 不会。逐项搬运的进度:S4(CVE 扫描)、S8(secret 扫描)、K1(latest tag 检测)已完全自动化;D1(空库演练)在 CI 里起一个干净 Postgres 容器跑 liquibase update 已实现一半。原则:每季度把手工打勾最多的三项评估自动化,让人的注意力留给判断性检查。
第三,清单有 owner 和复盘回路。 每块清单指定责任人(安全块归安全接口人、K8s 块归平台组),上线次日复盘的固定议程是”本次哪些项执行有问题、哪些项该改写”。第 29 篇的事故如何长出新行:坑 5 锁表之后,D3 从”评估锁风险”改写成了明确指令”大表索引必须 CONCURRENTLY + runInTransaction=false”,三个月后同款变更在演练中就被拦下了。
上线日流程(浓缩版)
T-1d checklist 全项打勾;生产副本 Liquibase 演练完成;告警值班确认
T-0 金丝雀:先放 1 个 Pod(或 5% 流量)观察 30 分钟看板
T+30m 全量滚动更新;盯错误率/P99/池占用/订单大盘
T+2h 留观,每 30 分钟记录一次关键指标
T+1d 复盘会:本次 checklist 漏项进清单
上线当天的 pre-flight 快速核对(五分钟版,完整清单的抽样):
# 镜像与版本确认:tag 是版本号而非 latest
kubectl -n prod get deployment -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.template.spec.containers[0].image}{"\n"}{end}'
# 探针与资源确认
kubectl -n prod get deploy order-service -o yaml | grep -A3 -E 'livenessProbe|readinessProbe|resources:'
# 告警通道演练:人为触发一条测试告警,值班手机能收到
# 回滚演练:在 staging 执行一次 rollout undo,记录耗时(应 < 3 分钟)
小结
- 清单的价值是把”忘了”变成”照单执行”:安全、数据库、性能、可观测、K8s、回滚六大块逐项打勾再放行。
- 数据库是回滚的根:前滚兼容(加法式变更、破坏延后一版)让镜像回滚永远安全。
- 探针、优雅停机、版本化 tag 这三个 K8s 细节决定滚动更新与回滚的成色。
- 上线走金丝雀,指标盯四件套,次日复盘反哺清单——checklist 是活的资产。