Day 12: 灾难恢复 + 生产事故注入
目标: Day 1-11 都在"建",Day 12 来"破" + "救",完成完整闭环 价值: SRE / 平台工程师 / CKA-CKS 必考核心 耗时: 4-5 小时 风险: 真的会让集群短暂不可用,得有 backup 保证可恢复
0. TL;DR (4 个事故)
| 段 | 事故注入 | 恢复手段 |
|---|---|---|
| A etcd cp 节点损毁 | stop etcd + 删 /var/lib/etcd | snapshot restore + member add |
| B OOM 雪崩 | deploy mem hog Pod 抢占资源 | LimitRange + ResourceQuota + 杀 Pod |
| C Cilium 全 down | delete cilium DaemonSet | reapply manifest + 看现存 Pod 是否影响 |
| D 生产 SOP | — | 5 个真事故的应急流程 |
1. SRE 黄金原则 (面试经典)
3 步法:
1. 止血 (stop the bleeding):
- 隔离故障组件,防止扩散
- 流量切走 / 副本下线
2. 诊断 (diagnose):
- 看 events / logs / metrics
- 不要急着改,先理解 "为什么"
3. 修复 (recover):
- 优先用 "已知好状态" 回滚(snapshot / 上一版本)
- 复杂事故记录 timeline,事后复盘
核心心态:
- 不慌: 集群不会"瞬间烧毁",大部分故障可恢复
- 不蛮: 不要
kubectl delete --force先,先看 events - 不孤: 关键操作 (snapshot restore / 删 etcd) 必须 pair work
10. 实时执行日志(6 维度)
Day 12.A — etcd 灾难:cp-2 节点 etcd 完全损毁 + member remove/add 恢复 ✅
A1. 灾难注入设计 + 实测
场景: cp-2 磁盘故障,/var/lib/etcd 数据丢
实测 timeline:
| 时刻 | 动作 | 集群状态 |
|---|---|---|
| T+0 | snapshot save: 453 MB / 24 万 keys | 3/3 healthy |
| T+10s | mv etcd.yaml + mv /var/lib/etcd | cp-2 etcd down |
| T+20s | endpoint health: cp-2 connection refused | 2/3 quorum,仍可写 |
| T+30s | cp-2 apiserver CrashLoopBackOff | HAProxy 自动 failover 到 cp-1/cp-3 |
| T+60s | kubectl create ns day12-test | ✅ 成功(per-node HAProxy 救场) |
| T+90s | etcdctl member remove c14422e68fa9c73e | 2/2 active members |
| T+100s | etcdctl member add k8s-cp-2 | 接受新 member |
| T+110s | 修 cp-2 etcd.yaml: --initial-cluster-state=existing + 3-node cluster list | manifest 就绪 |
| T+120s | mv etcd.yaml 回 manifests/ | kubelet 自启 etcd Pod |
| T+140s | etcd container Running | 从 leader 拉 snapshot/WAL |
| T+180s | endpoint health 3/3 true | 完全恢复 |
Total recovery time: ~2 分钟(从注入到 quorum 恢复)
A2. Pre-Disaster Snapshot
What (在 cp-1 上跑,etcd pod 健康时):
# 进 cp-1 etcd pod 跑 snapshot
ETCD_POD=$(kubectl get pod -n kube-system -l component=etcd \
--field-selector=spec.nodeName=k8s-cp-1 -o name | head -1)
kubectl exec -n kube-system $ETCD_POD -- sh -c "ETCDCTL_API=3 etcdctl \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /tmp/snapshot-day12.db"
# 把 snapshot 拷出 cp-1 (Pod 临时存储, Pod 重启会丢)
kubectl cp -n kube-system $ETCD_POD:/tmp/snapshot-day12.db /root/etcd-backups/snapshot-$(date +%Y%m%d-%H%M).db
ls -la /root/etcd-backups/
Snapshot 文件大小约 2-5 MB(我们集群规模)
生产策略:
- CronJob 每 30min 自动 snapshot
- 拷出集群到 S3 / NFS / 异地
- 保留 ≥ 7 天历史,可时间点回滚
A3. 注入故障 — cp-2 etcd 完全损毁
(开干填)
A4. 不是直接 restore — 正确流程
为什么不能直接 restore?
错误流程 (新手):
cp-2 etcd data 丢 → 在 cp-2 上 etcdctl snapshot restore
→ 启动 etcd 用恢复的 data → 加入集群?
❌ 错!restore 出来的 etcd 是"独立 cluster",member-id 跟现有 cluster 不一致
→ etcd 拒绝 join, "member ID mismatch"
正确流程:
1. etcdctl member remove <cp-2 member id> (从集群 active member 列表移除)
2. 在 cp-2 上清空 etcd data dir
3. 修改 etcd static pod manifest 加 --initial-cluster-state=existing
4. etcd member add <cp-2 url> 让 cluster 接受新 member
5. cp-2 etcd 启动,作为"新 member"加入(数据从 leader 拉)
关键认知: snapshot restore 适合"整个 cluster 全死了"灾难,单节点损毁应该用 member add
A5. 多 cp 全死的最严重灾难场景 — snapshot restore
3 个 cp etcd 全死 (机房断电 + 数据损毁):
1. 在某个还有 snapshot 的节点跑 restore:
etcdctl snapshot restore snapshot.db \
--name k8s-cp-1 \
--initial-cluster k8s-cp-1=https://10.0.24.31:2380 \
--initial-cluster-token etcd-cluster-1 \
--initial-advertise-peer-urls https://10.0.24.31:2380 \
--data-dir /var/lib/etcd-new
2. mv /var/lib/etcd-new /var/lib/etcd
3. 修改 etcd manifest 启动单节点 cluster (initial-cluster-state=new)
4. 其他 2 个 cp 重新 join: kubeadm reset + kubeadm join with new token
时间点丢失 = 距上次 snapshot 的时间(如 30 min 内的变更全丢)。
Day 12.B — OOM 雪崩注入 + LimitRange 救场 ✅
B1. 注入 — BestEffort Pod 抢 6GB(w-2 只 8GB)
apiVersion: v1
kind: Pod
metadata: {name: mem-hog, namespace: day12-test}
spec:
restartPolicy: Never
containers:
- name: stress
image: polinux/stress
args: [stress, --vm, "1", --vm-bytes, "6G", --vm-hang, "180"]
# ⚠️ 不设 resources.limits → BestEffort QoS → OOM killer 第一目标
Actual (15-60s 内):
dmesg:
oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=cri-containerd-...scope
Out of memory: Killed process 1530928 (stress) total-vm:6292240kB, anon-rss:6188660kB
Tasks in /kubepods.slice/kubepods-besteffort.slice/... are going to be killed due to memory.oom.group set
oom_reaper: reaped process 1530928
kubectl get pod mem-hog:
Status: OOMKilled, exitCode 137
关键观察:
- 集群其他 Pod 完全没受影响(longhorn / cilium / etcd 全 healthy)
- BestEffort QoS Pod 被精准杀掉,Burstable / Guaranteed 不动
- Node MemoryPressure 没触发(kernel OOM killer 比 K8s Eviction 更快)
B2. 救场 — LimitRange + ResourceQuota
---
apiVersion: v1
kind: LimitRange
metadata: {name: default-limits, namespace: day12-test}
spec:
limits:
- type: Container
default: {cpu: 500m, memory: 512Mi} # 没设 limit 用这个
defaultRequest: {cpu: 100m, memory: 128Mi}
max: {cpu: 2, memory: 2Gi} # 上限,即使显式写也不能超
---
apiVersion: v1
kind: ResourceQuota
metadata: {name: ns-quota, namespace: day12-test}
spec:
hard:
requests.cpu: "4"
requests.memory: 4Gi
limits.cpu: "8"
limits.memory: 8Gi
pods: "10"
再注入同样 6GB stress Pod:
mem-hog-v2:
Limits: cpu 500m, memory 512Mi ← LimitRange 自动注入
Requests: cpu 100m, memory 128Mi
Status: OOMKilled (15s 内)
ExitCode: 137
关键区别:
- 第一次:节点级 OOM(kernel OOM killer 杀整个 BestEffort cgroup)
- 第二次:容器级 OOM(container cgroup 到 limit 立刻被 kill,精确隔离)
生产推论:
- 任何 namespace 上线都先加 LimitRange + ResourceQuota
- Kyverno require-resources policy 双保险(我们 Day 5.F 已配)
- 监控
kube_pod_container_status_last_terminated_reason{reason="OOMKilled"}
Day 12.C — Cilium 全 down 流量切断 + helm upgrade 恢复 ✅
C1. 注入
kubectl delete ds cilium -n kube-system
Actual (10-20s 内):
cilium-agent Pods: All deleted (5/5 gone)
Node taints:
k8s-cp-1: node.cilium.io/agent-not-ready (set by cilium operator)
k8s-cp-2: node.cilium.io/agent-not-ready
... 所有节点
C2. 影响分析(面试可讲)
| 影响项 | 状态 |
|---|---|
| 新 Pod 调度 | ❌ 阻塞 — FailedScheduling: untolerated taint cilium.io/agent-not-ready |
| 已有 Pod | ✅ 进程不动(eBPF 程序在内核还在) |
| 跨节点 Pod-to-Pod | ❌ Connection refused(SNAT/DNAT 失效) |
| Pod → ClusterIP Service | ⚠️ 间歇性失败(iptables-rule 但 Cilium 实际接管) |
| 已有 Pod 跟 host network | ✅ 不影响 |
| Hubble flow | ❌ Relay 收不到数据(agent 死了) |
C3. 恢复
helm upgrade cilium cilium/cilium --reuse-values --version 1.16.5 -n kube-system
Actual (60-180s):
- Cilium DaemonSet 重建,5 agent Pod Running
- agent ready → cilium-operator 自动移除节点 taint
- 新 Pod 调度恢复(test-recover 在 w-2 上 Running,IP 10.244.1.182)
关键认知:
- Cilium 用 taint-on-startup 模式: agent 没 ready 时节点带 taint
- 这是防御机制 — 防止 Pod 调度到无网络节点变僵尸
- 删 DS 等于全集群 taint 强制 Pause,helm reapply 即恢复
Day 12.D — 生产 SOP 应急手册
D1. 5 个真生产事故的 SOP
SOP-1: etcd 单 cp 节点损毁
症状:
- 1 个 cp 节点的 etcd Pod CrashLoopBackOff 或 Pod 起不来
kubectl get nodes仍 work(2/3 quorum 维持)- 该 cp 节点的 apiserver 也 CrashLoopBackOff
- 用户报"偶尔 kubectl 超时"(HAProxy 健康检查标 cp 为 unhealthy 之前)
应急 SOP:
[1分钟] 1. 确认影响范围:
ETCD_POD=$(kubectl get pod -n kube-system -l component=etcd \
--field-selector=spec.nodeName=k8s-cp-1 -o name | head -1)
kubectl exec -n kube-system $ETCD_POD -- etcdctl endpoint health \
--endpoints=<3-cp-2379-list>
[3分钟] 2. 拉最新 snapshot (灾难前一次的):
ls -la /backup/etcd-snapshots/
# 找最近的 *.db
[5分钟] 3. member remove 死掉的 cp:
etcdctl member remove <cp-id>
[10分钟] 4. 修复磁盘 / 重建节点 / 清 /var/lib/etcd
[15分钟] 5. member add + 改 manifest --initial-cluster-state=existing
etcdctl member add <new-cp-name> --peer-urls=https://<cp-ip>:2380
# 改 /etc/kubernetes/manifests/etcd.yaml 加 existing + 新 cluster list
[20分钟] 6. mv etcd.yaml 回 manifests/
等 30s, kubelet 自启 etcd
[25分钟] 7. 验证 3/3 healthy
etcdctl endpoint health
kubectl get pods -n kube-system -l component=etcd
Total: 25 分钟内可救回
SOP-2: 所有 cp 死(灾难级 — 整机房断电)
症状: kubectl get nodes 卡死,所有 apiserver 不可达
SOP:
1. 别慌。worker 节点上 Pod 一直在跑(kubelet local mode)
2. 找最近 snapshot(异地 backup,不能丢在本机房!)
3. 选 1 个 cp 节点:
etcdctl snapshot restore snapshot.db \
--name <cp1-hostname> \
--initial-cluster <cp1>=https://<cp1-ip>:2380 \
--initial-cluster-token <new-token> \
--initial-advertise-peer-urls https://<cp1-ip>:2380 \
--data-dir /var/lib/etcd-new
4. mv /var/lib/etcd-new → /var/lib/etcd
5. 改 etcd manifest --initial-cluster-state=new + initial-cluster 仅含 cp1
6. cp1 起来后,其他 cp 走 kubeadm reset + kubeadm join 重新加入
7. 时间点丢失 = 距上次 snapshot 时间(所以高频 snapshot 必要!)
关键: snapshot 必须异地存(S3/异机房 NFS),不能跟集群同处
SOP-3: Worker 节点 OOM 雪崩
症状:
- 节点 MemoryPressure: True
- 多个 Pod Evicted
- Grafana 看 node mem util > 95%
SOP:
[即刻] 1. 隔离: kubectl drain <node> --ignore-daemonsets
→ 把 workload 调走
[5分钟] 2. 找元凶 Pod:
kubectl top pods -A --sort-by=memory | head -10
# 看哪个 Pod mem 异常高
[10分钟] 3. 临时杀掉元凶: kubectl delete pod <elite-name>
[长期修复]:
4. namespace 加 LimitRange 默认 limit
5. namespace 加 ResourceQuota 总量
6. Kyverno policy require-resources enforce (Day 5.F)
7. Grafana 加 alert: 当 mem_util > 85% 持续 5min → 钉钉
SOP-4: CNI (Cilium) 全集群 down
症状:
kubectl get nodeswork- 但新 Pod 卡 Pending:
untolerated taint node.cilium.io/agent-not-ready - 已有跨节点 Pod 通信失败
SOP:
[立刻] 1. 不要 panic delete 任何 Pod (避免破坏现有 work)
2. 检查: kubectl get pods -n kube-system -l k8s-app=cilium
a. 如果空 (DS 被误删) → helm upgrade cilium 重装
b. 如果 CrashLoopBackOff → 看 log + 重启 Pod
c. 如果 Pending → 看 events (image pull / RBAC)
[5分钟] 3. 业务流量切走 (DNS 切到备 cluster 或 fallback page)
[10分钟] 4. helm upgrade cilium cilium/cilium --reuse-values
sleep 60
kubectl get pods -n kube-system -l k8s-app=cilium
[15分钟] 5. 验证: 起一个 test Pod 看 IP 分配 + ping 跨节点 Pod
预防: Cilium DS 加 priorityClassName: system-node-critical,严防被 evict
SOP-5: apiserver cert 过期
症状:
kubectl报x509: certificate has expired- 集群所有 apiserver 同时挂(同时 install 同时过期)
SOP:
[预防为主!]
1. 监控 cert 过期: prometheus alert
apiserver_client_certificate_expiration_seconds{quantile="0.01"} < 30*24*3600
[一年 -1天] 2. 提前续期:
sudo kubeadm certs renew all
# 重启 cp 上的 apiserver / controller / scheduler
[已过期但还能 ssh]:
3. ssh 进 cp,kubeadm certs renew all
4. 直接重启 etcd / kubelet / restart kube-apiserver pod
[集群完全失联]:
5. 进单个 cp 节点:
kubeadm certs renew all
systemctl restart kubelet
6. 用 /etc/kubernetes/admin.conf 在该节点本地 kubectl 救集群
D2. 黄金原则
| 原则 | 解释 |
|---|---|
| 3-2-1 backup | 3 份 backup,2 种介质,1 份异地 |
| chaos engineering | 月度灾难演练(我们就在做!) |
| runbook 即代码 | SOP 写 markdown,放 git,review |
| 没 monitor 没 alert 没 SLO 等于裸奔 | Prometheus + AlertManager + 钉钉 |
| 关键操作 pair work | 删 etcd / --force 必须 2 人 review |
| 回滚优先于修复 | snapshot restore 比"边修边推断"快 10x |
99. Day 12 完整完成
- [x] A — etcd cp-2 损毁注入 + member remove/add 恢复(2 min)
- [x] B — OOM 雪崩注入 + LimitRange 救场(15s 容器 OOM)
- [x] C — Cilium 全 DS 删 + helm upgrade 恢复(60s 内 agent 起 + taint 清)
- [x] D — 5 个真生产事故 SOP 写完整
简历金句:
做过 K8s 灾难恢复全链路演练:
- etcd: 单 cp 节点 etcd 数据完全损毁 →
member remove/add+--initial-cluster-state=existing2 min 救回 3/3 quorum- OOM: BestEffort Pod 6GB 抢占 → 节点 OOM kill 整 cgroup;LimitRange + ResourceQuota 后转为容器级精确 OOM
- CNI: Cilium 全 DS 删 → 节点自动 taint + 新 Pod Pending 防御 + helm upgrade 60s 恢复
- 完整 SOP 应急手册:5 个真生产事故 × 25 分钟内救回