Skip to content

Kubernetes IP 或 ENI 耗尽导致的调度失败手册

English · GitHub source English version: README.md

当 Pod 长时间处于 Pending,且 scheduler event 出现 InsufficientIPOrENI、ENI/IP 扩展资源不足,或 ENI 型 Pod 网络缺少容器子网时,可使用本通用手册。本文以腾讯云容器服务(TKE)的 VPC-CNI 为具体示例;所有环境值均为占位符,必须以实际集群生效配置为准。

TKE 文档说明:在 VPC-CNI 模式下,Pod IP 从容器子网分配;一个节点可调度的 Pod 数量受该节点支持的 ENI 和 IP 数量限制;节点与容器子网还必须位于相同可用区。参考 TKE 容器网络概述TKE 容器集群网络规划

目录

安全边界

不要把所有 Pending Pod 都归因为 IP 或 ENI 问题。例如,node.kubernetes.io/unreachable taint 表示节点健康路径,必须单独排查。DaemonSet Pod 虽会自动获得部分 toleration,但这不代表节点健康,也不代表网络资源充足。参见 Kubernetes DaemonSet 文档

变更前,应在本地记录已生效的集群、网络模式、受影响可用区和维护窗口,并确认变更授权;不得提交这些实际值。

  • 未确认子网属于集群 VPC、用于 Pod 分配且位于受影响节点可用区前,不要添加子网。
  • 未区分容器子网 IP 耗尽和节点 ENI/IP allocatable 耗尽前,不要扩容节点。
  • 不要为了让 scheduler event 消失而删除业务 Pod。
  • 未检查副本数、更新策略、可用性要求和回滚路径前,不要大范围执行 rollout restart
  • 不要公开 cluster ID、VPC/subnet ID、CIDR、账号 ID、主机名、业务名、截图或内部控制台 URL。

本地运维策略中的容量数字(例如使用率目标或 IP 预留)不是 Kubernetes 或 TKE 的产品保证。应根据实测工作负载需求和已批准的服务目标设置。

收集症状与容量证据

先收集只读证据。所有占位符只能在获授权的事故环境中替换。

kubectl get pods -A --field-selector=status.phase=Pending
kubectl get events -A --sort-by=.lastTimestamp
kubectl describe pod <pod-name> -n <namespace>
kubectl get nodes -L topology.kubernetes.io/zone
kubectl describe node <node-name>

记录原始 scheduler message 后,将 Pod 请求的扩展资源与候选 Node 的 allocatable 资源对比:

kubectl get pod <pod-name> -n <namespace> \
  -o jsonpath='{range .spec.containers[*]}{.name}{"\t"}{.resources.requests}{"\n"}{end}'

kubectl get node <node-name> \
  -o jsonpath='{.status.allocatable}{"\n"}'

对于 TKE VPC-CNI,tke-eni-ip-webhook 可能会为 Pod 添加 tke.cloud.tencent.com/eni-ip 一类扩展资源 request。TKE 在 webhook 文档 中说明了此 admission 行为和相关资源名。必须查看实际 Pod request、Node allocatable 和网络模式,不能假定资源 key 或网络模式。

还应通过获批准的云控制台或 API 流程收集:

  • 容器子网与集群、VPC 的关联状态。
  • Node 与 Pod subnet 的可用区。
  • 受影响 Pod subnet 剩余可分配 IP。
  • 实际实例规格的 ENI 与辅助 IP 限额。
  • CNI/IPAM 组件健康状态,以及关联 control-plane 或 cloud API 错误。

故障分类

按以下顺序判断。多个条件可能同时存在;所有条件都清除前不得关闭事故。

Pod 持续 Pending
    |
    +-- 候选节点 NotReady 或 unreachable?
    |       +-- 是:先恢复节点健康,不能仅标记为 ENI/IP 耗尽。
    |
    +-- 受影响可用区或集群未关联 Pod subnet?
    |       +-- 是:按变更流程添加同可用区的获批准 Pod subnet。
    |
    +-- Pod subnet 的可用 IP 不足?
    |       +-- 是:扩展获批准的 Pod subnet 容量,再确认 IPAM 已识别。
    |
    +-- Node 的扩展资源 allocatable 已耗尽?
    |       +-- 是:扩容节点,或执行获批准的实例规格/Pod 密度方案。
    |
    +-- 未发现明确容量不足?
            +-- 在重试前检查 CNI/IPAM、admission webhook 与 cloud API 错误。

不要只凭一条 event 推断根因。应关联 scheduler event、Node taint 和 Ready 状态、subnet 状态、可用 IP 数、扩展资源 request,以及 allocatable 容量。

按依赖顺序处置

1. 先解除节点健康阻塞

候选 Node 处于 NotReady 或 unreachable 时,先按已批准的节点健康手册处理。记录 taint、condition 和恢复证据。不要用对 unreachable Node 的 toleration 替代节点健康恢复。

2. 让 Pod subnet 容量可用

若所需 Pod subnet 未关联集群,或不在受影响可用区内,只能通过获批准的云变更流程添加子网。继续前必须确认:

  • 子网属于集群 VPC,且已预留作预期 Pod 网络用途。
  • 子网可用区与受影响 worker node 一致。
  • 它不与现有 Node、Pod 或 Service 地址范围重叠。
  • 它具有满足获批准的近期负载和故障预留的可用 IP。
  • 集群 CNI/IPAM 组件已观察到该变更。

TKE 网络规划文档确认,在 VPC-CNI 模式下可通过添加容器子网处理 Pod subnet IP 不足,并说明了可用区约束。该文档确认的是机制,不是通用的容量计算公式;应使用自己的获批准容量计算。

3. 恢复节点 ENI/IP 余量

若子网仍有可用 IP,但 Node allocatable ENI/IP 已耗尽,应通过获批准的节点池扩容或实例规格流程处理。在容量加入前,避免新工作负载继续进入已耗尽节点;若使用临时 taint,必须记录范围、原因,并在验证后移除。

每次基础设施变更后,先在受影响可用区创建或观察一个低风险、已获授权的工作负载,再重启生产工作负载。确认它能够调度、获得 IP,且没有产生新的网络资源调度 event。

4. 容量看似正常时检查 IPAM 与 admission 组件

若子网与 Node 容量看似充足,检查集群 CNI/IPAM 与 admission 组件。在 TKE VPC-CNI 中,文档列出的组件包括 tke-eni-ipamdtke-eni-agent;不要盲目重启或修改它们。保留脱敏错误特征;若 control plane 或 cloud API 无法分配容量,应向云厂商升级处理。

仅在需要时重启工作负载

增加容量不等于必须重启全部工作负载。仅当需要重新协调,且服务 owner 已批准影响时,才重启受影响 workload。

重启前检查副本可用性和更新策略:

kubectl get deployment <deployment-name> -n <namespace> -o yaml
kubectl get daemonset <daemonset-name> -n <namespace> -o yaml
kubectl get statefulset <statefulset-name> -n <namespace> -o yaml
kubectl get pdb -n <namespace>

获批准后的滚动重启:

kubectl rollout restart deployment/<deployment-name> -n <namespace>
kubectl rollout status deployment/<deployment-name> -n <namespace>

kubectl rollout restart daemonset/<daemonset-name> -n <namespace>
kubectl rollout status daemonset/<daemonset-name> -n <namespace>

kubectl rollout restart statefulset/<statefulset-name> -n <namespace>
kubectl rollout status statefulset/<statefulset-name> -n <namespace>

kubectl rollout restart 支持 Deployment、DaemonSet 和 StatefulSet,参见 kubectl reference。PodDisruptionBudget 会约束部分自愿中断,但 Kubernetes 文档明确说明直接删除 Pod 或 Deployment 可能绕过它。PDB 只是运维决策的一个输入,不能当作重启一定安全的保证。参见 Disruptions

验收条件

只有获授权证据显示以下适用条件全部满足,才可关闭事故:

  • 受影响 Node 为 Ready,所有阻塞 taint 已被解释、移除或有意保留。
  • 集群拥有可用、同可用区且容量足够的 Pod subnet。
  • Node ENI/IP 扩展资源不再耗尽。
  • 一个已获授权的新 Pod 能在受影响可用区成功调度并获取网络资源。
  • 每个受影响 DaemonSet 均达到 DESIRED = CURRENT = READY
  • 每个已重启 Deployment 或 StatefulSet 均完成 rollout,且通过服务专属健康检查。
  • 约定观察期内不再出现 InsufficientIPOrENI scheduler event。
  • 事故记录明确区分已观察证据、已完成动作,以及仍待验证的动作。

常用工作负载检查:

kubectl get pods -n <namespace> -o wide
kubectl get daemonset <daemonset-name> -n <namespace>
kubectl rollout history deployment/<deployment-name> -n <namespace>
kubectl get events -A --sort-by=.lastTimestamp

预防与事故记录

应按可用区监控容量,而不是只看集群总体:

  • 每个 Pod subnet 的可用 IP。
  • 每个节点池的 ENI/IP 扩展资源 allocatable 与 request。
  • 按原因、可用区聚合的 Pending Pod 与 scheduler event。
  • CNI/IPAM 分配错误和 cloud API 失败。

可复用事故记录只应保存脱敏证据:错误特征、资源类型、容量趋势、Node/Pod condition、已完成的变更类别、rollout 结果和观察期。真实环境标识、原始日志、命令历史和截图应保留在获授权的内部事故系统,不应进入公开仓库。