这条路径解决“故障页看懂了,真出问题还是不会查”的问题:用 12 个可复现的 lab 把现象、验证命令和修复动作串起来。适合已经读过一条学习路径、需要把知识变成手感的开发、运维和 SRE。
前置知识:会用 Linux 命令行和 Docker;K8s 相关 lab 需要 kind 和 kubectl;数据库与中间件 lab 需要本机有空闲端口和约 2 GB 内存。建议按 4 周完成,每周 3–4 小时,一次只做一个 lab。
所有 lab 都要求在一次性虚拟机、容器或独立 kind 集群里完成。涉及 root、mount、tc、kill 的步骤必须先确认目标环境;实验后清理容器、挂载点和集群,避免端口、磁盘和内存残留。
| 演练 | 练什么 | 环境 | 对应排障页 | 完成后能回答 |
|---|---|---|---|---|
| lab-cpu-100 | CPU 满载、热点与节流 | Linux 或容器 | cpu-100 | 真计算、等 IO 还是被 cgroup 限流 |
| lab-disk-full | 空间耗尽与已删除文件 | Linux root + loop | no-space-left | df 与 du 不一致时先查什么 |
| lab-inode-exhausted | inode 耗尽 | Linux root + tmpfs | inode-exhausted | 有空间为什么创建不了文件 |
| lab-pod-pending | 调度失败 | kind | pod-pending | Pending 时先看哪类事件 |
| lab-pvc-pending | 存储绑定失败 | kind | k8s-pvc-pending | PVC 为什么一直不 Bound |
| lab-crashloopbackoff | 容器反复退出 | kind | crashloopbackoff | 退出码和 previous 日志怎么读 |
| lab-dns-failure | 集群 DNS 故障 | kind | k8s-dns-failed | 查 CoreDNS、Service 还是策略 |
| lab-network-latency | 延迟注入与测量 | Linux Docker + NET_ADMIN | packet-loss | 延迟、丢包、带宽、DNS 怎么区分 |
| lab-mysql-deadlock | InnoDB 死锁 | Docker | mysql-deadlock | 1213 与 1205 的区别 |
| lab-postgres-connection-exhausted | 连接耗尽 | Docker | postgres-connection-exhausted | 连接泄漏还是池配置问题 |
| lab-kafka-consumer-lag | 消费积压 | Docker | kafka-consumer-lag | 总 lag 如何掩盖分区倾斜 |
| lab-redis-hotkey | 热 Key | Docker | redis-hotkey | 热 Key 与大 Key 怎么区分 |
完成基础 12 个之后,按故障域补做进阶演练。它们更接近真实故障:涉及节点、证书、发布、消息重平衡和 etcd 脑裂,必须严格在一次性环境里做。
| 演练 | 练什么 | 环境 | 对应排障页 |
|---|---|---|---|
| lab-oom-killed | 容器内存上限与 OOMKilled | Docker / kind | oom-killed |
| lab-imagepullbackoff | 镜像 tag、认证与仓库连通性 | kind | imagepullbackoff |
| lab-node-notready | kubelet 停止、节点压力与驱逐 | kind | node-notready |
| lab-certificate-expired | 证书过期、链验证与轮换 | Docker / 本地 TLS 服务 | certificate-expired |
| lab-nginx-502 | 上游端口、进程与错误日志 | Docker Compose | 502 Bad Gateway |
| lab-helm-failed | 模板、资源冲突与 release 状态 | kind + Helm | Helm |
| lab-kafka-rebalance | 消费者进出与 rebalance | Docker Compose | Kafka rebalance |
| lab-es-unassigned | 分片分配、磁盘水位与副本 | Docker Compose | Elasticsearch yellow |
| lab-etcd-quorum | quorum 丢失与恢复顺序 | 3 节点 kind / etcd | etcd quorum |
| lab-mysql-replication-lag | 主从延迟与大事务 | Docker Compose | MySQL 主从延迟 |
进阶演练不强制排进四周计划。建议每完成一个基础阶段,就挑一个同故障域的进阶 lab;例如完成 K8s 基础后做 lab-oom-killed 和 lab-node-notready,完成消息与数据阶段后做 lab-kafka-rebalance 和 lab-mysql-replication-lag。
目标:建立“先预测、再注入、后取证”的节奏,分清 CPU、磁盘空间和 inode 三类资源故障。
阅读顺序:
df、du、lsof 的排查顺序。df -i 和文件计数的判断。动手任务:在一次性 Linux 虚拟机里连续做 CPU 和 inode 两个实验,注入前先写下预期现象,注入后用两条命令取证并清理。风险是 CPU 实验会吃满一个核、tmpfs 会占内存;必须带 timeout 和容量上限,不要对根分区做填充。
docker run -d --rm --name lab-cpu --cpus=1 alpine:3.20 sh -c 'timeout 120s sh -c "while :; do :; done"'
docker stats --no-stream lab-cpu
mkdir -p /tmp/lab-inode
sudo mount -t tmpfs -o size=64m,nr_inodes=256 tmpfs /tmp/lab-inode
df -i /tmp/lab-inode
sudo umount /tmp/lab-inode
过关标准:
docker ps 和 df 回到基线。目标:按 Pod 生命周期定位调度、存储和容器启动失败,不靠重启碰运气。
阅读顺序:
lastState 和 previous 日志。动手任务:在独立 kind 集群里依次做 Pending、PVC Pending、CrashLoopBackOff 三个 lab,每次先写判断,再用 Events、describe 和 logs 验证。风险是误删共享集群资源;只能在 kind-lab 集群和 lab 命名空间操作,结束后删除整个集群。
kind create cluster --name lab-k8s
kubectl create namespace lab
kubectl -n lab get events --sort-by=.lastTimestamp
kubectl -n lab describe pod $(kubectl -n lab get pods -o jsonpath='{.items[0].metadata.name}')
过关标准:
lastState 找到退出原因,而不是只看 restart count。目标:用延迟注入和 DNS 故障验证网络假设,区分延迟、丢包、超时和解析失败。
阅读顺序:
resolv.conf。动手任务:在 Linux Docker 里做延迟实验,先在客户端容器测基线,再注入 200 ms 延迟和 50 ms 抖动,最后删除 qdisc 恢复。风险是 tc 会影响该容器全部出站流量;只能加 NET_ADMIN 到一次性容器,绝不能对宿主机网卡或生产 Pod 执行。
docker network create lab-net
docker run -d --name lab-server --network lab-net nginx:1.27-alpine
docker run -d --name lab-client --network lab-net --cap-add=NET_ADMIN nicolaka/netshoot:v0.13 sleep 3600
docker exec lab-client ping -c 3 lab-server
docker exec lab-client tc qdisc add dev eth0 root netem delay 200ms 50ms
docker exec lab-client tc -s qdisc show dev eth0
docker exec lab-client tc qdisc del dev eth0 root
过关标准:
resolv.conf、Service、Endpoints 逐层确认。目标:用单容器复现死锁、连接耗尽、消费积压和热 Key,把现象映射到指标与修复动作。
阅读顺序:
max_connections 复现连接耗尽。MONITOR 和 commandstats。动手任务:按顺序做四个中间件 lab,每个 lab 只收集一条核心证据并写一句结论;四个容器使用固定端口,做完立即删除。风险是端口冲突和内存占用;端口只绑定回环地址,不要复用共享主机上的现有实例。
docker exec lab-mysql mysql -uroot -plabpass -e 'SHOW ENGINE INNODB STATUS\G' | grep -A20 'LATEST DETECTED DEADLOCK'
docker exec lab-pg psql -U postgres -c 'SELECT state, count(*) FROM pg_stat_activity GROUP BY state;'
docker exec lab-kafka /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group lab-group
docker exec lab-redis redis-cli INFO commandstats | sort -t= -k2 -rn | head
过关标准:
第 1 周做阶段一,第 2 周做阶段二,第 3 周做阶段三并重做一次 CPU 实验,第 4 周做阶段四并从零复现一个 lab。每次复盘只写五行:现象、假设、验证命令、根因、修复与回滚;周末比较首次定位时间、验证命令数和回滚耗时。
tc 和容器删除都可能影响别人,必须隔离。合上页面,先回答,再展开对照。
df 和 du 不一致,说明什么?下一步命令是什么?du 未覆盖对应挂载点;下一步用 lsof +L1 找持有已删除文件的进程,并核对挂载点。