目录

1. 问题现象:沙箱创建失败与 HTTP 400

2. 排查路径:排除干扰项,锁定真因

2.1 排除节点调度限制

2.2 发现 PodCIDR 为空

2.3 定位正确的 IPAM 资源类型

2.4 确认根因:子网 IP 真实耗尽

3. 底层技术原理:谁负责给 Pod 分配 IP

4. 为什么不能直接 kubectl edit Subnet

5. 解决方案与最佳实践

6. 经验总结


在 Kubernetes 集群运维中,Pod 无法启动是最常见的故障之一。当错误信息指向网络层时,排查路径往往因 CNI 插件与 IPAM 架构的多样性而变得复杂。本文以一起真实的 FailedCreatePodSandBox 故障为例,完整复盘从表象到根因的排查过程,深入剖析 Rama CNI + alinb Networking 架构下的 IP 分配机制,并明确各组件在 IP 分配链路中的职责边界。

1. 问题现象:沙箱创建失败与 HTTP 400

故障表现:Pod ai-web-proxy 持续处于 ContainerCreating 状态,Events 中反复出现以下警告:

Warning  FailedCreatePodSandBox  kubelet
Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "...": 
[ai/ai-web-proxy-.../...:rama]: error adding container to network "rama": 
request ip return 400 wait for pod ... be coupled with ip failed

初步解读

  • FailedCreatePodSandBox:Kubelet 调用容器运行时创建 Pause 容器时失败,问题发生在 Pod 生命周期的最早阶段。
  • network "rama":集群使用 Rama 作为 CNI 插件。
  • request ip return 400:这是最关键的线索。HTTP 400 (Bad Request) 表明 IPAM 服务端收到了请求但主动拒绝,这区别于 404(资源不存在)、500(服务端崩溃)或超时。拒绝意味着请求参数合法但业务逻辑不允许,最常见的原因是资源配额耗尽或校验失败。
2. 排查路径:排除干扰项,锁定真因
2.1 排除节点调度限制

首先检查节点 c21j03012 的 Pod 容量:

  • maxPods: 220,当前仅运行 41 个 Pod。
  • 节点上无 Pending/Terminating 状态的僵尸 Pod 占用 IP。
  • 结论:问题不在 K8s 调度层,而在网络 IP 供给层。
2.2 发现 PodCIDR 为空

执行 kubectl get node <node> -o jsonpath='{.spec.podCIDR}' 返回空值。在标准 Flannel/kubenet 模式下,这通常是致命错误。但在本集群中,这是一个正常现象——因为 IP 管理权已完全移交给了外部 IPAM 组件,K8s Controller Manager 不再负责分配 PodCIDR。这提醒我们:不能用原生 K8s 网络模型去套用第三方 CNI 架构

2.3 定位正确的 IPAM 资源类型

尝试查询 ramaippool CRD 失败,说明 Rama 在此环境中仅是 CNI 执行器,并非 IPAM 所有者。通过 kubectl get crd | grep networking 发现真正的 IP 管理资源是 alinb Networking 体系下的 subnets.networking.alinb.com。同时确认集群中唯一的 Rama 工作负载为 rama-manager Deployment(3/3 Ready),它同时承担了 IPAM 服务端和 Subnet/IPInstance 控制器的职责,数据面则以二进制插件形式直接存在于节点上。

2.4 确认根因:子网 IP 真实耗尽

查询 Subnet 状态得到决定性证据:

字段

含义

CIDR

10.131.24.0/22

理论 1024 IP

Total

1014

扣除预留后可分配 IP

Used

1014

已分配数 = 总数

Available

0

IP 池彻底枯竭

至此,故障链条闭合:Subnet IP 耗尽 → rama-manager 拒绝分配(400) → Rama CNI 透传错误 → Kubelet 创建沙箱失败

3. 底层技术原理:谁负责给 Pod 分配 IP

在 Kubernetes 中,Pod IP 的分配并非由单一组件完成,而是一个涉及 Kubelet、CRI、CNI 插件及 IPAM 服务的多层协作流程。在本案例架构中,具体职责划分如下:

组件

角色

是否决定具体 IP

说明

Kubelet

流程触发者

发起 Sandbox 创建请求,本身不分配 IP

CRI (containerd)

标准化调用桥梁

创建 net namespace 后调用 CNI 插件

Rama CNI (节点二进制插件)

网络配置执行者 + IPAM 客户端

向 rama-manager 申请 IP,并将获取到的 IP 配置到 Pod 网卡

rama-manager (IPAM 服务)

IP 决策、分配与状态管理

唯一 IP 分配入口,根据 Subnet CRD 选择可用 IP 并创建 IPInstance

云平台 VSwitch

物理地址空间供给

✅ (定义上限)

Subnet CRD 是其只读映射,扩容必须先在云平台侧完成

⚠️ 关键认知:在原生 K8s 中,kube-controller-manager 的 Node IPAM Controller 负责为节点分配 podCIDR,再由 host-local 等本地 IPAM 从 podCIDR 中为 Pod 分 IP。但在本案例的云原生网络架构中,这一职责已完全上移至中心化的 rama-manager,节点 podCIDR 字段因此为空。理解这一架构差异,是避免误判故障根因的前提。

4. 为什么不能直接 kubectl edit Subnet

在排查过程中,尝试将 CIDR 从 /22 改为 /21 被 Webhook 拒绝:

admission webhook "rama-v1.validating.rama" denied the request: must not change range CIDR

这体现了云原生网络的基础设施即代码(IaC) 单向同步原则:K8s CRD 是底层网络资源的只读投影,而非权威数据源。正确的扩容路径必须是:云平台控制台扩展 VSwitch → alinb Networking Controller 感知变更 → 自动更新 Subnet CRD → IPAM 可用 IP 增加。反向操作被设计上禁止,因为 K8s 无法驱动底层交换机修改路由表。

5. 解决方案与最佳实践

优先级

方案

操作要点

P0 紧急

释放低优先级 Pod

迁移非关键负载到其他子网节点,立即回收 IP

P1 短期

清理孤儿 IPInstance

检查 spec.podRef 指向已删除 Pod 的记录并手动删除

P2 中期

新增 Subnet

创建新 CIDR 的 Subnet CRD 关联到同一 Network,调整调度亲和性引导新 Pod

P3 长期

云平台扩容 VSwitch

联系基础设施团队扩展底层地址段,从根本上解决容量问题

6. 经验总结
  1. 不要假设 CNI = IPAM:现代 K8s 网络栈中,CNI 插件与 IP 管理服务常常解耦。排查 IP 问题时,必须先确认谁是真正的 IPAM Owner(本例中为 rama-manager 而非 Rama CNI 二进制)。
  2. 尊重 Webhook 约束:Admission Controller 的拒绝是安全护栏,绕过它通常会导致更严重的状态不一致。应理解其背后的架构约束,寻找正确的操作路径。
  3. 关注 HTTP 状态码语义:在网络组件交互中,400 表示"当前状态下永远不可能成功",调用方不应重试,而应触发告警或扩容流程,避免无效重试风暴压垮 API Server。
  4. 建立 IP 容量监控:IP 耗尽是可预测的容量问题,应在 Available/Total < 20% 时触发告警,而非等到归零后被动响应。

更多推荐