面试知识库
进阶

后端部署发布与回滚实战#

一句话答案#

后端发布核心是「小批量+可观测+秒回滚」:金丝雀放量观察核心指标,异常立即 rollback;回滚要区分代码/配置/DB/依赖四种路径,每种都有对应 SOP。

核心要点

一、发布全流程

代码合并 → CI构建(单测+镜像) → 推送镜像仓库 → 部署Staging验证
  → 金丝雀(5%流量) → 观察15min → 扩到30% → 观察15min → 全量发布
plaintext

每个阶段的门禁:

  • CI 阶段:单测通过率100%、代码扫描无P0漏洞、镜像体积检查
  • Staging:核心接口冒烟测试通过、无新增ERROR日志
  • 金丝雀:P99延迟无劣化、错误率无上升、业务指标无异常

二、三种发布策略对比

策略原理回滚速度风险适用场景
滚动发布逐个替换Pod分钟级新旧版本共存时间长常规无破坏性变更
蓝绿发布两套环境切流秒级(切回旧环境)资源成本翻倍核心服务大版本升级
金丝雀发布小流量验证再扩量秒级(摘除canary)需要流量染色能力高频迭代、需精细验证

三、回滚决策 SOP

四、发布事故案例集

案例1:金丝雀 OOM 导致批量 Pod 重启

  • 现象:金丝雀节点频繁重启,内存持续增长
  • 原因:新代码引入内存泄漏(未关闭的Stream),5%流量足以触发
  • 排查:kubectl top pod 看内存趋势 → jmap 导出堆 → MAT 分析
  • 处置:立即摘除金丝雀Pod,修复代码后重新发布
  • 教训:金丝雀阶段必须监控内存趋势,不能只看错误率

案例2:DB Migration 导致接口超时

  • 现象:全量发布后核心接口P99从50ms飙到3s
  • 原因:新增索引的DDL锁表,加上新代码依赖该索引但索引还未建完
  • 排查:SHOW PROCESSLIST 发现大量Waiting for table metadata lock
  • 处置:kill DDL → 代码回滚 → 非高峰期用pt-online-schema-change重建索引
  • 教训:DB变更与代码变更要解耦,先加索引再发代码

案例3:配置推送灰度不生效引发全量故障

  • 现象:修改超时配置,预期只推5台机器,实际全量生效导致下游被打爆
  • 原因:配置中心命名空间混淆,推送了default namespace
  • 排查:配置中心操作审计日志
  • 处置:立即回退配置版本(秒级)
  • 教训:配置变更也需要灰度能力,不同环境严格隔离namespace

案例4:依赖方升级导致序列化不兼容

  • 现象:发布后部分接口返回500,日志报序列化异常
  • 原因:上游服务升级了protobuf版本,新增字段unknown处理方式变了
  • 排查:对比发布前后的RPC调用日志,定位到特定字段解析失败
  • 处置:依赖方回滚 + 本方增加兼容性解析逻辑
  • 教训:依赖方升级要联调验证,protobuf要开启unknown字段保留

五、发布相关监控指标

指标含义告警阈值
error_rate_delta发布前后错误率变化>0.5%
latency_p99_delta发布前后P99变化>50%
pod_restart_countPod重启次数>2次/5min
rollback_count回滚次数>0(通知)
deployment_duration发布耗时>10min

六、Graceful Shutdown 与流量摘除

# K8s Pod 优雅关闭配置
lifecycle:
  preStop:
    exec:
      command: ["sh", "-c", "sleep 5"]  # 等待Service摘除endpoints
terminationGracePeriodSeconds: 30       # 给应用30s清理时间
yaml

关闭顺序:

  1. K8s 将 Pod 标记为 Terminating
  2. Service 从 endpoints 中摘除该 Pod(几秒延迟)
  3. preStop hook 执行(等待摘除完成)
  4. 发送 SIGTERM,应用开始优雅关闭
  5. 拒绝新请求,完成进行中请求
  6. 关闭连接池、释放资源
  7. 超过 terminationGracePeriodSeconds 则 SIGKILL
面试回答(2分钟版)

我们的发布流程是金丝雀模式。代码合并后CI自动构建镜像、跑单测和扫描,通过后部署到Staging做冒烟验证。验证通过进入金丝雀阶段,先放5%流量到新版本,观察15分钟看P99延迟、错误率和核心业务指标有无劣化,没问题再扩到30%,最终全量。回滚方面我会区分四种情况:代码问题用kubectl rollout undo秒级回滚;配置问题走配置中心版本回退;数据库变更提前准备反向脚本;依赖方问题启用降级或切备用地址。我们有个原则是”发布后15分钟内出现异常先回滚再排查”,不在故障现场犹豫。发布完成后持续观察发布前后的指标delta,重点看错误率变化和延迟劣化。Pod的优雅关闭也很重要,通过preStop hook等待流量摘除完成再关进程,避免请求打到正在关闭的实例。

追问与易错

追问方向:

  • “金丝雀怎么做流量染色?”→ 网关层在请求 Header 中标记版本标签(如 x-canary=true),通过 Istio VirtualService 或 Nginx weight 将带标签的流量路由到金丝雀 Pod
  • “回滚后数据不一致怎么办?”→ 新版本期间写入的数据需要评估影响范围,可能需要补偿脚本清理/修正脏数据;关键是发布前制定数据回滚预案
  • “数据库变更怎么做到可回滚?”→ 只做加法不做减法(加字段不删旧字段),用 pt-online-schema-change 避免锁表,大表变更走双写过渡期,提前准备反向 migration 脚本
  • “多服务同时发布依赖顺序怎么管?”→ 按依赖图拓扑排序发布(被依赖方先发),接口保持向前兼容(新版本兼容旧调用方),必要时通过版本协商确保兼容
  • “发布频率和稳定性怎么平衡?”→ 设定固定发布窗口(如每周二/四下午),大促前设变更冻结期,CI/CD 流水线自动化门禁(测试覆盖率/代码扫描)保证质量

易错点:

  • ❌ “回滚就是重新部署旧版本”——应该用rollout undo保留历史,而不是手动改镜像tag
  • ❌ “DB migration可以随便回滚”——删字段、改类型等破坏性变更通常不可逆
  • ❌ “金丝雀没问题就全量”——要观察足够长时间,内存泄漏等问题可能延迟暴露
  • ✅ 发布=变更,所有变更(代码/配置/DB/依赖)都要有对应回滚预案