目录
[1. 问题现象:沙箱创建失败与 HTTP 400](#1. 问题现象:沙箱创建失败与 HTTP 400)
[2. 排查路径:排除干扰项,锁定真因](#2. 排查路径:排除干扰项,锁定真因)
[2.1 排除节点调度限制](#2.1 排除节点调度限制)
[2.2 发现 PodCIDR 为空](#2.2 发现 PodCIDR 为空)
[2.3 定位正确的 IPAM 资源类型](#2.3 定位正确的 IPAM 资源类型)
[2.4 确认根因:子网 IP 真实耗尽](#2.4 确认根因:子网 IP 真实耗尽)
[3. 底层技术原理:谁负责给 Pod 分配 IP](#3. 底层技术原理:谁负责给 Pod 分配 IP)
[4. 为什么不能直接 kubectl edit Subnet](#4. 为什么不能直接 kubectl edit Subnet)
[5. 解决方案与最佳实践](#5. 解决方案与最佳实践)
[6. 经验总结](#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. 经验总结
- 不要假设 CNI = IPAM :现代 K8s 网络栈中,CNI 插件与 IP 管理服务常常解耦。排查 IP 问题时,必须先确认谁是真正的 IPAM Owner(本例中为
rama-manager而非 Rama CNI 二进制)。 - 尊重 Webhook 约束:Admission Controller 的拒绝是安全护栏,绕过它通常会导致更严重的状态不一致。应理解其背后的架构约束,寻找正确的操作路径。
- 关注 HTTP 状态码语义:在网络组件交互中,400 表示"当前状态下永远不可能成功",调用方不应重试,而应触发告警或扩容流程,避免无效重试风暴压垮 API Server。
- 建立 IP 容量监控:IP 耗尽是可预测的容量问题,应在 Available/Total < 20% 时触发告警,而非等到归零后被动响应。