Kubernetes故障排查实战:35个场景从原理到修复
1. 项目概述:为什么我故意搞坏Kubernetes集群35次?
如果你正在学习Kubernetes,这个场景你一定不陌生:跟着教程部署了一个Nginx,一切顺利,感觉自己已经掌握了这项“云原生核心技术”。然后周一早上回到电脑前,发现你的Pod正卡在CrashLoopBackOff状态,像一只陷入死循环的电子宠物,不断重启、崩溃、再重启。你盯着终端,大脑一片空白,开始疯狂搜索错误信息,复制粘贴一堆kubectl命令,半小时过去了,问题依旧,挫败感油然而生。这就是学习Kubernetes的经典困境——你通过搭建学会了“生”,但没人教你如何“病”时自救。文档和教程教会了你正确的YAML语法,却很少告诉你当集群“发脾气”时,那些状态字段背后到底在哭诉什么。
这正是我创建“Troubleshoot Kubernetes Like a Pro”这个开源项目的初衷。我花了大量时间,在我的本地集群里故意制造了35种真实的故障场景。从调度失败到容器崩溃,从网络断联到存储卷挂载错误,我亲手“破坏”了它们,然后记录下每一个故障的现象、排查路径和修复方案。我这么做,是为了让你不必再经历同样痛苦的试错过程。这个项目不是一个理论指南,而是一个实战训练场。你不需要任何云账号,只需要一个本地Kubernetes环境(Minikube、Kind或Docker Desktop),就能安全地、可控地引爆这些“炸弹”,并在爆炸声中真正理解Kubernetes的内部运作机制。
注意:所有操作均在本地隔离环境进行,绝对安全,不会影响任何生产或线上服务。这是刻意练习的精髓——在安全区里失败,积累的是宝贵的实战经验。
2. 核心学习理念:在安全环境中主动制造故障
传统的学习路径存在一个巨大的断层。我们习惯于学习“最佳实践”,搭建“完美”的应用。但现实世界是混乱的,软件会崩溃,配置会出错,网络会波动。Kubernetes的强大之处在于其声明式API和强大的自愈能力,但这也意味着当问题发生时,它往往被层层抽象所掩盖。一个kubectl get pods命令返回的Pending或ImagePullBackOff,对新手来说就像天书。
这个项目的核心理念是翻转学习过程:从“避免失败”转向“理解失败”。我们不再害怕错误,而是主动邀请错误,并把它当作最严厉的老师。通过亲手应用一个会导致故障的YAML文件(issue.yaml),观察集群的反应,使用标准的kubectl诊断命令(如describe,logs,events)进行排查,最后应用修复文件(fix.yaml)来解决问题。这个“破坏-调查-修复-理解”的循环,模拟了真实线上故障排查的全过程,能让你肌肉记忆般地掌握排障技能。
为什么这种方法有效?因为它触及了深度学习的本质:情境记忆与模式识别。当你亲手处理过一个因节点亲和性配置错误而无法调度的Pod后,下次再看到Pending状态,你的大脑会立刻关联到“检查nodeSelector或affinity规则”。这种通过实践形成的条件反射,远比阅读十遍文档要牢固得多。
3. 项目结构与使用模式解析
这个项目的结构非常清晰,旨在最小化学习阻力,最大化实践效果。整个仓库按故障类别组织,涵盖了Kubernetes运维中最常遇到的几大难题。
3.1 统一的四步学习法
每个故障场景都严格遵循同一个简单而强大的模式,确保学习体验的一致性:
制造故障 (
kubectl apply -f issue.yaml)这是第一步,也是打破心理障碍的一步。你需要主动执行一个命令,将一个“有问题”的资源配置应用到你的集群中。这个YAML文件可能包含一个指向不存在镜像的容器、一个请求了过量CPU的Pod,或者一个端口配置错误的服务。执行后,故障即刻显现。调查诊断 (使用
kubectl get/describe/logs)故障出现后,项目不会给你任何提示。就像在生产环境中一样,你需要自己充当“侦探”。使用你最熟悉的kubectl命令去探查:kubectl get pods -o wide: 查看Pod状态和所在节点,获得第一印象。kubectl describe pod <pod-name>: 这是你的“显微镜”。特别关注Events部分,这里按时间顺序记录了Pod生命周期中的所有关键事件,是定位问题的金矿。例如,“FailedScheduling”、“FailedMount”、“Failed to pull image”等事件会直接指出问题方向。kubectl logs <pod-name>: 如果容器能启动但很快退出,日志是查看应用层错误信息的关键。kubectl get events --all-namespaces: 查看集群级别的事件,有时能发现更广泛的问题。
实施修复 (
kubectl apply -f fix.yaml)经过一番调查,你心中应该有了假设。此时,应用修复YAML文件。这个文件通常只对issue.yaml做了一处或几处关键修改。应用后,观察Pod状态是否恢复正常(例如,从CrashLoopBackOff变为Running)。深度理解 (阅读
description.md)这是将实践转化为知识的关键一步。每个场景都附有一个详细的说明文件,它会解释:- 问题本质:这个故障模拟了现实中的什么情况?
- 根本原因:是配置错误、资源不足还是权限问题?
- 识别信号:在
describe或logs中,你应该寻找哪些特定的错误信息? - 修复原理:为什么
fix.yaml中的修改能解决问题?这背后涉及Kubernetes的什么机制?
3.2 两种实践方式
项目提供了两种上手方式,适应不同的学习习惯:
方式一:使用交互式脚本(推荐给初学者)这是最便捷的方式。克隆仓库后,运行一个简单的Bash脚本,它会以交互菜单的形式引导你完成整个过程。
git clone https://github.com/vellankikoti/troubleshoot-kubernetes-like-a-pro.git cd troubleshoot-kubernetes-like-a-pro ./manage-scenarios.sh运行脚本后,你会看到一个按类别排列的场景列表。只需输入编号,脚本会自动为你应用issue.yaml,并进入“调查阶段”。等你准备好后,它会继续帮你应用fix.yaml并清理环境。这种方式将操作标准化,让你可以专注于故障现象本身。
方式二:手动操作(适合希望完全控制的学习者)如果你希望更贴近真实操作,或者想练习完整的kubectl命令流,可以进入每个场景的目录手动执行。
cd scenarios/crashloopbackoff # 进入某个具体场景目录 # 1. 制造故障 kubectl apply -f issue.yaml # 2. 调查 kubectl get pods kubectl describe pod <problem-pod-name> kubectl logs <problem-pod-name> --previous # 查看前一个容器的日志,对CrashLoopBackOff特别有用 # 3. 修复 kubectl delete -f issue.yaml # 先清理错误配置 kubectl apply -f fix.yaml # 4. 验证 kubectl get pods手动操作能让你更深刻地记忆命令序列和排查流程,尤其是在需要删除错误配置再应用正确配置时,这一步在生产中也很常见。
4. 35个故障场景深度分类与实战精讲
这35个场景并非随意罗列,而是精心设计的,覆盖了从基础设施到应用层的完整故障链。下面我将挑选几个最具代表性的类别和场景,深入剖析其原理和排查思路。
4.1 调度失败类:当Pod无处可去
Pod的Pending状态是调度失败的标志。调度器(kube-scheduler)无法为Pod找到一个满足所有要求的节点。这类问题通常与资源、规则和节点状态有关。
场景:亲和性规则冲突
- 故障模拟:在
issue.yaml中,Pod通过nodeAffinity或podAffinity/AntiAffinity设置了非常严格的调度规则,例如要求节点必须拥有一个不存在的标签(disktype: ssd),或者必须与某个特定Pod共存,但目标节点不满足条件。 - 排查要点:执行
kubectl describe pod <pending-pod>。在Events部分,你会看到类似FailedScheduling: 0/1 nodes are available: 1 node(s) didn't match Pod's node affinity/selector.的信息。这明确告诉你调度失败的原因是节点选择器或亲和性规则不匹配。 - 修复原理:
fix.yaml会修正亲和性规则,要么使用集群中实际存在的节点标签,要么放宽规则(如将requiredDuringSchedulingIgnoredDuringExecution改为preferredDuringSchedulingIgnoredDuringExecution)。这教会你区分“必须”和“最好”的调度约束,以及如何设计高可用的亲和性策略。
场景:污点与容忍度不匹配
- 故障模拟:集群中的节点被设置了污点(Taint),例如
key=value:NoSchedule,而你的Pod没有配置对应的容忍度(Toleration)。 - 排查要点:
describe pod的事件会显示FailedScheduling: 0/1 nodes are available: 1 node(s) had untolerated taint {key: value}。你需要理解污点是节点“拒绝”Pod的一种方式,常用于守护节点或运行特定系统组件。 - 修复原理:
fix.yaml会在Pod规约中添加对应的容忍度。这里的关键学习点是:容忍度是Pod的属性,它允许(而非要求)Pod被调度到有对应污点的节点上。你需要谨慎添加容忍度,避免普通工作负载被调度到不应去的节点。
场景:资源不足
- 故障模拟:Pod请求(requests)的CPU或内存超过了集群中任何单个节点上的可用资源。
- 排查要点:
Events会显示FailedScheduling: 0/1 nodes are available: Insufficient cpu/memory。你需要用kubectl describe node查看节点的可分配资源(Allocatable)和已分配量。 - 修复原理:
fix.yaml会降低Pod的资源请求,或者(在模拟环境中)你可以通过增加节点来解决。这强调了requests和limits的区别:requests是调度依据,必须满足;limits是运行时的硬性天花板。不合理的requests设置会直接导致调度失败。
4.2 容器崩溃类:应用的生命不能承受之重
Pod处于CrashLoopBackOff或Error状态,通常意味着容器内的应用进程启动失败或迅速退出。这是开发者和运维人员最常见的噩梦。
场景:CrashLoopBackOff - 错误的容器命令
- 故障模拟:在
issue.yaml的容器定义中,command或args字段被设置为一个不存在的命令,例如["non-existent-binary"]。 - 排查要点:这是最经典的排障场景。首先
kubectl logs <pod-name>可能看不到输出,因为容器根本没能启动。此时必须使用kubectl describe pod。在Events中,你会看到Started container然后立刻Exited with error。更关键的是,在容器的Last State或State字段,Exit Code通常是一个非零值(如127,表示命令未找到)。使用kubectl logs <pod-name> --previous可以尝试抓取上一次崩溃前的日志。 - 修复原理:
fix.yaml会将命令修正为一个有效的命令,如["sleep", "3600"]或["nginx", "-g", "daemon off;"]。这个场景训练你理解:容器镜像的默认入口点(Entrypoint)可以被command覆盖,如果覆盖错了,容器就会秒退。
场景:OOMKilled - 内存超出限制
- 故障模拟:Pod设置了较低的内存限制(limit),但容器内的进程(通常通过一个压力测试脚本)试图分配超过此限制的内存。
- 排查要点:Pod状态会短暂显示
Running,然后变为OOMKilled。kubectl describe pod会清晰显示Terminated Reason: OOMKilled。这是Linux内核cgroup机制在起作用,它强制终止了超限的进程。 - 修复原理:
fix.yaml会适当提高内存限制,或者修正应用的内存使用模式。这里的核心教训是:limits是硬限制,触及即被杀。你需要通过监控和 profiling 来合理设置limits,通常建议limits是requests的1.5倍左右,并为系统组件预留空间。同时,理解应用的内存增长模式比盲目设置大内存限制更重要。
场景:启动探针、存活探针与就绪探针失败
- 故障模拟:这是非常微妙且常见的一类问题。
issue.yaml中配置了错误的探针(Probe)检查。- 存活探针(Liveness)失败:探针检查一个不存在的路径(如
/healthz),导致Kubelet认为应用死亡,不断重启容器。你会看到CrashLoopBackOff,但logs可能显示应用本身是正常的。 - 就绪探针(Readiness)失败:探针失败,Pod一直处于
0/1 Ready状态,不会被添加到Service的端点列表,导致流量无法进入。
- 存活探针(Liveness)失败:探针检查一个不存在的路径(如
- 排查要点:仔细查看
kubectl describe pod的输出。在容器状态部分,会明确显示Liveness probe failed或Readiness probe failed。你需要检查探针的配置(initialDelaySeconds,periodSeconds,path,port等)是否与容器内应用的实际健康检查端点匹配。 - 修复原理:
fix.yaml会修正探针的配置,指向正确的端口和路径。这个场景至关重要,因为它区分了应用存活性和服务就绪性。存活探针失败会重启容器;就绪探针失败只是将Pod从服务流量中隔离。错误配置的存活探针是导致“自杀式重启”的常见元凶。
4.3 镜像与存储问题:供给链的断裂
场景:ImagePullBackOff
- 故障模拟:
issue.yaml中指定的容器镜像不存在于任何仓库(如myapp:non-existent-tag),或者指向一个需要认证的私有仓库但未配置imagePullSecrets。 - 排查要点:Pod状态明确为
ImagePullBackOff。describe pod的Events会详细说明原因,例如Failed to pull image "myapp:non-existent-tag": rpc error: code = NotFound desc = ...或Failed to pull image "private.reg.io/app": denied: requested access to the resource is denied。 - 修复原理:
fix.yaml会更正镜像标签或添加正确的imagePullSecrets。这提醒你:镜像标签管理、私有仓库认证和网络可达性是CI/CD流水线稳定性的基础。永远使用明确的、存在的镜像标签,避免使用latest。
场景:存储卷挂载失败
- 故障模拟:Pod引用了未定义的PersistentVolumeClaim(PVC),或者PVC处于
Pending状态(无法绑定到合适的PersistentVolume)。 - 排查要点:Pod状态可能为
Pending或ContainerCreating且长时间无进展。describe pod的Events会显示FailedMount: Unable to attach or mount volumes: ... persistentvolumeclaim "my-pvc" not found或FailedMount: MountVolume.SetUp failed for volume "pvc-xxx" : mount failed: exit status 32。 - 修复原理:
fix.yaml会确保PVC被正确定义,并且StorageClass或PV的配置能满足PVC的请求(如accessModes, storage)。对于HostPath类型,会确保节点上的目录存在且有正确权限。这个场景揭示了Kubernetes存储抽象层下的复杂性,你需要理清PVC、PV、StorageClass和底层存储系统(如NFS、云盘)之间的关系。
4.4 网络与安全:无形的墙与缺失的钥匙
场景:服务端口不匹配
- 故障模拟:Service的
targetPort指向了Pod容器未监听的端口。 - 排查要点:从Service访问无响应,但直接通过Pod IP和容器端口访问可能正常。检查
kubectl describe service和kubectl describe pod,对比Service的selector、port、targetPort与Pod的labels和容器暴露的ports是否一致。kubectl get endpoints可以直观看到Service背后是否有正确的Pod IP和端口。 - 修复原理:
fix.yaml会修正Service或Pod的端口定义,确保targetPort与容器containerPort匹配。这是微服务通信中最基础的错误之一,强调了声明式配置中“一致性”的重要性。
场景:RBAC权限不足
- 故障模拟:Pod使用了某个ServiceAccount,但这个ServiceAccount没有足够的RBAC(Role-Based Access Control)权限来执行其需要的操作(如读取ConfigMap、创建Pod等)。
- 排查要点:应用日志中可能会出现权限拒绝的错误。更直接的排查方式是查看相关组件的日志(如kube-apiserver的审计日志),或者为Pod配置一个具有足够权限的ServiceAccount进行对比测试。
kubectl auth can-i命令可以模拟权限检查。 - 修复原理:
fix.yaml会为ServiceAccount绑定正确的Role或ClusterRole。这个场景是理解Kubernetes安全模型的基石:最小权限原则。你需要明确每个工作负载需要访问哪些API资源,并仅授予必要的权限。
5. 实战演练:以“CrashLoopBackOff”场景为例的完整排障流程
让我们以一个具体的“CrashLoopBackOff”场景为例,走一遍完整的排障思维流程。假设你已经运行了./manage-scenarios.sh并选择了对应的场景。
第一步:观察与初步判断应用issue.yaml后,你首先运行kubectl get pods。
NAME READY STATUS RESTARTS AGE crashloop-pod 0/1 CrashLoopBackOff 3 87s看到CrashLoopBackOff,你立刻知道:容器启动了,但很快退出,Kubernetes正在按照指数退避策略(等待时间越来越长)不断尝试重启它。RESTARTS次数在快速增加。
第二步:深入探查,寻找线索现在使用最重要的诊断命令:kubectl describe pod crashloop-pod。你需要像阅读病历一样仔细查看输出,重点关注以下几个部分:
Events(事件):滚动到最下方。这里可能显示:
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 2m default-scheduler Successfully assigned default/crashloop-pod to minikube Normal Pulled 2m kubelet Successfully pulled image "busybox:latest" Normal Created 2m kubelet Created container crash-container Normal Started 2m kubelet Started container crash-container Warning BackOff 118s (x5 over 2m) kubelet Back-off restarting failed container事件显示调度、拉取镜像、创建容器都成功了,容器也启动了,但随后立即失败并进入重启回退。这说明问题出在容器启动之后,很可能是容器内的命令或应用本身有问题。
Containers State(容器状态):在描述信息的中部,找到容器状态:
State: Waiting Reason: CrashLoopBackOff Last State: Terminated Reason: Error Exit Code: 127 Started: ... Finished: ...Exit Code: 127这是一个关键信号!在Linux中,退出码127通常意味着“命令未找到”。这强烈暗示容器定义的command或args是错误的。Container Spec(容器规格):向上看容器的定义:
Containers: crash-container: Image: busybox:latest Command: ["/bin/non-existent-command"] # 问题在这里! Args: []真相大白。容器命令被设置为一个不存在的路径
/bin/non-existent-command。BusyBox镜像的默认命令是sh,但这里被覆盖了,导致容器一启动就因找不到命令而失败。
第三步:验证假设与修复根据以上信息,你的假设是“命令错误导致容器立即退出”。为了验证,可以尝试查看日志(虽然可能没有输出):kubectl logs crashloop-pod --previous。
现在,应用修复。运行脚本的修复步骤或手动执行kubectl apply -f fix.yaml。再次查看Pod状态:
kubectl get pods NAME READY STATUS RESTARTS AGE crashloop-pod 1/1 Running 0 10sPod恢复正常!查看修复后的YAML,你会发现command被修正为了一个有效的命令,例如["sleep", "3600"]。
第四步:复盘与知识内化打开该场景的description.md文件,阅读官方解释。它会系统性地总结:
- 问题:无效的容器启动命令。
- 原因:
command字段覆盖了镜像的默认入口点(Entrypoint),而指定的命令在容器内不存在。 - 识别:Pod状态为
CrashLoopBackOff,describe显示Exit Code: 127,Events显示容器启动后立即终止。 - 修复:将
command修改为容器内存在的有效可执行文件路径。 - 扩展思考:这引出了
command和args与Docker镜像ENTRYPOINT和CMD的交互关系。在Kubernetes中,command对应ENTRYPOINT,args对应CMD。如果同时覆盖,需要确保路径和参数的正确性。
通过这样一次完整的循环,你将“CrashLoopBackOff + Exit Code 127”这个故障模式与“错误的容器命令”这个根本原因牢牢绑定在一起。下次在生产中遇到类似现象,你的排查速度将大大加快。
6. 环境准备与最佳实践指南
要开始你的故障排查训练,你需要一个本地Kubernetes环境。以下是几种主流选择及其快速启动指南:
Minikube(最经典的单节点集群)Minikube在虚拟机中运行一个单节点集群,功能完整,非常适合学习和开发。
# 安装Minikube(请参考官方文档获取最新安装命令) # 启动集群(推荐使用docker驱动,更轻量) minikube start --driver=docker # 验证 kubectl get nodesKind(Kubernetes in Docker,极速启动)Kind使用容器作为“节点”,能在几秒内启动一个集群,非常适合快速测试和CI/CD。
# 安装Kind # 创建一个集群 kind create cluster --name troubleshooting-cluster # 验证 kubectl cluster-info --context kind-troubleshooting-clusterDocker Desktop(macOS/Windows用户最便捷)Docker Desktop内置了单节点Kubernetes,一键启用,无需额外安装。
- 打开Docker Desktop设置。
- 进入“Kubernetes”选项卡。
- 勾选“Enable Kubernetes”,点击“Apply & Restart”。
- 等待启动完成,在终端中即可使用
kubectl。
提示:无论选择哪种方式,请确保
kubectl版本与集群版本大致兼容。建议使用工具如kubectx和kubens来方便地切换上下文和命名空间,避免操作错误集群。
实践中的黄金法则
- 从描述(Describe)命令开始:
kubectl describe是你的第一把,也是最重要的一把瑞士军刀。它聚合了资源的状态、事件和配置详情,80%的常见问题都能在这里找到线索。 - 关注事件(Events):Events是按时间排序的日志,记录了资源生命周期中的关键转折点。它位于
describe输出的底部,但价值位于顶部。总是从最新的事件往上看。 - 理解状态(Status)的含义:每个Pod状态都是一个故事。
Pending: 调度器在找工作,问题出在调度层面(资源、亲和性、污点)。ContainerCreating: 正在拉取镜像、创建容器、挂载存储。卡在这里通常是镜像拉取或存储问题。CrashLoopBackOff: 容器启动后退出。问题在容器内部(命令、参数、应用崩溃、探针)。ImagePullBackOff: 镜像拉取失败。检查镜像名、标签、仓库认证。Running但0/1 Ready: 通常是就绪探针失败。Running但不断重启:通常是存活探针失败。
- 善用日志(Logs)和前一个容器日志:
kubectl logs <pod-name>查看当前容器日志。对于崩溃的容器,一定要加--previous参数查看上一次运行的日志,那里可能有崩溃前的错误输出。 - 使用
-o wide和-o yaml获取更多信息:kubectl get pods -o wide可以看到Pod所在的节点,这对定位节点特定问题很有帮助。kubectl get pod <pod-name> -o yaml可以获取完整的资源定义,用于和你的YAML文件做对比。 - 模拟生产,隔离命名空间:建议为这个练习创建一个独立的命名空间(如
kubectl create ns trouble-drill),并在其中操作。这符合生产环境多租户隔离的习惯,也便于最后清理(kubectl delete ns trouble-drill)。
7. 面向不同角色的学习路径与价值
这个项目对不同背景的从业者都有极高的价值,你可以根据自己的角色聚焦在不同的场景组上。
对于初学者和开发者你的目标是从恐惧到熟悉。建议按顺序从最直观的场景开始:
- 容器崩溃类:先攻克
CrashLoopBackOff、OOMKilled。这些故障现象明显,修复直接,能快速建立信心。 - 调度失败类:理解
Pending状态。学习资源请求、节点选择器、亲和性这些调度核心概念。 - 探针故障:理解
Running但不Ready的微妙之处。掌握存活探针和就绪探针的区别,这是保障应用健壮性的关键。你将获得:面对Pod异常时不再慌张,能系统性地使用describe和logs定位问题根因,对Pod生命周期有直观感受。
对于准备CKA/CKAD认证的应试者这些考试高度重视故障排查能力。这个项目是绝佳的模拟题库。
- 全覆盖练习:确保35个场景全部亲手操作一遍。考试环境与本地Kind/Minikube环境高度相似。
- 限时训练:模拟考试环境,给自己设定时间(如10分钟)去诊断和修复一个随机场景。训练命令的熟练度和排查的逻辑性。
- 重点关注:
kubectl describe、kubectl logs、kubectl exec(用于进入容器调试)、kubectl edit(用于快速修复)等核心命令的灵活运用。理解Events和资源状态是得分关键。你将获得:面对未知故障时结构化的排查思路,大幅提升命令执行速度和准确性,从容应对认证考试中的排障题目。
对于SRE和运维工程师你们需要的是对复杂、深层问题的洞察力和预防能力。
- 深度挖掘网络与存储场景:特别是需要CNI插件(如Calico)的网络策略(NetworkPolicy)场景。理解Pod网络隔离、服务发现失败的根本原因。
- 研究安全与RBAC场景:理解ServiceAccount、Security Context、Pod Security Standards。这些是保障集群安全性的基石,配置错误可能导致严重漏洞。
- 分析资源管理场景:深入理解
requests、limits、LimitRange、ResourceQuota以及它们如何影响调度、服务质量(QoS)和Pod驱逐(Eviction)。 - 思考“教育性”场景:如“过时的Kubernetes版本”。这不仅是修复一个配置,更是理解版本差异、API废弃策略和升级风险。你将获得:从“解决问题”升华到“设计韧性系统”的能力。你能预见到哪些配置可能在未来引发故障,并在设计阶段就规避它们。你能编写更健壮的Helm Chart或Kustomize配置,并制定有效的监控和告警规则(针对Pod状态、事件、资源使用率等)。
8. 故障排查心智模型与进阶技巧
在经历了数十次“破坏与修复”后,你应该形成一套属于自己的排查心智模型。以下是我总结的一些进阶思路和技巧,这些在官方手册中往往不会明确写出。
排查的“洋葱模型”从外到内,层层剥离,是最高效的排障路径:
- 集群层:
kubectl get nodes所有节点都Ready吗?kubectl get cs(或kubectl get componentstatuses)核心组件健康吗? - 资源层:
kubectl get pods -A | grep -v Running查看所有非运行状态的Pod。问题是否广泛存在? - 工作负载层:聚焦到出问题的Pod。
kubectl describe pod和kubectl logs是主力。 - 容器内层:如果怀疑是容器内应用问题,使用
kubectl exec -it <pod-name> -- /bin/sh进入容器内部,检查文件、进程、网络连接。 - 内核/节点层:极少数情况需要登录节点,检查
docker或containerd日志、系统日志(journalctl)、资源使用情况(top,df,free)。
“对比法”与“二分法”
- 对比法:当你有一个正常工作的Pod和一个出问题的Pod时,最直接的方法就是对比两者的YAML定义。使用
kubectl get pod <good-pod> -o yaml > good.yaml和kubectl get pod <bad-pod> -o yaml > bad.yaml,然后用diff工具比较。差异点往往就是问题所在。 - 二分法:对于复杂的部署(如多容器Pod、有Init Container的Pod),可以尝试注释掉部分配置,逐步缩小问题范围。例如,先去掉所有探针配置,看Pod能否启动;再逐个添加探针,定位是哪个探针出了问题。
利用kubectl debug进行实时诊断Kubernetes 1.18+ 提供了强大的kubectl debug命令,可以给运行中的Pod添加一个临时调试容器(使用Ephemeral Container),共享进程命名空间和网络命名空间。这对于调试那些没有Shell的“精简镜像”(如distroless)或排查进程问题非常有用。
# 创建一个临时调试容器,并连接到它 kubectl debug -it <problem-pod> --image=busybox:latest --target=<container-name> -- sh在调试容器里,你可以使用ps aux查看目标容器的进程,netstat查看网络连接,cat查看文件内容,而无需修改原有Pod的定义。
将排查过程文档化与自动化在真实团队中,重复出现的故障应该被沉淀为“运维手册”或“诊断剧本”。你可以借鉴这个项目的模式,为你们自己的应用创建类似的故障场景库。例如:
- 创建“已知问题”知识库:记录历史上发生过的故障现象、根因、排查步骤和修复方案。
- 编写诊断脚本:对于常见问题,可以编写Shell脚本或使用Kubernetes的作业(Job)来自动化收集诊断信息(如一次性收集所有相关Pod的describe、logs、events)。
- 配置监控与告警:根据这些故障模式,设置更有意义的告警。例如,不仅仅是“Pod重启了”,而是“Pod因OOMKilled重启,请检查内存配置”或“Pod持续NotReady,请检查就绪探针”。
9. 总结:从故障中生长出的掌控力
我故意搞坏我的Kubernetes集群35次,不是为了证明它脆弱,恰恰相反,是为了在一次次“修复”中,亲手触摸到它强大而精密的内部机制。故障不再是令人恐惧的黑盒,而是变成了一个可以观察、分析和学习的透明实验。
通过这个项目,你学到的远不止35个命令或YAML片段。你培养的是一种系统性排障的直觉。当警报再次响起,你不会再陷入盲目的搜索和尝试。你会冷静地打开终端,执行kubectl describe,像一位经验丰富的侦探一样,从Events的蛛丝马迹中还原故障现场。你会理解Pending、CrashLoopBackOff、ImagePullBackOff这些状态码背后的故事,知道该去哪里寻找下一块拼图。
真正的专业知识,往往诞生于对“错误”的深刻理解之中。这个开源项目就是你安全的训练场。现在,克隆这个仓库,启动你的本地集群,开始你的第一次“破坏”吧。记住,在这里,每一次成功的修复,都是你对Kubernetes更深一层的掌控。祝你排障愉快。
