2. CNI 插件架构:CNI 标准接口、容器运行时与网络插件的交互流程
容器网络接口(CNI, Container Network Interface)是 Kubernetes 生态中事实上的网络标准。理解 CNI 的架构设计,是排查网络缓存命中故障、分析 Calico 策略生效路径的前提。本节从接口规范、组件角色、交互流程三个维度展开。
2.1 CNI 标准接口规范
CNI 定义了一组 容器运行时 与 网络插件 之间的契约。该契约以 JSON 格式的配置文件、标准输入(stdin)参数和环境变量为载体,规定了插件必须实现的四个操作:
| 操作 | 方向 | 核心行为 | 典型触发场景 |
|---|---|---|---|
| ADD | 容器 → 网络 | 将容器加入网络命名空间,分配 IP、路由、接口 | Pod 创建、Sandbox 启动 |
| DEL | 网络 → 容器 | 清理网络资源,释放 IP、删除 veth pair | Pod 删除、Sandbox 销毁 |
| CHECK | 运行时 → 插件 | 验证容器网络状态是否与预期一致 | kubelet 周期性同步、网络策略重评估 |
| VERSION | 运行时 → 插件 | 返回插件支持的 CNI 规范版本号 | kubelet 启动时探测插件能力 |
关键约束:CNI 插件必须是独立的二进制可执行文件,运行时通过
exec 调用。插件不能依赖守护进程(daemon)常驻,但 Calico 等高级插件会通过 CNI 链式调用启动 Felix 等后台组件。
2.2 核心组件角色与职责
一次完整的容器网络接入涉及三个角色:
- 容器运行时(Container Runtime):如 containerd、CRI-O。负责调用 CNI 插件,传递网络命名空间路径、容器 ID、网络配置等参数。
- CNI 配置管理(Config Manager):通常由 kubelet 内置的
cniConfig模块承担。读取/etc/cni/net.d/目录下的配置文件,决定调用哪个插件及调用顺序。 - CNI 插件(Plugin):分为三类:
- Main 插件:负责创建网络设备(如 bridge、veth、ipvlan)。
- IPAM 插件:负责 IP 地址分配(如 host-local、calico-ipam)。
- Meta 插件:负责组合多个插件(如 flannel、calico 的 CNI 入口)。
2.3 交互流程:从 Pod 创建到网络就绪
下图展示了 kubelet 收到 Pod 创建请求后,容器运行时与 CNI 插件之间的完整交互路径:
2.4 链式调用与插件组合
Kubernetes 支持 CNI 链式调用(Chaining),即一个网络配置可以指定多个插件顺序执行。前一个插件的输出作为后一个插件的输入。以 Calico 为例,典型的链式配置如下:
{
"cniVersion": "0.3.1",
"name": "k8s-pod-network",
"plugins": [
{
"type": "calico",
"datastore_type": "kubernetes",
"etcd_endpoints": "",
"log_level": "info",
"ipam": { "type": "calico-ipam" }
},
{
"type": "portmap",
"capabilities": { "portMappings": true },
"snat": true
},
{
"type": "bandwidth",
"capabilities": { "bandwidth": true }
}
]
}
故障排查提示:当容器网络缓存命中失败时,应优先检查链式调用中 第一个插件(通常是 Calico 主插件)的 ADD 操作是否成功。若主插件返回错误,后续的 portmap、bandwidth 插件将不会执行,导致 Pod 网络不可用。
2.5 交互流程中的关键数据结构
CNI 插件通过 标准输入(stdin) 接收网络配置,通过 环境变量 接收运行时上下文。以下是最核心的输入参数:
| 参数来源 | 参数名 | 说明 | 缓存故障关联 |
|---|---|---|---|
| 环境变量 | CNI_CONTAINERID |
容器唯一 ID | Calico 用此 ID 查询端点缓存 |
| 环境变量 | CNI_NETNS |
网络命名空间路径(如 /proc/1234/ns/net) |
路径错误会导致 ADD 操作失败 |
| 环境变量 | CNI_IFNAME |
容器内接口名称(通常为 eth0) |
接口名冲突会触发缓存清理 |
| stdin JSON | ipam.type |
IPAM 插件类型 | IPAM 缓存过期导致 IP 重复分配 |
| stdin JSON | capabilities |
插件能力声明 | 能力不匹配导致链式调用中断 |
2.6 缓存故障在交互流程中的典型表现
结合 CNI 交互流程,容器网络缓存命中故障通常出现在以下环节:
- IPAM 缓存未刷新:Calico-ipam 在内存中缓存已分配的 IP 列表。当 Pod 快速创建/删除时,缓存可能未及时同步到 etcd,导致新 Pod 分配到已被占用的 IP。
- 端点缓存(Endpoint Cache)过期:Calico Felix 维护 WorkloadEndpoint 缓存。若缓存未随 Pod 生命周期更新,ADD 操作可能引用已删除的端点信息,造成路由错误。
- CNI 配置缓存:kubelet 会缓存
/etc/cni/net.d/目录的解析结果。修改配置后若不重启 kubelet,新配置不会生效,导致插件调用链与预期不符。
⚠️ 常见误区:不要将 CNI 插件本身的执行日志与容器运行时的日志混淆。排查缓存问题时,应同时查看
kubelet 日志(journalctl -u kubelet)和 calico-node 日志(kubectl logs -n kube-system calico-node-xxxx),对比两者对同一 Pod 的 ADD 操作记录。