跳到正文

K8s 生产踩坑 ​

一句话:K8s 的多数「坑」不是它做错了,而是把它当成「自动运维」——它的每个机制都有明确的作用域,超出作用域的行为(比如指望 PDB 防节点失联)一定落空。

适用版本:Kubernetes 1.29+。默认值以官方文档为准。

为什么需要 ​

K8s 把很多运维动作自动化了,但自动化的边界不直观:它会重启容器、补副本、摘流量、驱逐 Pod,但不会替你判断「这个重启是不是在放大故障」,也不会在节点失联时遵守 PDB。生产事故往往来自对这些边界的误解,而不是组件本身有 bug。

下面五类问题覆盖了最常见的故障面,每类按「现象 → 证据 → 根因 → 处置」组织。

核心机制 ​

一、探针配错,Pod 循环重启 ​

现象:Pod 反复 CrashLoopBackOff,业务日志里没有异常。

证据:kubectl describe pod 的 Events 里有 Liveness probe failed;kubectl logs --previous 看不到启动报错。

根因:initialDelaySeconds 太短或 timeoutSeconds=1 太紧,应用还在启动或 GC 停顿,liveness 就判定失败并杀容器。重启后又要重新启动,形成循环。

处置:慢启动应用加 startupProbe,窗口按 P99 启动时间设;liveness 的 failureThreshold 放大到能容忍一次停顿,不要把下游依赖检查塞进 liveness。

二、没设内存 limit,节点被拖垮 ​

现象:一个节点上多个 Pod 被 Evicted,节点 NotReady,其他服务跟着抖。

证据:kubectl describe node 出现 MemoryPressure;kubectl top pods --all-namespaces --sort-by=memory 能看到某个 Pod 一路涨;kubectl describe pod 显示 OOMKilled 或 Evicted。

根因:Pod 没设 limits.memory,内存泄漏把节点可用内存吃光。kubelet 先按 QoS 驱逐 BestEffort/Burstable,再触发系统 OOM,关键系统 Pod 也可能被杀。

处置:所有 Pod 设 limits.memory;核心服务 requests = limits 拿 Guaranteed;命名空间加 ResourceQuota 和 LimitRange 兜底。limit 不是越小越好,压得太低会让正常峰值也 OOM。

三、节点 NotReady,PDB 并不会保护你 ​

现象:一个节点失联,上面的 Pod 被批量删除并在其他节点重建,服务短暂不可用;PDB 明明配了 minAvailable,却没有阻止。

证据:kubectl get pods -o wide 显示原节点上的 Pod 消失;kubectl describe node 的 Conditions 是 Ready=Unknown;Events 里有 NodeNotReady 或 TaintManagerEviction。

根因:PDB 只约束自愿中断——kubectl drain、Eviction API、集群升级、descheduler。节点失联属于非自愿中断:节点控制器给节点打上 node.kubernetes.io/unreachable:NoExecute 污点,Pod 在容忍时间(默认 300s,以官方文档为准)后被删除,控制器在别处重建,整个过程不经过 PDB。

处置:用 podAntiAffinity 或 topologySpreadConstraints 把副本打散到不同节点/可用区;保证集群有余量接住重建;跨可用区部署。PDB 仍然要配,但它保的是维护窗口,不是节点故障。

四、改了 ConfigMap,应用还读旧配置 ​

现象:更新 ConfigMap 后等了几分钟,容器里的文件变了,应用行为没变;或者文件根本没变。

证据:kubectl exec <pod> -- cat /path/config 看容器内文件;kubectl describe pod 看挂载方式和更新时间。

根因:kubelet 同步 ConfigMap 有周期(约 1 分钟,以官方文档为准);用 subPath 挂载的文件不会随 ConfigMap 更新;应用可能只在启动时读一次配置,文件变了也不重载。

处置:不支持热加载的应用,改完 ConfigMap 后 kubectl rollout restart deployment/<name>;需要自动触发的用 checksum 注解或配置中心;不要用 subPath 挂需要更新的配置。

五、Service 正常,但调用方 connection refused ​

现象:Service 的 ClusterIP 能解析,调用方却间歇性 connection refused 或超时。

证据:kubectl get endpoints <svc> 看 Endpoint 是否有 Pod IP;kubectl get pods -o wide 看 Pod 是否 Ready;kubectl describe pod 看 readiness 是否失败。

根因:分两种。Endpoint 为空,是 selector 不匹配或 Pod 没 Ready,新连接根本没有后端;Endpoint 正常但仍报错,多半是长连接被 conntrack 钉在已经删除的旧 Pod IP 上,或者调用方直连了 Pod IP 而不是 Service。

处置:先查 Endpoint 和 readiness;连接池配空闲回收和重试;gRPC 这类长连接用 Headless Service 加客户端负载均衡,不要指望 ClusterIP 在 Pod 重建后自动迁移已有 TCP 连接。

最小上手 ​

按「先看症状,再看事件,最后看日志」的顺序:

bash
# 1) 找出不健康的 Pod
kubectl get pods -A -o wide | grep -v -E 'Running|Completed'

# 2) 看 Events:调度失败、探针失败、OOM、驱逐原因都在这
kubectl describe pod <pod> -n <ns> | sed -n '/Events:/,$p'

# 3) 看上一次退出的日志和退出码
kubectl logs <pod> -n <ns> --previous
kubectl get pod <pod> -n <ns> \
  -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}{" "}{.status.containerStatuses[*].lastState.terminated.exitCode}{"\n"}'

# 4) 服务不通先查 Endpoint
kubectl get endpoints <svc> -n <ns>

# 5) 节点问题查 Conditions 和资源分配
kubectl describe node <node> | grep -A6 Conditions
kubectl describe node <node> | grep -A10 'Allocated resources'

生产动作 ​

  • 资源:所有容器设 requests/limits;核心服务 requests = limits;配额命名空间用 LimitRange 给默认值。
  • 探针:慢启动配 startupProbe;liveness 只判死锁;readiness 快速摘流量;阈值按 P99 而不是平均值。
  • 可用性:PDB + 反亲和/拓扑打散同时配;PDB 的 ALLOWED DISRUPTIONS 必须大于 0,否则 drain 会卡住。
  • 配置:ConfigMap 变更走滚动重启或配置中心;不要用 subPath 挂需要热更新的文件。
  • 发布:镜像固定 tag/digest,revisionHistoryLimit 保留历史(默认 10,以官方文档为准),确保 kubectl rollout undo 有版本可退。

边界 ​

  • PDB 不防节点故障:它只约束自愿中断;节点失联时 Pod 会被直接重建,保护要靠打散和冗余。
  • limit 不是稳定性保证:设了 limit 仍可能 OOM,只是把影响限制在单个容器;内存泄漏要修代码。
  • 自动重启会放大故障:liveness 把下游抖动误判为自身死亡时,所有副本会一起重启;阈值要留余量。
  • Endpoint 正常不等于链路正常:网络策略、conntrack、连接池、DNS 缓存都可能在 Endpoint 之后出问题。
  • K8s 不负责数据一致性:滚动发布期间新旧版本并存,schema 和消息格式必须前后兼容。

讲清楚 ​

K8s 生产故障大多不是组件 bug,而是作用域误判。最典型的是 PDB:它只约束 drain、升级这类自愿中断,节点 NotReady 时 Pod 会被直接删除重建,PDB 不会拦。节点故障要靠反亲和、拓扑打散和冗余容量。

第二类是资源:没设 limit 的 Pod 能把节点内存吃光,触发驱逐和雪崩;设了 limit 但太小,正常峰值也会 OOM。核心服务用 requests = limits 拿 Guaranteed。

第三类是探针和配置:liveness 太紧会把慢请求当死锁,所有副本一起重启;ConfigMap 更新不会自动让应用重载,subPath 挂载甚至不会更新文件。排障时先看 Events,再看上一次日志和退出码,不要上来就重启。

把一门技术讲透到既懂也会用