ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

K8s Pod 异常排查:从 Pending 到 OOMKilled 实战

K8s Pod 异常排查:从 Pending 到 OOMKilled 实战 调试 Pod 这件事最让人抓狂的地方在于控制台给你的往往只有一行状态码——Pending、CrashLoopBackOff、OOMKilled剩下的全要你自己去猜。刚接手 k8s 集群那阵子我遇到 Pod 异常的第一反应就是kubectl logs结果十次里有六次日志是空的因为容器压根没起来根本没东西可打。后来踩的坑多了才慢慢总结出一套相对固定的排查路径先看状态和事件再看调度和资源最后才扎进应用日志里。这篇就把这套 k8s Pod 异常与故障排查的思路完整摊开讲一遍从状态分类、命令取证、高频场景逐条拆解到速查表和避坑经验。不管你是刚开始学 k8s 编排的新手还是已经被Pending折磨过几轮的老手都能从里面挑到能直接抄作业的部分。1. 排查前先建框架Pod 异常到底分哪几层我见过太多人排查 Pod 问题时东一榔头西一棒子本质原因是脑子里没有分层模型。Pod 从被创建到稳定运行中间要经过调度、拉镜像、创建容器、启动进程、通过探针、接入 Service 好几个环节每一环都有自己独立的失败模式。把它们混在一起看就会觉得Pod 异常是个玄学拆开来看其实每一层能出的错就那么几种。1.1 三个维度定义异常看一个 Pod 是否正常我一般同时看三个维度缺一个都可能误判。第一个维度是Pod 阶段Phase也就是kubectl get pod里 STATUS 那一列最粗的划分Pending、Running、Succeeded、Failed、Unknown。它描述的是 Pod 在整个生命周期中的宏观位置粒度很粗但定位方向极快——看到Pending就不用去翻应用日志了问题一定在调度之前。第二个维度是容器状态藏在kubectl get pod -o yaml的status.containerStatuses里每个容器都有waiting、running、terminated三种可能。CrashLoopBackOff、ImagePullBackOff这些让人眼熟的状态码全都是waiting.reason的值而不是 Pod 阶段。很多新手会把它们当成并列概念其实层级完全不同。第三个维度是就绪条件Conditions最常看的是Ready和ContainersReady。这个维度最容易被忽略因为 Pod 显示Running不代表它能接流量——如果 readinessProbe 一直失败Pod 是 Running 但 ReadyFalseService 的 Endpoints 里根本不会有它的 IP。线上出现过服务明明起来了但 502的事故八成栽在这里。提醒RunningReadyFalse是最隐蔽的一类故障因为它不会触发任何重启事件里也常常是空的只能靠探针日志和kubectl get endpoints反推。1.2 为什么从外到内比直接看日志快得多我给自己定过一条死规矩任何 Pod 异常第一分钟绝对不碰应用日志。原因很实在——如果容器都没启动成功kubectl logs要么报错要么空白纯属浪费时间而kubectl describe pod里的 Events 段落几乎从不让人失望它会明明白白告诉你0/3 nodes are available: 3 Insufficient memory或者Failed to pull image。所谓从外到内指的是按这个顺序推进调度层 → 镜像层 → 容器启动层 → 应用运行层 → 网络存储层。每一层都有明确的过或不过的判定信号前一层没过后一层的所有信息都是噪声。这套顺序的价值在集群规模大的时候尤其明显因为一个看似是应用崩溃的问题根因可能是节点磁盘压力导致的驱逐你只盯着容器看永远找不到。生活里有个类比很好理解家里水龙头不出水你会先去检查是不是整栋楼停水调度层再看自家总阀镜像层最后才拆水龙头应用层。反过来一上来就拆龙头大概率白拆。1.3 排查必备的三个前置信息真正动手之前有三样东西必须先拿到手否则后面所有推理都是猜。实际生效的 Pod YAML不是 Git 仓库里那份而是kubectl get pod name -o yaml出来的那份。经过 mutating webhook、默认值注入之后两者可能差很远我遇到过注入 sidecar 后端口冲突的案例看仓库配置完全看不出来。最近的事件流kubectl get events加上字段选择器过滤能还原出问题发生那几分钟的完整时间线。节点当时的负载快照kubectl top node和kubectl describe node用来判断是不是节点级问题波及了一批 Pod而不是单个 Pod 自己作妖。2. 取证环节把现场信息一次性收齐排查的功力一半在思路一半在命令用得熟不熟。我整理过一套现场取证六件套基本上是遇到异常 Pod 闭着眼睛敲一遍信息就齐了。2.1 六条必敲的命令# 1. 看 Pod 概况确认所在节点和重启次数 kubectl get pod -o wide -n ns # 2. 看详情和事件这是信息密度最高的一条 kubectl describe pod pod -n ns # 3. 看当前日志 kubectl logs pod -n ns -c container --tail200 # 4. 看上一次崩溃前的日志CrashLoop 场景的救命稻草 kubectl logs pod -n ns -c container --previous # 5. 看资源实际占用判断是不是逼近 limit kubectl top pod pod -n ns --containers # 6. 反查 Service 是否真的挂上了这个 Pod kubectl get endpoints svc -n ns第 4 条特别值得说。容器反复重启时当前那次日志往往只打印了两行就没了真正的崩溃堆栈在上一次的日志里。--previous这个参数我带了三年救过不止一次命。注意它只对已经终止过的容器有效容器第一次启动就卡住不动的场景用不上。2.2 describe 输出怎么读kubectl describe pod输出很长但值得逐段读的其实就四块。我把它们的作用和典型信号列成表方便对照。输出段落关键字段典型异常信号StatusPhase / ReasonPending、Evicted、NodeLostConditionsReady、PodScheduled、InitializedReadyFalse 且 message 指向探针ContainersState / Last State / Restart CountExit Code 非 0、Reason 为 OOMKilledEventsTypeWarning 的行FailedScheduling、FailedMount、Unhealthy其中Last State 的 Exit Code是很多人忽略的金矿。137 通常意味着被 SIGKILL结合Reason: OOMKilled基本能锁定内存问题143 是 SIGTERM多见于优雅退出超时被强杀1 是应用自己异常退出。把这几个码记住判断方向能省一半时间。2.3 事件会被清掉所以要趁早抓k8s 的 Event 默认只保留一小时--event-ttl可调而且数量多了会被聚合压缩。我遇过一次很尴尬的情况早上报障处理别的事下午回头看时事件已经过期describe里干干净净最后只能靠复现。所以现在的习惯是只要确认是线上问题第一件事就是把事件快照存下来。kubectl get events -n ns \ --field-selector involvedObject.namepod \ --sort-by.lastTimestamp加上--sort-by之后事件按时间排好能非常直观地看出先调度失败、再重试、最后成功但探针失败这种因果链。这比一屏乱序事件有用得多。3. 高频异常场景逐条拆解前面讲的是方法论这一节上硬菜。下面这几个状态码基本覆盖了日常八成以上的 Pod 故障。3.1 Pending调度不上去的几类原因Pending的含义很明确Pod 对象已经创建但还没有被绑定到任何节点。既然没到节点上容器、镜像、日志统统不用看问题一定在调度判定里。常见原因按出现频率排大致是这么几类。第一类是资源不足。事件里会写0/5 nodes are available: 5 Insufficient cpu。这里有个容易踩的坑k8s 调度看的是requests不是实际用量也不看你配的 limit。我曾经在一个节点上看到 CPU 使用率只有 20%但新 Pod 就是调度不上去原因是这个节点上已有的 Pod 把 requests 总和占满了——requests 是预留额度一旦分配出去就不再释放即使实际没用到。所以给 Pod 配 requests 时不能随手写个大数图安心那等于在浪费整块集群的调度空间。第二类是亲和性与污点。nodeSelector、nodeAffinity、podAntiAffinity、taints/tolerations任何一条不匹配都会让 Pod 无家可归。这类问题的特征是事件里会写didnt match node selector或者had taint一眼就能定位。我踩过的坑是给一个 Deployment 配了强制的 podAntiAffinity要求同一服务的两个副本不能在同一节点结果副本数扩到超过节点数时多出来的 Pod 就永远 Pending。这种设计要在集群容量规划时提前算清楚。第三类是 PVC 没绑上。用到了 PVC 但存储类没有可用的 PVPod 会一直停在 Pending。事件里提示pod has unbound immediate PersistentVolumeClaims。这类问题最容易误判因为它看起来像调度问题实际上是存储问题得去看 PVC 和 StorageClass 的状态。第四类是优先级抢占失败也就是那个让人一头雾水的no preemption victims found for incoming pod。这条信息的完整含义是有个高优先级的 Pod 调度不上去调度器尝试驱逐低优先级 Pod 给它腾位置但找了一圈发现——要么所有低优先级 Pod 的优先级都不够低抢占也得满足优先级差值要么被抢占的 Pod 移走之后腾出的资源依然装不下这个新 Pod要么候选者被 PodDisruptionBudget 保护着动不了。所以它本质上是我努力了但实在腾不出地方。处理思路有三条检查新 Pod 的 requests 是不是配得过大检查现有 Pod 的 PDB 是不是把可驱逐对象锁死了以及在容量确实不够时老实扩容节点别指望调度器变魔术。注意抢占是尽力而为的机制不是保证。别把关键业务的可用性押在优先级上容量规划才是根。3.2 ImagePullBackOff 与 ErrImagePull这两个状态经常成对出现ErrImagePull是首次拉取失败ImagePullBackOff是带退避重试的持续失败。它们的排查路径非常线性。先确认镜像地址和 tag 是否正确。听起来很废话但实际故障里这个原因占比并不低——尤其是用latest这种浮动 tag 的时候仓库里改名或者被清理掉Pod 就再也拉不动了。我的建议是生产环境一律用不可变 tag比如 commit hash既避免这类问题也让回滚有据可依。再看私有仓库的凭据。k8s 拉私有镜像靠的是imagePullSecrets而这个 secret 有三个层次要注意secret 本身要在同一个 namespace、类型必须是kubernetes.io/dockerconfigjson、配置里的 registry 地址要和镜像地址前缀完全匹配带不带端口、带不带 path 都算不同 registry。我见过 secret 创建时把https://也写进去的导致认证直接失效。最后看网络与节点层面。如果只有部分节点拉不动那基本是节点到仓库的网络问题如果是所有节点都不行要考虑仓库侧限流或者镜像仓库的 DNS 解析。还有一个隐蔽原因镜像架构不匹配。集群里混了 x86 和 ARM 节点时如果镜像只推了 amd64调度到 ARM 节点上就会拉取失败。用kubectl describe node看节点的kubernetes.io/arch标签再对照镜像的 manifest 列表就能确认。3.3 CrashLoopBackOff最需要耐心的一个CrashLoopBackOff不是错误原因它是 k8s 的一种退避策略容器启动后退出kubelet 就按 10 秒、20 秒、40 秒这样指数级拉长重启间隔最长到 5 分钟。所以看到这个状态要知道真正的信息在两处Last State的退出码以及上一次的日志。排查顺序我一般这么走。先看启动命令本身能不能独立跑通。如果 entrypoint 指向的文件不存在、脚本没有执行权限、路径写错容器会在毫秒级退出日志可能什么都没有。这种情况用kubectl get pod -o yaml看command和args再对着镜像里的实际路径核一遍。再看是不是依赖没就绪。微服务场景里特别常见应用启动时要连数据库、注册中心、配置中心这些依赖还没起来应用直接抛异常退出。这时候容器本身没问题是启动顺序的问题。解决办法不是给容器加更多重启次数而是用 initContainer 做依赖等待或者让应用自身实现带退避的重试连接。我在一个微服务项目里就吃过这个亏一个服务因为连不上配置中心反复重启重启产生的噪音又盖住了真正的问题绕了两小时才发现根因在依赖顺序。然后看探针配置。这是一类非常冤的故障应用其实正常启动了但 livenessProbe 的initialDelaySeconds给得太短第一次探测时应用还在初始化探测失败达到failureThreshold后 kubelet 直接把容器杀了于是进入 CrashLoop。特征是日志里能看到应用启动到一半就断了。修法很简单把initialDelaySeconds调大或者用 startupProbe 把启动阶段的探测和运行阶段的探测分开。最后看权限问题。用securityContext指定了非 root 用户运行但应用要写的目录属主还是 root进程一启动就 Permission denied。这类问题在日志里通常能找到明确的报错但只有在日志确实打出来了的前提下。退出码常见含义优先排查方向1应用自身异常退出启动命令、配置文件、依赖连通性126命令存在但不可执行文件权限、shebang127找不到命令entrypoint 路径错误137被 SIGKILL内存超 limit 触发 OOMKilled143被 SIGTERM优雅退出超时被强杀、探针失败被杀3.4 OOMKilled 与资源限额怎么算才靠谱OOMKilled的本质是容器的内存用量撞上了 cgroup 的 limit内核把进程干掉了。要强调的是这里有两层 OOM节点级 OOM和容器级 OOM。容器级 OOM 只杀越界的那个容器Pod 会重启节点级 OOM 则可能让 kubelet 驱逐一批 Pod影响面大得多。判断方法很简单看describe pod里的Reason写OOMKilled且退出码 137 就是容器级。内存 limit 怎么给是每个项目都会吵的问题。我的经验是limit 要给到业务峰值的 1.3 到 1.5 倍requests 参考稳定期的实际占用。给太紧会频繁 OOM给太松则浪费调度空间。以 JVM 应用为例还有个特别容易踩的坑——早期版本的 JVM 不识别容器 limit默认按宿主机物理内存算堆大小宿主机 64G 的话它敢开 16G 堆在 4G limit 的容器里必然被杀。现在一般通过-XX:MaxRAMPercentage70这类参数让 JVM 感知容器限额把堆控制在 limit 的 70% 左右剩下的留给元空间、线程栈和直接内存。关于2C4G 的 Pod 能扛多少并发我被问过很多次。这里必须说清楚这是个没有标准答案的问题任何直接给数字的回答都不负责任。它取决于你的业务是 IO 密集型还是 CPU 密集型、单请求耗时多少、有没有外部依赖瓶颈、GC 参数怎么调。靠谱的做法是压测——用 JMeter 这类工具从小并发逐步加压同时观察 Pod 的 CPU 使用率、内存曲线和响应时间 P99找到响应时间开始明显拐头的那个点再打个七折作为容量基线。压测时记得把 requests 和 limit 都按生产配置设好否则测出来的数据没法用。顺带提一下 QoS 等级它决定了节点资源紧张时谁先被驱逐QoS 等级满足条件被驱逐优先级Guaranteed所有容器的 requests 等于 limit最低Burstable至少一个容器设了 requests但不满足 Guaranteed中等BestEffort所有容器都没设 requests 和 limit最高关键业务我建议至少做到 Burstable核心链路做 Guaranteed别裸奔成 BestEffort。3.5 Running 但不 Ready探针类故障这个场景的特点是看起来一切正常Pod 是 Running重启次数是 0事件也很安静但服务就是访问不通。根因基本都在 readinessProbe 上。排查要分两步。先确认探针本身能不能通。最常见的三种错法端口写错比如应用监听 8080探针配了 80路径写错比如健康检查接口是/healthz配成了/health协议不匹配应用是 HTTPS探针用 HTTP 去探必然失败。这些都可以进容器里用curl或者wget手动验证一遍最直接。再确认探针参数是否合理。periodSeconds太短会给应用造成额外压力timeoutSeconds太短会让正常响应也被判失败failureThreshold太小则一次抖动就摘流量。我给的经验值是readinessProbe 的timeoutSeconds至少给 3 秒failureThreshold至少 3 次让偶发抖动不至于直接影响可用性。还有一种情况容易被误判成探针问题应用监听的地址是 127.0.0.1 而不是 0.0.0.0。这种情况下从容器内部 curl 自己是通的但 kubelet 从 Pod IP 去探就失败了。我当时排查这个问题花了很久因为思路一直被探针配置框住最后是进容器netstat看监听地址才发现的。3.6 多容器 Pod一个起不来会牵连谁一个 Pod 里放多个容器的时候典型的是业务容器加 sidecar它们共享网络命名空间、共享存储卷生命周期是绑在一起的。所以一个容器出问题影响面比想象中大。initContainer 会阻塞整个 Pod。initContainer 按顺序串行执行任何一个不返回成功后面的容器根本不会被创建Pod 会一直停在Init:0/1之类的状态。这时候主容器日志是空的必须去看 initContainer 的日志。主容器和 sidecar 会互相牵连。Pod 的重启策略是Always时只要有一个容器退出整个 Pod 就会被重建不是只重启那一个容器。所以 sidecar 因为自身原因崩溃会连带业务容器一起重启造成业务无端重启的假象。排查时一定要确认到底是哪个容器的Restart Count在涨。端口冲突是共享网络命名空间的直接后果。两个容器都想监听 8080第二个必然起不来报bind: address already in use。这类问题在多容器 Pod 里非常常见设计时就要把端口规划好。提醒多容器 Pod 的重启计数是分容器统计的。kubectl get pod里那个 RESTARTS 列是所有容器重启次数的总和看不出是谁在重启必须用-o json或者describe逐容器看。4. 深水区网络、存储与节点层面的异常前面几节解决的是容器自己的问题但实际故障里有一大块根因在容器之外。这部分排查难度明显更高因为症状往往表现为应用超时或报错而根因在基础设施。4.1 DNS 解析引发的可疑超时Service 名称解析出问题是 k8s 里最经典的幽灵故障。症状是应用日志里出现no such host或者请求偶发超时 5 秒。这里有个技术细节值得展开容器内/etc/resolv.conf里的ndots:5意味着任何不带点或者点数少于 5 个的域名都会被先当成相对域名依次拼上 search 域去试。一次失败的外部域名解析可能要先试 4 次集群内部的 search 域最后才用绝对域名查一次这中间的耗时可能正好卡在 5 秒。解决办法有几种跨命名空间访问时用完整的 FQDN末尾带点比如svc.ns.svc.cluster.local.能直接走绝对解析或者在 Pod 的 dnsConfig 里调低ndots。另外 CoreDNS 的副本数和节点上的 conntrack 表也会影响大规模场景下的解析稳定性集群规模上去之后这两项都要纳入巡检。排查这类问题的抓手是kubectl exec进容器用nslookup或dig对照测一遍同时看 CoreDNS 的日志和指标。别在应用层反复加超时重试那只是盖住症状。4.2 存储挂载失败为什么会让 Pod 卡住PVC 相关的故障有两种表现形态。一种是 Pod 停在Pending前面已经讲过是 PVC 没绑定。另一种更隐蔽Pod 已经被调度到节点上但卡在ContainerCreating事件里反复出现FailedMount或FailedAttachVolume。第二种情况的常见原因包括存储后端容量不足、挂载点被前一个 Pod 占用没释放多读少写的存储类型尤其容易撞、节点上挂载工具缺失或版本不匹配。排查方式是先看 PVC 和 PV 的状态与绑定关系再看kubectl describe node里有没有存储相关的异常最后看 kubelet 日志里挂载部分的报错。这类问题有个特点它可能只发生在部分节点上所以第一反应应该是是不是某个节点的问题而不是存储坏了。还有个特别容易踩的点挂载目录的权限。配置了securityContext用非 root 用户运行时如果挂载卷的属主是 root 且应用需要写就会启动失败。很多存储类型的fsGroup配置能自动处理属主但需要显式声明默认不生效。4.3 节点层面的压力信号Pod 异常有时候只是受害者真凶在节点。节点出问题时会有几个明确的前置信号我一般看这几个。磁盘压力DiskPressure是最常见的。节点磁盘使用超过阈值时kubelet 会给节点打上DiskPressure污点并开始驱逐 Pod。被驱逐的 Pod 状态会显示Evicted而且这个状态很容易被当成应用崩溃。排查时看kubectl describe node的 Conditions 段落DiskPressureTrue就是它了。日常要做的是配置镜像和日志的自动清理策略别让节点磁盘慢慢被镜像层堆满。内存压力MemoryPressure类似节点内存紧张时会驱逐 QoS 等级最低的 Pod。这就是为什么 BestEffort 的 Pod 总是在内存高峰时莫名其妙消失。PID 压力相对少见但很坑。节点上的进程数包括线程打满后新进程创建会失败容器启动时报fork/exec: resource temporarily unavailable。这类问题在跑大量小线程的服务上会出现排查思路是看节点上的pid.peak指标和 kubelet 配置。另外kubelet 本身的不健康也要考虑。有时节点是 NotReady 状态Pod 全部变成Unknown这时候盯着 Pod 看没有任何意义要直接去看节点上的 kubelet 服务和容器运行时状态。4.4 迁移和压测场景下的 Pod 异常集群迁移是个 Pod 异常的高发期因为环境变了很多原来刚好能用的配置会暴露出来。我在做集群迁移时遇到过几类典型问题值得单独说说。镜像拉不下来新集群的网络策略或者镜像仓库地址变了原来的 imagePullSecrets 在新命名空间里没同步。迁移前要把所有命名空间里的 secret 清点一遍。资源规格不够导致大量 Pending原来的节点规格和新集群不一样按老规格配的 requests 在新集群上调度不进去。迁移前应该先做一次容量核算把所有工作负载的 requests 总和加出来和可用容量比一比留出 20% 到 30% 的余量。存储类不兼容老集群用的存储类和新的不一样PVC 迁移后要么绑不上要么性能差很多。这块最好在迁移前做小规模验证别等全量切过去再发现。迁移完成后的压测环节最容易翻车。压测时 Pod 的异常表现和日常不同CPU 打满导致的限流throttling、内存曲线在峰值时顶到 limit 触发 OOM、连接数暴涨打满文件描述符、下游依赖扛不住导致级联超时——这些在低负载下都看不到。压测调优时我建议的观察顺序是先看 Pod 的 CPU throttling 比率container_cpu_cfs_throttled_seconds_total这类指标再看内存 RSS 峰值接着看 P99 响应时间和错误率的拐点最后才看节点整体水位。压测中出现 OOM 时不要急着加 limit先确认是不是内存泄漏否则只是把崩溃点推后。5. 速查表与实战避坑经验前面把原理和路径讲完了这一节收的是能直接贴在手边的干货。我的习惯是把高频问题和第一反应列成表遇到告警先扫一眼能省下大量瞎猜时间。5.1 常见状态速查表现象第一反应看什么高频根因Pendingdescribe 的 Events资源 requests 不足、亲和性不匹配、PVC 未绑定ContainerCreating 卡住Events、节点挂载状态镜像拉取中、卷挂载失败、CNI 异常ImagePullBackOff镜像地址、imagePullSecrets凭据不对、tag 不存在、架构不匹配CrashLoopBackOffLast State 退出码、--previous 日志启动命令错、依赖未就绪、探针过早杀容器OOMKilledlimit 配置、内存曲线limit 过小、JVM 堆未适配容器、内存泄漏Running 但 ReadyFalse探针配置、监听地址端口路径错、监听 127.0.0.1、探针参数过严Evicted节点 Conditions磁盘压力、内存压力、QoS 等级过低Terminating 卡住finalizers、节点状态优雅退出超时、finalizer 未清理、节点失联5.2 那些文档里不会写的坑坑一只看kubectl get pod的 RESTARTS 列会误判。这一列是所有容器的总和一个 Pod 有业务容器和 sidecar 时你根本不知道是谁在重启。必须用kubectl describe pod看每个容器各自的 restartCount。我曾经因为这个问题排查方向完全跑偏以为是业务崩溃实际上是 sidecar 在反复重启。坑二事件有 TTL过期就没了。默认保留一小时问题发现得晚一点就什么都看不到。我的做法是在告警规则里就把事件一并打包发送而不是等人工去查。对于间歇性复现的问题可以考虑把关键命名空间的事件导到日志系统里长期留存。坑三requests 和 limit 的关系比想象中重要。requests 影响调度limit 影响运行时上限。我见过配置里 requests 给 100m、limit 给 4 核的情况这会导致节点被严重超卖——调度器认为这个 Pod 只要 100m于是往同一个节点上塞了很多个结果运行时大家都想抢 4 核互相拖累。requests 和 limit 的差距建议控制在一倍以内除非你非常清楚业务的突发特性。坑四kubectl exec进不去不代表容器有问题。有些精简镜像里根本没有 shellexec报executable file not found。这种情况下用kubectl debug挂一个临时容器进去排查比纠结镜像里为什么没 shell 有意义得多。坑五复现前先确认是不是曾经好过。这一步非常关键。如果同一个镜像、同一份配置昨天还好好的今天突然不行了那根因大概率在环境侧节点、网络、存储、依赖服务而不是应用代码。反过来如果是一直没跑起来那就要从配置和镜像本身查起。这个判断能直接砍掉一半的排查分支。5.3 把排查能力变成预防能力排查做得再熟也不如让问题少发生。这几年我在几个集群上陆续加了一些预防性措施效果比较明显。统一健康检查规范。给团队定一份探针配置的模板startupProbe 负责启动阶段livenessProbe 只做最小化的存活判断不要在里面调下游依赖readinessProbe 才做完整的依赖检查。把 livenessProbe 写得太重是自找麻烦下游一抖动它就杀容器反而引发雪崩。给关键服务加 PDB。PodDisruptionBudget 能防止节点维护或驱逐时把某个服务的副本一次性带走太多。注意前面提到的抢占场景PDB 也可能成为抢占失败的元凶之一所以要合理设置minAvailable别设成和副本数一样大。把标准化的诊断信息做成脚本。我把前面那六条命令封装成了一个脚本输入命名空间和 Pod 名一次性把所有信息打出来并归档。好处是排查过程可追溯复盘的时候有原始材料而且新人也能照着跑。给资源配比做定期审计。每隔一段时间把各服务的 requests、limit 和实际用量拉出来对比看有没有长期占着不用或者经常逼近上限的情况。这项工作纯靠人记得做不现实最好和监控告警结合起来用量长期低于 requests 30% 就提示下调长期高于 limit 80% 就提示评估扩容。我自己在实际操作中的体会是Pod 故障排查最难的从来不是命令本身而是在信息不全的情况下保持判断的秩序感。状态码、事件、日志、指标这四类信息看起来杂乱但只要你固化了从外到内的检查顺序绝大多数问题都能在十分钟内定位到大致方向。真正费时间的往往是那些配置看起来都对的场景而这类场景的突破口多半藏在那些被 mutating webhook 改过、被默认值填充过、被环境差异影响的细节里——养成看实际生效配置而非仓库配置的习惯能省下非常多绕路的时间。
返回列表