路线图

面试 · 场景排查型

星辉 2026-07-02 阅读 13 min 2,712 字 路线图
面试 · 场景排查型 封面

目标:考察实战排障能力和系统性思维。从真实生产故障和踩坑经验提取场景,每题含排查决策树。 答题框架:现象 → 排查 → 根因 → 修复 → 治理(每题含决策树)。


1. 用户反馈"下单接口很慢"——你的排查步骤是什么?#

参考答案

决策树

text
用户反馈"下单很慢"
  ├─ Step 1: 确认影响范围(对照组破案)
  │   ├─ 是所有用户还是部分用户?(部分 → 可能是特定区域/客户端)
  │   ├─ 是所有接口还是只有下单?(只有下单 → 下单链路特有问题)
  │   ├─ 是持续性的还是偶发的?(偶发 → 可能是容量/抖动)
  │   └─ 对照组:找一个"正常"的服务对比,差异处就是根因方向
  ├─ Step 2: 看 Dashboard(黄金信号)
  │   ├─ P99 延迟是否确实上升?→ 是,看是哪段时间开始的
  │   ├─ 错误率是否也上升?→ 判断是"慢"还是"错"
  │   ├─ 流量是否有变化?→ 流量突增也会导致延迟上升
  │   └─ 饱和度如何?→ CPU throttle?连接池满?
  ├─ Step 3: Trace 分析(定位哪一跳慢)
  │   ├─ 打开 Tempo/Jaeger 看下单请求的完整链路
  │   ├─ 找到耗时最长的 span(是数据库查询?下游 RPC?)
  │   └─ 用 exemplar 从指标跳到具体慢请求的 trace
  ├─ Step 4: 定位到具体 Span
  │   ├─ 如果是 DB 慢 → 查 slow query log + 连接池 + 索引
  │   ├─ 如果是下游慢 → 查下游服务的 Dashboard
  │   ├─ 如果是网络 → 查丢包/重传/跨区延迟
  │   └─ 如果是自身处理慢 → 查 GC/log/CPU throttle
  ├─ Step 5: 关联最近变更
  │   ├─ 最近有没有发布?(查部署记录)
  │   ├─ 有没有配置变更?(ConfigMap/Secret/HPA)
  │   ├─ 有没有流量突增?(营销活动/上游重试)
  │   └─ 有没有基础设施变更?(节点轮换/DB 迁移)
  └─ Step 6: 修复 + 治理
      ├─ 止损:回滚/切流/扩容
      ├─ 根因:写 Postmortem
      └─ 治理:加告警、加 runbook、加压测

关键原则:从广到窄(影响面 → 服务 → 接口 → 具体调用),每层用证据说话。不要在表层打转——用对照组排除错误方向,用证据钉死每一层。


2. 凌晨 3 点告警:order-api p99 > 1s,你被 page。接下来每一步做什么?#

参考答案

决策树(时序)

text
T+0   Acknowledge 告警(表示"我看到了")
T+1   打开 Grafana Dashboard,确认:
      - 是真的延迟升高还是监控数据抖动?
      - 只影响 order-api 还是多个服务?
      - 错误率是否也在上升?
      - 流量是否有突增?
T+3   如果是真实故障:
      - 判断 SEV 级别(影响用户比例 → SEV-2 还是 SEV-3)
      - SEV-2 以上 → 拉事故群,任命 IC
T+5   初步诊断:
      - 最近有没有发布?(查部署记录)
      - 流量有没有突增?
      - 先看 Tempo trace 找 slowest span
      - 检查下游服务健康度
T+8   如果有明确嫌疑(如新发布的版本):
      - 优先回滚而非修复
      - IC 确认回滚命令 → 执行 → 观察指标是否回落
T+15  确认恢复 → 进入监控阶段
T+45  指标稳定 30 分钟 → Resolve → 排 Postmortem 时间

关键原则

  • 凌晨被叫醒最忌讳"先自己排查一小时"——效率低、业务在挂
  • 有回滚先回滚,根因可以明天在 staging 复现
  • 不确定严重度时按最严重预估,确认没问题再降级

治理:每次凌晨 page 后 review——这个告警是否真的需要半夜叫醒?如果是误报,调整阈值;如果是真故障,复盘响应速度。


3. ArgoCD sync 一直卡在"Progressing",Pod 显示 Pending。排查步骤?#

参考答案

决策树

text
kubectl describe pod <pending-pod> | grep -A5 Events

Events 可能显示:
  ├─ "FailedScheduling: 0/5 nodes are available: 5 Insufficient cpu"
  │   → 节点资源不足
  │   → 检查节点容量 + HPA + Cluster Autoscaler
  │   → 验证 Pod 的 resources.requests 是否合理
  ├─ "FailedScheduling: node(s) had untolerated taint"
  │   → Pod 缺 toleration
  │   → 检查节点 taint + Pod toleration 配置
  │   → ★ 排查所有同 nodeSelector 的组件(一个缺陷往往是一类)
  ├─ "FailedScheduling: node(s) didn't match Pod's node affinity"
  │   → nodeSelector/affinity 不匹配
  │   → 检查是否有可用节点
  │   → 检查节点标签是否正确
  ├─ "FailedScheduling: didn't trigger scale-up due to missing matching nodepool"
  │   → 自动扩缩器找不到匹配的节点池
  │   → 检查 Karpenter/CA 的 Provisioner 配置
  │   → 检查节点池是否支持所需的 instance type
  ├─ "FailedMount" 或 PVC 相关
  │   → PV/PVC 绑定问题
  │   → kubectl describe pvc
  │   → 检查 StorageClass 是否有可用 PV
  └─ ImagePullBackOff
      → 镜像拉不下来
      → 检查 image name 拼写
      → 检查 registry 可达性(跨境源?换 mirror)
      → 检查 imagePullSecrets

A 旗舰案例FailedScheduling: untolerated taint {system-pool=true: NoSchedule} → 发现 istio gateway 绑了 system-pool nodeSelector 但没配 toleration → 平时 NoSchedule 不驱逐运行中的 Pod,节点轮换时才暴露。

治理:发现一个组件缺 toleration,必须排查所有同 nodeSelector 的组件——"一个缺陷往往是一类"。


4. 一个新版本灰度 10% 后,错误率从 0.1% 升到 3%,但还没触发告警。你的决策?#

参考答案

立即决策:停止灰度,回滚灰度部分。

推理

  • 错误率从 0.1% → 3% = 30 倍增长。这不是正常的灰度波动
  • 虽然 3% 的绝对错误率可能还没触发固定阈值的告警(如告警设了 5%),但趋势和对比说明问题严重
  • 别把错误率当错误量放大:错误率是比率,不会随流量倍数放大。全量后新版本自身错误率仍是 ~3%(不是 30%),变的是两件事——① 错误的绝对量放大约 10 倍(新版本承接的流量从 10% 扩到 100%);② 受影响面从 10% 用户扩到 100% 用户
  • 算一笔账:当前服务整体错误率 ≈ 0.9×0.1% + 0.1×3% = 0.39%;全量后 ≈ 100%×3% = 3%(整体错误率抬升约 7.7 倍,绝对错误量抬升更多)。3% 持续 = SEV 级事故
  • 灰度阶段出现这种级别的退化,表明发布有严重质量问题

后续步骤

  1. 立即缩小灰度到 0% 或回滚
  2. 分析灰度期间 3% 错误的具体类型和根因
  3. 修复后在 staging 验证
  4. 下次灰度设定更保守的阈值:错误率增长 > 2 倍即暂停

治理

  • 灰度告警应该用"相对基线"而非"绝对阈值"——错误率 > 基线 2x 就该暂停
  • 自动回滚触发条件应该包含"灰度期间错误率相对上升"
  • 灰度观察期要足够长,慢 SQL 类问题在小流量时看不出来

5. 发布完成后,发现所有新 Pod 都是 CrashLoopBackOff。如何快速止血和排查?#

参考答案

止损第一:如果有指定回滚版本的机制,先回滚(恢复旧版本保证业务可用)。

排查决策树

text
新 Pod CrashLoopBackOff
  ├─ kubectl logs <pod> --previous
  │   ├─ 有日志 → 读日志定位错误
  │   │   ├─ "connection refused" → DB/Redis/下游连不上
  │   │   │   → 检查 Service 名是否正确(不同集群可能不同)
  │   │   ├─ "permission denied" → RBAC/安全上下文问题
  │   │   ├─ "config error" → ConfigMap/Secret 字段缺失或格式错误
  │   │   ├─ "OOMKilled" → 内存超限,调 limits 或查内存泄漏
  │   │   └─ "class not found" → 镜像版本不对
  │   └─ 无日志 → 容器在启动早期就挂了
  │       → 检查镜像是否正确
  │       → 检查 entrypoint/command
  ├─ kubectl describe pod <pod>
  │   ├─ "Liveness probe failed" → 健康检查配置与新版不兼容
  │   ├─ "Readiness probe failed" → 依赖未就绪
  │   └─ "Back-off restarting failed container"
  ├─ kubectl get events -n <ns> --sort-by='.lastTimestamp'
  │   └─ 看最近事件找线索
  └─ 确认回滚完成后,分析新版与旧版的差异

治理

  • CI/CD 流水线加"镜像启动测试"——发布前在 staging 验证 Pod 能正常启动
  • 健康检查配置要和代码版本匹配——liveness probe 不要配得太激进
  • 回滚路径必须预演过——不能在事故时才发现回滚也挂

6. 生产环境 Redis 突然 CPU 100%,业务开始超时。排查和止损步骤?#

参考答案

止损(30 秒内)

text
1. 如果 Redis 有 replica → 手动 failover 到 replica
2. 如果有读/写分离 → 先把读流量切到 replica
3. 如果没有 HA → 重启 Redis(接受短暂不可用)

排查决策树

text
redis-cli 连上:
  ├─ INFO commandstats → 看哪种命令最多(可能是 KEYS * 或大 HGETALL)
  ├─ SLOWLOG GET 20 → 看最近的慢命令
  ├─ CLIENT LIST → 看连接数是否异常
  ├─ INFO memory → 看是否在 evict key
  └─ INFO persistence → 看是否在 RDB/AOF 写盘(也会拉高 CPU)

根因可能:
  ├─ 某服务上线引入了 KEYS *(生产禁用!)
  ├─ 大 key 被频繁访问(HGETALL 一个 10MB 的 hash)
  ├─ 连接风暴(某服务快速重连)
  ├─ 内存满触发 eviction 导致 CPU 飙高
  └─ 持久化触发(RDB bgsave 时 fork 大内存进程)

A 旗舰案例:混沌工程 GameDay 中 kill Redis master → sentinel failover 后新 master CPU 瞬间拉满 → 发现大量连接在短时间建连,客户端没有连接池限流。

治理

  • Redis 禁用危险命令(KEYS、FLUSHALL)在生产环境
  • 大 key 监控告警(超过 10KB 的 key 报警)
  • 客户端连接池配置上限
  • 持久化策略评估——eviction 时不要触发 RDB

7. Prometheus 查询超时,Grafana Dashboard 一片红。如何排查?#

参考答案

决策树

text
Step 1: 确认 Prometheus 是否活着
  kubectl -n monitoring get pod -l app=prometheus
  kubectl -n monitoring logs prometheus-0 | tail -20
  → 看是否有 OOM / WAL corruption / disk full

Step 2: 检查查询是否有高基数
  └─ 最近有没有新增高基数 label?
     如 user_id、request_id 被放进了 label
     → 序列数爆炸 → Prometheus 内存 OOM
  → 查 Prometheus TSDB 状态: curl localhost:9090/api/v1/status/tsdb

Step 3: 检查存储
  └─ Prometheus TSDB 保留时间 + 当前 PVC 使用量
     磁盘满了 → Write-Ahead Log 写失败 → 整体卡住

Step 4: 检查 Recording Rules
  └─ 如果有长时间窗口的 Recording Rule(如 30d)在实时计算
     → 评估太慢 → 阻塞其他查询
  → 看 Prometheus Rules 页面的 evaluation time

Step 5: 紧急止血
  └─ 如果确认是查询卡死 → 重启 Prometheus
     (丢 2h 内存数据但恢复服务)
  └─ 如果是高基数 → 删除违规的指标序列

治理

  • 高基数 label 必须在 PR review 时拦截——user_id/request_id 绝不进 label
  • Prometheus 资源 limits 要够(建议 ≥ 8Gi 内存)
  • 定期检查序列数:prometheus_tsdb_head_series 告警
  • 长窗口 Recording Rule 用 30d 时要评估性能影响

8. 用户反馈"A 功能完全正常,B 功能全部 502"。你怎么开始排查?#

参考答案

关键线索A 正常 + B 全挂 → 不是整体故障,是 B 功能链路上的特定组件有问题。对照组已经天然存在——A 就是对照组。

决策树

text
Step 1: 区分 A 和 B 的调用链差异
  ├─ B 服务有没有自己的独立后端?
  ├─ B 服务是否依赖某个 A 不依赖的下游/数据库?
  └─ B 服务是否有独立的 Ingress/Gateway 配置?

Step 2: 定位 502 来源
  ├─ 是 NLB/ALB 返回的 502 → 查 target group health
  ├─ 是 Ingress/Gateway 返回的 502 → 查 upstream service endpoints
  └─ 是应用本身返回的 502 → 查应用日志

Step 3: 常见根因
  ├─ B 服务的 Pod 全挂了(OOM/deployment 误删)
  ├─ B 服务的 Service endpoints 为空(selector 不匹配)
  ├─ B 服务的网络策略阻断了流量
  └─ B 服务的 NLB target group health check 失败

Step 4: 对照检查
  kubectl get endpoints <B-service>  → 是否为空?
  kubectl get pod -l app=<B>         → Pod 是否 Running?
  kubectl describe svc <B-service>   → selector 是否正确?

A 旗舰案例:passport 全挂但 goalfymax 正常 → 对照发现 passport 直连 NLB,goalfymax 走 CloudFront → 问题在 passport → NLB 链路 → 发现 istio gateway Pod 永久 Pending。

治理:服务依赖图要预先画好,出事时能快速识别"哪些服务共享依赖"。


9. 告警群突然收到 50 条同一个服务的告警。你怎么判断这是"一次发布导致的真故障"还是"监控系统抽风"?#

参考答案

决策树

text
判断逻辑:
  ├─ 1. 看告警时间分布
  │    ├─ 同一秒同时触发 → 更像是监控链路问题
  │    └─ 逐步蔓延(先 1 个、30s 后 5 个、1min 后 50 个) → 像真实故障传播
  ├─ 2. 看 Dashboard 数据
  │    ├─ 指标是否真的异常?(错误率/延迟真的在升高)
  │    └─ 还是只有告警在响但指标正常? → Alertmanager 自身问题
  ├─ 3. 看是否有相关发布
  │    ├─ 查部署记录 → 最近 30min 有没有发布?
  │    └─ 如果有 → 优先怀疑是发布导致 → 准备回滚
  ├─ 4. 看影响面
  │    ├─ 能否在 staging/qa 复现? → 如果能 → 是代码问题
  │    └─ 只有 prod 有? → 可能是容量/特定流量模式
  └─ 5. 看告警是否有 Grouping 异常
       └─ Alertmanager 的 group_by 配置是否合理?
          一次发布挂 50 个 pod → 应该合并成 1 条告警

快速判断:打开 Grafana 看 5 分钟前的真实错误率——如果错误率确实在暴涨,是真故障;如果错误率平稳,是告警系统自身问题。

治理

  • Alertmanager 的 group_by 配置要合理——同 alertname + namespace + cluster 合并
  • 一次发布挂 50 个 Pod 应该是 1 条告警,不是 50 条
  • 告警数量异常本身就是告警——"5 分钟内告警 > 20 条"应该触发审查

10. 你发现一个服务在过去 2 小时一直在缓慢消耗 Error Budget(burn rate ~2x)。没有告警触发。你怎么处理?#

参考答案

为什么没告警:P0 告警阈值是 14.4x,P1 是 6x。2x 的燃烧率只触发 P2(Dashboard 级别,不发通知)。

但这种"缓慢泄漏"同样危险

  • 2x 持续一整天 = 消耗了 2 天的 Error Budget
  • 如果持续一周没人发现 → 月度 Budget 会超支

处理步骤

  1. 先看是什么在消耗:是间歇性 spike 还是持续劣化?
  2. 拉 Tempo 看 slow traces,定位瓶颈
  3. 检查是不是有新的部署或流量模式变化
  4. 如果是低频的间歇性问题 → 降低 P1 告警阈值(6x → 3x),或增加 12h 窗口告警
  5. 记入 Error Budget 回顾议题:这种"慢烧"场景告警是否覆盖?

治理

  • 不能只配 P0 告警。P1(6x)和 P2(1x)同样重要——P2 是雷达,告诉你"有问题在酝酿"
  • P2 告警要有可见的出口——不能只放 Dashboard,配日报或周报邮件
  • 每月 Error Budget 回顾要看"慢烧"模式——哪些 Budget 消耗没有触发 P0/P1

11. GitOps 仓库 merge 了一个 HTTPRoute 变更,但集群内路由没更新。排查步骤?#

参考答案

决策树

text
Step 1: 检查 ArgoCD Application 状态
  argocd app get <app-name>
  → 看 Sync Status: OutOfSync 还是 Synced?
  → 看 Health Status: Healthy / Degraded?

Step 2: 如果是 OutOfSync 但没自动同步
  → 检查 syncPolicy.automated 是否开启
  → 主力 ArgoCD 常设手动 sync → merge 后需手动触发
  → kubectl patch application -n argocd <app> --type=merge --patch '{"operation":{"sync":{"revision":"HEAD"}}}'

Step 3: 如果是 Synced 但实际路由无效
  → kubectl get httproute -n <ns> <name> -o yaml
  → 检查 field 是否正确(Gateway API vs Istio VS 是两套路由!)
  → HTTPRoute 管南北流量,VirtualService 管东西流量

Step 4: 验证路由是否生效
  → curl -H "Host: xxx" <gateway-ip>
  → 看实际路由到哪个 backend

Step 5: 如果是 ArgoCD AutoSync 卡住
  → 看 Application 的 Conditions
  → ComparisonError / SyncError
  → 可能是 CRD 太大超过 256KB annotation 上限 → 加 ServerSideApply

踩坑大全 1.10:K8s Gateway API HTTPRoute 和 Istio VirtualService 是两套独立路由——改 HTTPRoute 可以影响南北流量(外部进来),但不影响东西流量(Pod-to-Pod),反之亦然。设计灰度/AB/PR 隔离方案时必须双轨规划。

治理:非镜像类 gitops 改动(HTTPRoute、Service、ConfigMap)merge 后必须手动触发 sync,不能假设 ArgoCD 会自动同步。


12. 一个服务突然开始报大量的 5xx,但 CPU/内存看起来都正常。怎么排查?#

参考答案

决策树

text
CPU/内存正常 + 5xx 高 → 排除自身过载 → 往下游或网络方向查

排查路径:
  ├─ 1. 看 5xx 的具体类型
  │    ├─ 502 Bad Gateway → 上游从下游收到的响应无效
  │    ├─ 503 Service Unavailable → 服务临时过载或维护
  │    └─ 504 Gateway Timeout → 下游响应超时
  ├─ 2. 如果是 502/504 → 大概率是下游问题
  │    ├─ 查下游服务的 Dashboard
  │    ├─ 查 Tempo trace 看失败在哪个 span
  │    └─ 查下游的连接池是否耗尽(连接数 vs max)
  ├─ 3. 如果是 503 → 可能是自己过载
  │    ├─ 检查线程池队列长度(CPU 正常但队列满 → 阻塞等待而非计算)
  │    └─ 检查 Rate Limiting 是否触发
  ├─ 4. 关联变更
  │    ├─ 有没有 DNS 变更?
  │    ├─ 证书过期?
  │    ├─ NetworkPolicy 变更?
  │    └─ 下游服务发布?
  └─ 5. 隐式错误检查
       ├─ 服务返回 200 但业务结果错?
       └─ 检查业务层埋点

治理:5xx 告警要区分类型——502/503/504 各有不同的根因方向。告警 annotation 里包含错误类型分布。


13. 一台 K8s node 变成 NotReady。你的排查流程?#

参考答案

决策树

text
kubectl describe node <node-name>

看 Conditions:
  ├─ MemoryPressure=True → 节点内存不足
  ├─ DiskPressure=True → 节点磁盘空间不足
  ├─ PIDPressure=True → 进程数超限
  └─ NetworkUnavailable=True → CNI 插件问题

看具体根因:
  ├─ kubectl get node <node> -o json | jq '.status.conditions'
  ├─ 登录节点: journalctl -u kubelet --since "10 min ago"
  └─ 检查: kubelet 是否活着?CNI 是否正常?磁盘是否满了?

常见根因:
  ├─ /var/lib/kubelet 磁盘写满(日志/image layers)
  ├─ CNI 插件 CrashLoop(VPC CNI / Calico / Cilium)
  ├─ kubelet 证书过期
  ├─ containerd/docker 挂了
  └─ 内核 oom killer 影响了 kubelet

A 旗舰案例:新节点全 NotReady → CNI DaemonSet CrashLoop → MissingIAMPermissions → 追查到 CNI 走 IRSA 但 addon 更新中间态导致 IRSA 临时失效 → fallback 到没配 CNI policy 的 node role → 形成自锁死循环。

双层归因

  • 触发因素:addon 更新中间态 + IRSA 短暂失效(偶发)
  • 结构性缺陷:node role 没配 CNI policy 兜底(可以根治)
  • 真正的 Action Item:给所有 EKS node role 普配 CNI policy 兜底

14. 部署后数据库连接池瞬间打满,所有请求排队等待。排查和止损?#

参考答案

止损(按优先级)

text
1. 连接数超限 → 调大 max_connections(临时,快速止血)
2. Kill 长时间 idle 的连接
3. 如果确认是新版本的问题 → 回滚

排查根因

text
1. 新版本是否有连接泄漏?
   → 在代码里检查:连接是否在 finally 中正确归还

2. 是否有 N+1 查询?
   → Tempo trace 看单次请求发了多少次 DB 查询
   
3. 连接池配置是否合理?
   → maxPoolSize = ?
   → 等待队列满了之后的行为(fail-fast 还是无限排队?)

4. 新版是否导致更多并发请求打过来?
   → 看 QPS 变化

A 旗舰案例:N+1 查询 + 连接池满 → 无慢查询的请求也被阻塞(因为连接池没可用连接) → 整个服务不可用。单次事件消耗月度 Error Budget 的 68%。

治理

  • DB 连接数监控告警(连接数 > 80% 告警)
  • PR 流程加 SQL lint 检查(防 N+1)
  • staging 复制 prod 1/10 真实流量压测
  • 连接池配置 fail-fast(队列满立即拒绝,不无限等待)

15. 你发现 staging 环境的监控告警已经 75 天没有触发过了。这正常吗?怎么排查?#

参考答案

不正常——即使是 staging 环境,也应该偶尔有真实的告警测试或至少 Watchdog 心跳。

排查步骤

text
1. 主动触发一条测试告警
   → 写一条 expr: vector(1) 的规则
   → 看能否在 Alertmanager 和钉钉看到

2. 如果看不到:
   ├─ 规则是否加载?→ Prometheus Rules API
   ├─ ruleSelector 是否匹配?→ staging 的 rule label 是否和 selector 一致
   ├─ Alertmanager 是否可达?→ Prometheus targets 页面
   └─ 钉钉 webhook 是否有效?→ token 过期?关键词白名单?

3. 常见根因:
   ├─ staging 的 ruleSelector 用 release: kube-prometheus → 但规则没打这个 label
   ├─ staging 的 Alertmanager 整体挂了 → 没死机开关发现
   └─ 钉钉群 token 已失效

4. 修复后验证:
   └─ 主动制造一次 firing → 确认全链路推送到群

A 旗舰教训:sandbox staging 告警因 label 不匹配 ruleSelector 75 天从未生效。发现时才知道"根本没有告警覆盖"。所有告警上线必须主动制造一次 firing 验证

治理

  • 告警上线 checklist 包含"主动制造一次 firing"
  • 每周巡检脚本对账:git 里的规则数 vs Prometheus 加载的规则数
  • 每个 Alertmanager 必须有死机开关(Watchdog)
  • 告警数量作为团队 KPI——长期 0 触发 = 可能是规则失效

16. 某服务 Error Budget 还剩 60%,但开发想发一个"很重要的新功能"。你的决策?#

参考答案

决策:可以发,但要走标准灰度流程。

推理

  • Budget 60% 处于"正常发布"区间(>50%)
  • "很重要的新功能"不是特殊理由——Budget 机制的目的就是让发布决策数据化
  • 但要确认:灰度策略合规、观察指标明确、回滚路径清晰

注意事项

  1. 灰度必须分阶段:1% → 10% → 50% → 100%
  2. 每阶段观察 Error Budget burn rate
  3. 如果灰度期间 burn rate > 6x,立即停止
  4. 如果发布后 Budget 掉到 25% 以下,下次发布需要 review

治理:Error Budget 机制的核心是"数据说话"——Budget 充足就放行,不看"功能多重要"。如果开发觉得"重要"就可以绕过规则,Budget 机制就失效了。

反过来问(面试常见追问):如果 Budget 已经耗尽、正在冻结,又来了一个必须上的紧急安全修复怎么办?——见 Q23。关键预告:错误预算政策必须预留紧急豁免例外,冻结冻的是功能发布,不是安全补丁/数据修复/合规这类紧急修复。


17. 用户报告"凌晨 2 点服务不可用",但当时没有任何告警。怎么排查?#

参考答案

决策树

text
Step 1: 确认用户报告是否真实
  ├─ 查 Grafana 看凌晨 2 点的指标
  │   ├─ 错误率有上升?→ 真实故障
  │   └─ 指标正常?→ 可能是用户侧问题或监控盲区
  └─ 查日志看有没有 ERROR

Step 2: 如果是真故障但没告警
  ├─ 告警规则是否正确?
  │   ├─ ruleSelector 是否匹配?→ 75 天没生效的坑
  │   ├─ for 时间太长?→ 故障短于 for 时间
  │   └─ 阈值设太高?
  ├─ 告警链路是否正常?
  │   ├─ Prometheus 当时在运行吗?
  │   ├─ Alertmanager 当时在运行吗?
  │   └─ 钉钉 webhook 当时通吗?
  └─ 告警是否被 silence 了?
      └─ 查 silence 历史

Step 3: 如果是监控盲区
  ├─ 凌晨 2 点是否有真实流量?
  │   ├─ 有流量 → 真实用户受影响
  │   └─ 无流量 → 可能是合成监控盲区
  ├─ 是否有黑盒监控覆盖?
  │   └─ 没有黑盒 → 凌晨无流量时无信号
  └─ 死机开关是否在工作?

Step 4: 修复 + 治理
  ├─ 补告警规则
  ├─ 加黑盒监控(cuj-prober 7×24 拨测)
  ├─ 加死机开关
  └─ Postmortem:为什么监控盲区存在

治理

  • 黑盒监控是真实流量的补充——凌晨无流量时唯一信号
  • 死机开关必须有——监控自己挂了要知道
  • 每月 review 告警覆盖率——哪些故障应该有告警但没有

18. Pod 频繁重启(每 5 分钟一次),但业务看起来正常。需要处理吗?#

参考答案

需要处理——频繁重启是隐患,即使业务"看起来正常"。

决策树

text
Step 1: 确认重启原因
  kubectl describe pod <pod> | grep -A5 "Last State"
  ├─ OOMKilled → 内存 limit 太低或内存泄漏
  ├─ Liveness probe failed → 健康检查配错或应用卡顿
  ├─ Error → 应用代码异常退出
  └─ Evicted → 节点资源不足

Step 2: 评估业务影响
  ├─ 重启时 in-flight 请求会失败吗?
  │   └─ 没有 preStop hook → SIGTERM 立即断连
  ├─ 重启时新请求会路由到这个 Pod 吗?
  │   └─ readiness probe 没及时摘除 → 流量丢失
  └─ 重启频率高会导致连接池抖动

Step 3: 修复
  ├─ OOMKilled → 调内存 limit 或查内存泄漏
  ├─ Liveness probe → 调整超时/间隔
  ├─ 加 preStop hook → 优雅终止
  └─ 加 PDB → 防止同时重启多个

治理

  • Pod 重启次数告警(> 3 次/小时)
  • preStop hook 是必须的——不配就是 in-flight 请求全挂
  • readiness probe 要先于 liveness probe 失败——先摘流量再重启

19. 集群里某个 namespace 的资源使用率突然飙升,影响其他 namespace。怎么处理?#

参考答案

决策树

text
Step 1: 止损——隔离影响
  ├─ 识别是哪个 namespace
  ├─ 如果有 NetworkPolicy → 临时限制其网络
  ├─ 如果有 ResourceQuota → 检查是否生效
  └─ 必要时 scale down 该 namespace 的 Pod

Step 2: 定位根因
  ├─ kubectl top pod -n <ns> → 找出最耗资源的 Pod
  ├─ 看是否有异常流量(DDoS?循环调用?)
  ├─ 看是否有新发布引入 bug
  └─ 看是否有 OOM 导致频繁重启

Step 3: 长期治理
  ├─ ResourceQuota:每个 namespace 设 CPU/内存上限
  ├─ LimitRange:每个 Pod 设默认 requests/limits
  ├─ NetworkPolicy:限制 namespace 间流量
  ├─ PriorityClass:核心服务高优先级
  └─ 节点池隔离:核心服务用专用节点池

A 旗舰案例:某服务内存 HPA 误配,副本从 4 扩到 13,吃掉专用节点池所有资源,导致 maxSurge 的 Pod Pending,发版超时。

治理:每个 namespace 必须配 ResourceQuota + LimitRange。没有资源限制的 namespace 是"定时炸弹"。


20. 数据库主从延迟突然增大,从库 lag > 60s。怎么排查?#

参考答案

决策树

text
Step 1: 确认影响
  ├─ 从库是否在提供读服务?→ 是 → 用户可能读到旧数据
  ├─ 从库是否用于故障切换?→ 是 → HA 可能失败
  └─ lag 持续增大还是稳定?

Step 2: 定位根因
  ├─ 主库写入量突增?
  │   ├─ 看主库 QPS
  │   └─ 看是否有批量写入/DDL
  ├─ 从库性能问题?
  │   ├─ 从库 CPU/IO 是否满
  │   ├─ 从库是否有慢查询阻塞复制
  │   └─ 从库规格是否低于主库
  ├─ 网络问题?
  │   └─ 主从间网络延迟
  └─ 复制配置问题?
      ├─ 单线程复制 → 大事务会阻塞
      └─ 并行复制配置不当

Step 3: 止损
  ├─ 如果从库用于读 → 临时把读流量切回主库
  ├─ 如果 lag 持续增大 → 暂停从库的非必要查询
  └─ 如果是 DDL 导致 → 等待 DDL 完成

Step 4: 修复
  ├─ 启用并行复制(MySQL: slave_parallel_workers)
  ├─ 从库规格升级
  ├─ 长事务优化
  └─ 主库写入分批

治理

  • 复制延迟告警:lag > 30s 告警,lag > 60s page
  • 从库不承接关键读(除非 lag < 1s)
  • DDL 必须在低峰期执行
  • 定期演练主从切换

21. 钉钉群里的告警突然不响了,但 Prometheus 和 Alertmanager 都看起来正常。怎么排查?#

参考答案

决策树

text
Step 1: 确认告警是否真的没发
  ├─ Alertmanager UI 看是否有 firing 告警
  │   → 有 firing 但钉钉没收到 → 通知链路问题
  │   → 没有 firing → 告警规则问题
  └─ Prometheus UI 看告警状态

Step 2: 通知链路排查
  ├─ webhook-dingtalk 是否活着?
  │   kubectl -n monitoring get pod -l app=webhook-dingtalk
  ├─ 钉钉 webhook URL 是否有效?
  │   → token 被管理员删/重建 → 静默失效
  │   → curl 测试 webhook
  ├─ 钉钉群关键词白名单?
  │   → firing 含"告警"过关
  │   → resolved 只含"恢复"被拒(常见坑!)
  └─ Alertmanager 路由配置是否正确?

Step 3: 主动测试
  ├─ curl webhook-dingtalk 发测试消息
  ├─ 看钉钉群是否收到
  └─ 如果收不到 → 钉钉 API 返回什么错误码

Step 4: 常见根因
  ├─ 钉钉 token 静默失效(管理员操作)
  ├─ resolved 通知被关键词拦截
  ├─ webhook-dingtalk pod 挂了
  ├─ Alertmanager 配置被覆盖
  └─ 钉钉群被解散/机器人被移除

A 旗舰教训:resolved 通知黑洞——firing 含"告警"关键词过关,resolved 只含"恢复"被钉钉拒绝,业务方以为告警没了实际从没收到恢复消息。

治理

  • 钉钉群关键词覆盖"告警/运维/恢复"三个全集
  • 每周主动发测试告警验证链路
  • 死机开关(Watchdog)作为告警链路的持续验证
  • 切群/换 adapter 时必须 curl 测试 firing + resolved 两类消息

22. 一个服务最近一周 MTTR 明显上升(从 15 分钟到 45 分钟)。怎么分析原因?#

参考答案

决策树

text
Step 1: 数据分析
  ├─ MTTR 上升是突然的还是渐进的?
  ├─ 是所有事故的 MTTR 都上升,还是某几次长事故拉高均值?
  └─ 哪类事故的 MTTR 上升最明显?

Step 2: 分解 MTTR
  MTTR = TTD(检测时间)+ TTA(响应时间)+ TTM(修复时间)
  ├─ TTD 上升 → 告警滞后或缺失
  ├─ TTA 上升 → on-call 响应慢
  └─ TTM 上升 → 修复难度增加

Step 3: TTA 上升的原因
  ├─ on-call 人员不熟悉系统(新人?)
  ├─ 告警 Runbook 过时或缺失
  ├─ 升级链不畅(找不到能处理的人)
  └─ 告警太多导致疲劳(先观察 5 分钟看是不是误报)

Step 4: TTM 上升的原因
  ├─ 事故复杂度增加(新架构?新依赖?)
  ├─ 回滚路径不通畅(镜像被清?配置丢失?)
  ├─ 排障工具不熟悉(trace 看不懂?日志找不到?)
  └─ 跨团队协调耗时

Step 5: 治理
  ├─ 补 Runbook
  ├─ 培训 on-call 人员
  ├─ 简化回滚流程
  ├─ 混沌工程演练(提升修复熟练度)
  └─ Postmortem 分析每次长事故的瓶颈

治理:MTTR 是 SRE 的核心 KPI,必须每月 review。MTTR 上升是组织能力退化的信号,不能等闲视之。


23. Error Budget 已耗尽、发布正在冻结中,此时来了一个必须马上上线的紧急安全修复(比如正在被利用的 0day)。你怎么决策?#

参考答案

这是错误预算政策成熟度的分水岭题——考的不是"敢不敢破例",而是"政策本身有没有为破例预留合法通道"。

决策:上。但走紧急变更通道——不是绕过流程,是走另一条更严的流程。

核心认知:冻结冻的是"功能发布",不是"紧急修复"。

  • 错误预算耗尽 → 冻结的目的是"停止用新功能继续消耗可靠性、把工程力转向修稳定性"。
  • 安全 0day、正在丢数据的 bug、合规红线、缓解正在发生的事故——这些不修的风险远大于发布的风险。用"预算耗尽"挡住安全补丁,是把政策当教条。
  • 成熟的错误预算政策必须预写豁免条款:安全补丁 / 数据修复 / 合规变更 / 事故缓解可豁免冻结。豁免不是"谁喊重要谁上",而是事先定义好的类别 + 明确的审批人

决策树

text
紧急上线请求
  ├─ 1. 判定是否属于豁免类别
  │    ├─ 安全漏洞正被利用 / 数据正在损坏 / 合规硬要求 / 事故缓解 → 属于,继续
  │    └─ "很重要的新功能" → 不属于,冻结照旧(这是 Q16 的场景,不是本题)
  ├─ 2. 走紧急变更审批(比常规更严,不是更松)
  │    ├─ 记录:为什么必须现在上、不上的风险、变更范围
  │    ├─ 审批人:SRE Lead + 安全/业务 owner 签字背书
  │    └─ 最小化变更面:只带这个修复,绝不夹带其他功能("搭便车"是大忌)
  ├─ 3. 更严的灰度 + 回滚预案(预算已耗尽,容错为零)
  │    ├─ 灰度更慢更细:1% → 观察更久 → 逐级放
  │    ├─ 回滚路径预先验证过(不能事故时才发现回滚也挂)
  │    ├─ 专人盯 burn rate,任何异常立即回滚
  │    └─ Feature flag 兜底:能秒级关闭最好
  └─ 4. 事后
       ├─ 这次紧急消耗计入 Budget,但单独标记(区分"功能挥霍"和"安全必需")
       ├─ Postmortem:为什么预算会耗尽到"连安全修复都要挣扎"的地步
       └─ 复盘豁免流程本身是否顺畅

反模式

  • 教条冻结:拿"预算耗尽"当免死金牌拒绝安全补丁 → 组织被 0day 打穿,政策本身成了风险源。
  • 借豁免夹带私货:以"紧急"为名把攒着的功能一起发 → 豁免通道被滥用,下次没人信。
  • 紧急就跳过灰度直接全量:预算已为零,一旦这个"修复"本身有 bug,直接 SEV-1。越紧急越要小步。

治理:错误预算政策文档里必须写清三件事——① 豁免类别清单;② 每类的审批人;③ 紧急通道的强制约束(最小变更面 + 强制灰度 + 回滚预案预验证)。没有预写豁免的冻结政策,第一次遇到 0day 就会被现场推翻,政策公信力归零。


答题技巧:场景排查题的关键是结构化决策树。不要"先做 X 再做 Y"的线性思维,要"如果 A 则 X,如果 B 则 Y"的分支思维。每一步都用证据说话,不猜。框架:现象 → 排查(决策树)→ 根因 → 修复 → 治理。