Spiderpool 多网卡 IPAM 深度解析:Underlay 网络缺的那块拼图

Macvlan、ipvlan、SR-IOV 这几个 CNI 有个共同特点:数据面很漂亮,管理面近乎空白。它们能把 Pod 直接挂到物理网络上,省掉隧道封装的开销,但你问它"这个 IP 能不能跟着 Pod 漂移到别的节点"、“Pod 崩了 IP 怎么收回来”、“两张网卡的回包怎么别走错路由”,它们基本答不上来。

Spiderpool 要补的就是这块。它不是又一个新 CNI——它不碰数据面,而是给这些原生 Underlay CNI 配一套企业级的 IPAM、路由协调和连通性增强,让它们从"能跑"变成"能生产"。

这篇文章分四块:

  1. 为什么 Underlay 需要专门的 IPAM——Overlay 和 Underlay 对 IPAM 的需求差在哪;
  2. 架构与 CRD 体系——controller-agent 怎么分工,六个 CRD 各管什么;
  3. 多网卡 IPAM 全流程——从网卡划分、池选择到策略路由协调、垃圾回收;
  4. 实战与坑——部署示例,以及几个文档里容易看漏的细节。

先说一个容易踩的坑。 coordinator 的配置字段在两个地方拼写不一样:SpiderCoordinator CRD 里是 podDefaultRouteNIC、podDefaultCniNIC(NIC 三个字母全大写),而写进 CNI conflist 时是 podDefaultRouteNic、podDefaultCniNic(Nic 首字母大写)。同一个概念,两种序列化上下文。照着 CNI 文档往 CRD 里填会静默不生效,这个后面第五节细说。


一、为什么 Underlay 需要专门的 IPAM

云原生网络分两个流派,对 IPAM 的需求差得很远。理解这个差异,是理解 Spiderpool 全部设计动机的前提。

维度 Overlay(Calico/Cilium) Underlay(Macvlan/ipvlan/SR-IOV)
IP 来源 独立网络平面,隧道封装 宿主机所在的物理网络
IP 分配粒度 子网按节点切成 IP block,本地分配 需要全集群任意节点可分配
IP 资源 充沛,可随意划分 稀缺,是真实的物理资源
东西向通信 NAT + ClusterIP 直通,原生不支持 ClusterIP
对固定 IP 的需求 弱 强
回收失败的后果 几乎无感 新 Pod 直接起不来

上表把 Cilium 归在 Overlay 一列是简化说法——Cilium 并非纯 Overlay,它同时支持 Overlay 和 Underlay(Native Routing)两种模式,详见下面 1.1 的澄清。

Underlay 场景对 IPAM 的四个硬要求:

单个 IP 可在任意节点分配。 有固定 IP 需求的副本可能漂到任意节点,IP 得跟着 Pod 走,而不是绑死在某个 Node 的 IP block 上。Overlay 那套"按节点切 block"的做法在这里直接失效。

同一应用跨子网取 IP。 集群跨数据中心或跨 VLAN 时,同一个 Deployment 的不同副本,需要在各自节点上拿到匹配本地子网的 IP。

IP 强固定。 传统应用上云前跑在裸金属上,服务之间要感知源/目的 IP,防火墙还要按 IP 做精细管控。这个需求在金融、制造、能源这些行业里是硬约束,不是"最好有"。

强回收。 IP 是稀缺物理资源。分配失败或 Pod 崩溃后收不回来,池子很快就被泄露的 IP 占满,新 Pod 全部 pending。

原生 Macvlan/ipvlan/SR-IOV 对这四条几乎全部无能为力。这就是 Spiderpool 的切入点。

1.1 常见误解:Cilium 是"纯 Overlay"吗

先澄清一个容易搞混的判据:"给 Pod eth0 分配 IP"并不是 Overlay 的特征——所有 CNI 插件都会给 Pod 网卡分配 IP,Macvlan、SR-IOV 同样会给 Pod 分配 IP 并配置到 eth0 或 net1 上。真正决定一个方案属于 Overlay 还是 Underlay 的,是两个维度:Pod IP 的来源(集群内部的独立 IP 空间,还是物理网络现有的子网)和数据面的转发机制(隧道封装,还是直接走物理网络):

维度 Overlay Underlay
Pod IP 来源 集群独立 Pod CIDR(如 10.0.0.0/16) 物理网络现有子网(与节点同网段)
数据面转发 VXLAN/Geneve 隧道封装 直接经物理交换机/路由器转发
物理网络感知 无需感知 Pod IP 天然可路由 Pod IP
MTU 因封装头开销需降低 与物理网卡一致,无损耗

Cilium 实际支持三种网络模式,官方文档明确列出,并且允许混合使用:

  • VXLAN Overlay(默认):Pod IP 从集群 Pod CIDR(如 10.0.0.0/16)分配,每个节点分得一个子块;跨节点流量封装在 VXLAN 隧道中,物理网络完全无需感知 Pod IP 的存在;
  • Geneve Overlay:同上,封装协议换成 Geneve;
  • Native Routing(直连路由):不封装,所有非本节点 Pod 的数据包直接交给 Linux 内核路由子系统转发。代价是物理网络必须能路由 Pod CIDR——要么通过节点静态路由,要么通过 BGP 动态通告。此外 Cilium 还支持 Multi-Pool IPAM,从 CiliumPodIPPools 分配 Pod CIDR,并通过 BGP Control Plane 把这些 CIDR 通告给 Underlay 网络。

关键澄清:Native Routing 不等于 Spiderpool 所说的 Underlay。 Cilium Native Routing 虽然不用隧道,但 Pod IP 仍来自独立的 Pod CIDR,而非物理网络的现有子网——这两者有本质区别:

Cilium Native Routing Spiderpool + Macvlan/SR-IOV
Pod IP 10.0.x.x(虚拟 IP 空间) 192.168.1.x(与节点同一物理子网)
物理网络配合 路由器需额外添加路由表项 交换机天然知道如何路由,零额外配置

所以更准确的说法是:Cilium Native Routing 是**“无隧道的 Overlay”——需要物理网络配合转发的虚拟网络;而 Spiderpool 针对的是"Pod 直接使用物理网络真实 IP"**的场景。

回到 Spiderpool 的架构,Cilium 的角色就清楚了:

1
2
3
4
5
6
7
8
9
10
11
Pod 网络接口:
├── eth0 ← Cilium/Calico (Overlay 或 Native Routing)
│ 用途: 集群东西向通信、Service 访问
│ IP 来自: Pod CIDR
│
├── net1 ← Macvlan/SR-IOV (Underlay) + Spiderpool IPAM
│ 用途: 南北向流量、对外服务、RDMA
│ IP 来自: 物理网络现有子网
│
└── net2 ← 另一个 Macvlan/SR-IOV (Underlay)
用途: 存储网络、管理网络分离

Spiderpool 的典型用法是让 Cilium/Calico 管理 eth0(集群内部通信),Spiderpool 接管 net1/net2 等 Underlay 网卡。coordinator 插件会检测到非 eth0 网卡由 Underlay CNI 创建,自动进入 overlay 模式,同步集群默认 CNI 的 Pod 子网——把 spidercoordinator.spec.podCIDRType 设为 cilium 即可自动探测 Cilium 的 Pod CIDR,并注入路由保证 Pod 从 eth0 访问 Service 时回包也走 eth0(这正是 8.2 节"别让它猜"那条运维建议的正面用法)。


二、整体架构

Spiderpool 走的是经典的 controller-agent 模式,外加几个 CNI chain plugin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
┌──────────────────────────────────────────────────────────────┐
│ Kubernetes 集群 │
│ │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ Spiderpool │◄──────►│ Spiderpool Agent │ │
│ │ Controller │ │ (DaemonSet / 每节点) │ │
│ │ (Deployment) │ │ · 安装 CNI 二进制 │ │
│ │ · CRD 校验/状态 │ │ · 响应 IP 分配请求 │ │
│ │ · IP 分配/回收 │ │ · 与 Controller │ │
│ │ · 自动化 IP 池 │ │ 交互完成分配/释放 │ │
│ └────────────────────┘ └─────────┬──────────┘ │
│ │ Unix Socket │
│ CRD 资源: ▼ │
│ SpiderIPPool ┌──────────────────────────────┐ │
│ SpiderSubnet │ IPAM Plugin(供 main CNI 调用) │ │
│ SpiderMultusConfig└──────────┬───────────────────┘ │
│ SpiderEndpoint │ │
│ SpiderCoordinator ┌──────────▼───────────────────┐ │
│ SpiderReservedIP │ CNI Chain Plugins: │ │
│ │ · coordinator(路由协调) │ │
│ │ · ifacer(VLAN/Bond 父接口) │ │
│ │ · rdma(RDMA netns 隔离) │ │
│ └──────────┬───────────────────┘ │
│ │ │
│ ┌──────────▼───────────────────┐ │
│ │ Main CNI: │ │
│ │ Macvlan / ipvlan / SR-IOV / │ │
│ │ OVS / Calico / Weave … │ │
│ └──────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘

各组件职责:

组件 类型 核心职责
Spiderpool Controller Deployment 与 API-Server 交互,管理各 CRD 的校验、创建与状态;响应 Agent 请求完成 IP 分配、释放、回收;驱动自动化 IP 池
Spiderpool Agent DaemonSet 安装 Multus、coordinator、IPAM 等插件到节点 /opt/cni/bin;响应 CNI 的 IP 分配请求;与 coordinator 同步配置
IPAM plugin 二进制 供 main CNI 调用的 IPAM 实现,实施具体分配
coordinator chain plugin main CNI 之后执行:多网卡路由调谐、IP 冲突检查、宿主机连通性、MAC 固定
ifacer chain plugin 动态创建 Bond / VLAN 子接口,作为 Macvlan/ipvlan 的父接口
Multus CNI meta plugin CNI 调度器,负责给 Pod 挂多张网卡
SR-IOV Operator Operator 简化 sriov-cni 的安装与配置
RDMA device plugin Device Plugin 发现主机共享 RDMA 设备;RDMA CNI 做网络命名空间隔离

关键点:Spiderpool 不接管数据面。流量怎么走还是 Macvlan/SR-IOV 说了算,Spiderpool 管的是"谁拿哪个 IP"和"路由表怎么写"。


三、CRD 资源体系

六个 CRD 构成分层结构:

CRD 作用
SpiderIPPool IP 池:IP 范围、网关、路由、各类亲和性
SpiderSubnet 子网:IPPool 的上层模板,支持自动创建/扩缩/回收 IPPool
SpiderMultusConfig 封装 Multus NetworkAttachmentDefinition 的创建
SpiderEndpoint 记录 Pod 的 IP 分配详情,用于回收
SpiderCoordinator coordinator 的全局默认配置
SpiderReservedIP 保留 IP,不参与分配

3.1 SpiderIPPool 的关键字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
spec:
subnet: 172.18.41.0/24 # 池所属子网
ips: # IP 范围
- 172.18.41.10-172.18.41.100
excludeIPs: # 排除范围
- 172.18.41.50
gateway: 172.18.41.1
routes: # 自定义路由
- dst: 10.0.0.0/8
gw: 172.18.41.1
ipVersion: 4 # 4 / 6
default: false # 是否集群默认池
multusName: # 限定仅被特定 NAD 使用
- kube-system/macvlan-ens192
# 亲和性(决定这个池能被谁用)
podAffinity: {}
namespaceAffinity: {}
namespaceName: []
nodeAffinity: {}
nodeName: []

亲和性这组字段是 Underlay 场景的关键——跨子网时,你需要让"A 机房的节点只能用 A 子网的池",nodeAffinity/nodeName 正是干这个的。

3.2 SpiderSubnet 与 SpiderIPPool 的父子关系

启用 SpiderSubnet 后,每个 IPPool 都归属于同 CIDR 的 Subnet,并从中继承网关、路由等属性。这让自动化池管理成为可能:应用扩缩容时,Spiderpool 可以从 Subnet 里切出新的 IPPool,而不用运维手动算 IP 段。


四、多网卡 IPAM 工作流程

4.1 网卡划分:Multus + SpiderMultusConfig

Kubernetes 原生只给 Pod 一张网卡。Spiderpool 集成 Multus 扩展这个能力,但不要求你手写 NetworkAttachmentDefinition——你写 SpiderMultusConfig,它自动翻译成 NAD。

一个典型的生成结果(conflist):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"cniVersion": "0.3.1",
"name": "sriov-match-master-subnet",
"plugins": [
{
"type": "sriov",
"vlan": 100,
"min_tx_rate": 0,
"max_tx_rate": 0,
"ipam": {
"type": "spiderpool",
"match_master_subnet": true,
"default_ipv4_ippool": ["rdmarail1-*"]
}
},
{ "type": "rdma" },
{ "type": "coordinator" }
]
}

结构很清晰:main CNI(sriov)+ IPAM(spiderpool)+ chain plugins(rdma、coordinator)。注意 chain plugin 的顺序——coordinator 必须在最后,因为它要在网卡都配好之后才能调谐路由。

这里有个和开头那个坑同源的现象:CNI conflist 里是 snake_case 的 match_master_subnet,而 SpiderMultusConfig CRD 里用 camelCase。CRD 是给人写的,conflist 是给 CNI runtime 读的,两套命名规范各管一段。

4.2 IP 池选择:五级回退

Spiderpool 的池选择是多级回退策略,优先级从高到低:

1
2
3
4
5
6
7
8
9
10
11
1. Pod Annotation 指定池
ipam.spidernet.io/ippool 或 ipam.spidernet.io/subnet
↓ 没有则
2. 多网卡 Annotation(按 interface 区分)
ipam.spidernet.io/ippools 或 ipam.spidernet.io/subnets
↓ 没有则
3. Namespace 级默认池
↓ 没有则
4. CNI 网络配置中指定的默认池
↓ 没有则
5. 集群默认池(spec.default: true)

排查"Pod 拿到了意料之外的 IP"时,按这个顺序往下查基本能定位。

4.3 多网卡池指定

给每张网卡指定不同的池:

1
2
3
4
5
ipam.spidernet.io/ippools: |-
[
{"interface": "eth0", "ipv4": ["v4-ippool1"], "ipv6": ["v6-ippool1"], "cleangateway": true},
{"interface": "net1", "ipv4": ["v4-ippool2"], "ipv6": ["v6-ippool2"], "cleangateway": false}
]

cleangateway 这个字段值得注意:它控制是否清除该网卡从池里继承的默认路由。多网卡场景下你只想要一张网卡提供默认路由,其他网卡的默认路由必须清掉,否则路由表会打架。

SpiderIPPool 的 routes 字段可以注入自定义路由,但如果已经设置了 gateway,就别再往 routes 里塞 0.0.0.0/0——两者会冲突。

4.4 自动池:SpiderSubnet

手工维护 IPPool 在副本数频繁变化时很痛苦。SpiderSubnet 提供"零运维"模式:

1
2
3
4
5
6
7
annotations:
ipam.spidernet.io/subnets: |-
[
{"interface": "eth0", "ipv4": ["subnet-demo-v4-1"]},
{"interface": "net2", "ipv4": ["subnet-demo-v4-2"]}
]
ipam.spidernet.io/ippool-ip-number: "+2"

工作方式:

  • Spiderpool 为这个 Deployment/StatefulSet/Job 自动创建专属 IPPool;
  • +2 是弹性模式——池子大小 = 副本数 + 2,副本数变化时自动扩缩;
  • 应用删除后,自动回收池子。

不带 + 就是固定数量。弹性模式的那个 +2 是给滚动更新留的余量:滚动期间新旧 Pod 并存,不留余量会卡住。

4.5 coordinator:多网卡路由协调

这是 Spiderpool 里技术含量最高的部分。coordinator 作为 chain plugin 在 main CNI 之后执行,解决四个问题:

问题一:Underlay Pod 访问不了 ClusterIP。
Macvlan 的 Pod 流量直接走物理网卡,根本不经过宿主机的 iptables/IPVS,自然碰不到 kube-proxy 的 Service 规则。coordinator 的解法是给 Pod 额外创建一对 veth 设备,东西向流量走 veth 回到宿主机协议栈,南北向走 Macvlan 网卡。

问题二:多网卡来回路径不一致。
这是多网卡最经典的坑。请求从 net1 进来,回包却从 eth0 出去,中间任何一个有状态设备(防火墙、LB)都会把这个包丢掉。coordinator 调谐策略路由表,保证从哪张网卡进就从哪张网卡出。

问题三:Pod 和宿主机不通。
kubelet 的 liveness/readiness 探针从宿主机发起。Macvlan 有个著名限制——同一父接口上的 Macvlan 子接口和宿主机之间无法直接通信。健康检查因此永远失败。coordinator 通过那对 veth 和路由控制打通这条路径。

问题四:MAC 地址漂移。
支持固定 Pod 的 MAC 前缀,给需要 MAC 绑定的场景用。

4.6 深挖:为什么 Underlay Pod 碰不到 ClusterIP——iptables/IPVS 的命名空间边界

上面问题一值得单独展开,因为它的根因不是 Macvlan 的 bug,而是 Linux 网络栈的结构性事实。核心原因一句话:iptables/IPVS 挂载在宿主机网络命名空间的 IP 栈上,而 Macvlan 让 Pod 的 IP 接口直接接到物理链路上,宿主机只做二层转发,包不进入宿主机的 IP 层,所以宿主机上的 iptables/IPVS 根本没有机会处理这些包。

iptables/IPVS 的作用域是"某个网络命名空间"。 Linux 的 netfilter 钩子(iptables、IPVS、conntrack)是按网络命名空间隔离的。kube-proxy 通常把 Service 的 iptables/IPVS 规则写在宿主机 netns 里;只有进入宿主机 netns 的 IP 包,才会经过宿主机 IP 层,触发 PREROUTING、FORWARD、INPUT、OUTPUT 等钩子。而 Pod 有自己独立的 netns,Pod 里发出的包默认不会自动进入宿主机 netns 的 IP 栈。

Macvlan 的数据路径绕过了宿主机 IP 层。 对比一下普通 bridge CNI 的路径:

1
2
Pod netns → veth pair → 宿主机 netns → Linux bridge
→ 宿主机 iptables/IPVS → 物理网卡

veth 的一端在 Pod netns,另一端在宿主机 netns,流量必须进入宿主机网络栈,iptables/IPVS 就能生效。

而 Macvlan 的路径是:

1
Pod netns → macvlan 子接口 → 父物理网卡(仅二层) → 物理交换机

Macvlan 子接口位于 Pod netns 内,拥有独立的 MAC 和 IP;父接口(物理网卡)虽然在宿主机 netns 中,但只作为二层出口使用。包从 Pod 协议栈发出后,经 Macvlan 驱动直接交给父物理网卡发送,不进入宿主机的 IP 路由和 netfilter 处理。返回流量也类似:物理网卡收到帧后,按目的 MAC 直接转发给对应的 Macvlan 子接口,进入 Pod netns,同样不经过宿主机 IP 层。

IPVS 为什么也失效? IPVS 本质上是 netfilter 框架的一部分,工作在宿主机 netns 的 LOCAL_IN / FORWARD 等钩子上。它的典型逻辑是:Pod 发出目的地址为 Service ClusterIP 的包 → 包必须进入宿主机 netns → kube-proxy 写入的 IPVS 规则命中,把 ClusterIP DNAT 成某个后端 Pod IP。但 Macvlan Pod 的默认路由通常直接指向物理网关,发往 ClusterIP 的包被当成普通 IP 包从 Macvlan 接口发到物理网络——物理交换机不认识 ClusterIP,也不会把包送回宿主机,IPVS 规则完全不会被查询。

实际运维中会看到的现象:

  • Service 的 ClusterIP 无法访问或行为异常;
  • NetworkPolicy、NAT、conntrack 等依赖宿主机 netfilter 的功能可能失效;
  • 宿主机 iptables -L 的计数器看不到这些 Pod 的转发流量;
  • 宿主机 tcpdump 在父接口上可能只看到二层帧,看不到经过宿主机 IP 栈的包。

两个例外:

  1. 如果 Pod 还保留了一张由集群默认 CNI 提供的接口(比如 Multus 场景下的默认 eth0),走该接口的流量仍会经过宿主机 iptables/IPVS,Macvlan 接口只用于特定高性能网络——这正是 1.1 节那张多网卡接口图的分工逻辑;
  2. 如果宿主机主动与 Macvlan Pod 通信,可能涉及宿主机 IP 栈,但同一父接口上 Macvlan 子接口和宿主机之间通常存在隔离,配置不当会不通(就是 4.5 问题三那个坑)。

想在 Macvlan 下恢复 Service/NetworkPolicy,一般需要额外方案:eBPF CNI(Cilium)、Pod 内策略,或 Multus 保留默认网络接口——Spiderpool 的 coordinator 走的是第三条路的增强版:补一对 veth 把东西向流量导回宿主机协议栈。所以,Macvlan 不是"故意绕过"iptables/IPVS,而是它的设计就是把 Pod 直接放进物理二层网络,宿主机只当二层交换机用,自然不会触发宿主机三层 netfilter。

4.7 coordinator 的字段:两套命名

现在回到开头那个坑。coordinator 的配置分两个层面:

SpiderCoordinator CRD(全局默认,单例,名字固定叫 default,安装时自动生成):

字段 说明 默认值
mode auto / underlay / overlay / disable auto
podCIDRType 集群 Pod CIDR 的获取方式:auto/cluster/calico/cilium/none auto
podDefaultRouteNIC Pod 默认路由所在网卡 underlay 为 eth0,overlay 为 net1
podDefaultCniNIC Pod 第一张网卡的名字 eth0
hostRuleTable 主机访问 Pod Underlay IP 的策略路由表号 500
hijackCIDR 需额外从主机转发的子网 [](空)
tunePodRoutes 是否调谐 Pod 路由 true
detectGateway 网关可达性检测 false
detectIPConflict IP 冲突检测 false
podMACPrefix 固定 MAC 前缀 ""(关闭)
txQueueLen 发送队列长度 0(不改)

三个需要留意的点:

  1. NIC vs Nic:上表是 CRD 的写法(全大写 NIC)。写进 NetworkAttachmentDefinition 或 SpiderMultusConfig 的 CNI 配置段时,是 podDefaultRouteNic、podDefaultCniNic(首字母大写 Nic)。填错不会报错,只会静默不生效。

  2. overlayPodCIDR 和 serviceCIDR 在 .status 里,不在 .spec。这两个字段是 Spiderpool 被动探测填充的,不是给你配的。有些资料把它们列成 spec 字段,照着配会被 webhook 拒绝或忽略。

  3. 优先级:NAD / SpiderMultusConfig 里显式设置的字段 覆盖 SpiderCoordinator 的全局默认值;没设置的才回退到全局。

hijackCIDR 是实际运维中最常改的字段。比如要让 Pod 访问 nodelocaldns 或某些集群外服务时经过宿主机协议栈:

1
2
kubectl patch spidercoordinators default --type='merge' \
-p '{"spec": {"hijackCIDR": ["169.254.20.10/32", "1.1.1.1/32"]}}'

注意:已经在运行的 Pod 必须重启才能让新路由进入它的 netns。这条改完不重启等于没改。

4.8 IP 回收与垃圾回收

IP 泄露在 Underlay 场景是致命的,Spiderpool 的 GC 设计得比较细。

SpiderIPPool 的删除保护:

1
2
3
4
5
6
7
8
9
10
11
删除请求 ──► webhook 检查
│
├─ 池中仍有 IP 被 Pod 占用 ──► 拒绝删除
│
└─ 允许 ──► 池进入「删除中」状态
│ 此时:停止分配,但允许释放
▼
所有 IP 释放完毕
│
▼
controller 移除 finalizer ──► 真正删除

SpiderEndpoint 的生命周期: Pod 拿到 IP 后,Spiderpool 同步创建 SpiderEndpoint 记录分配详情;Pod 删除时,controller 依据这条记录释放 IP,再清理对象、移除 finalizer。

四类 GC 触发场景(Pod informer + 定时扫描双保险):

场景 说明
Pod 被删除 StatefulSet 重启场景除外(要保持固定 IP)
Pod 处于 Terminating/Succeeded/Failed 且超过 DeletionGracePeriodSeconds + AdditionalGraceDelay
IPPool 记录的 Pod 在 K8s 中已不存在 兜底扫描,处理 informer 漏掉的事件
IPPool 记录的 Pod UID 与实际不匹配 同名 Pod 被重建,老记录是垃圾

第四条特别重要:Pod 同名重建时 UID 会变,只比名字会误判,比 UID 才能识别出这是一个新 Pod、老 IP 记录该回收了。

GC 相关环境变量:

1
2
3
SPIDERPOOL_GC_IP_ENABLED=true                  # GC 总开关,默认开
SPIDERPOOL_GC_ADDITIONAL_GRACE_DELAY=5 # 额外宽限期(秒),默认 5
SPIDERPOOL_GC_TERMINATING_POD_IP_ENABLED=true # 是否回收 Terminating Pod 的 IP

五、其他能力

5.1 eBPF 加速

基于 eBPF 的 kube-proxy replacement 加速 Service 访问,同节点 Pod 之间用 socket 短路。官方给出的数字:相比 kube-proxy,延迟最多改善 25%,吞吐提升 50%。

5.2 RDMA

Macvlan、ipvlan、SR-IOV 都是承载 RDMA 的重要技术,两种典型组合:

场景 方案
RoCE RDMA shared device plugin + Macvlan,把 master 接口的 RDMA 设备共享给容器
InfiniBand SR-IOV,每个容器独占一个 VF

配合 gpu-operator 还能启用 GPUDirect RDMA 和 gdrcopy,让 GPU 和网卡直接对话,省掉主机内存那一跳。

5.3 ifacer:VLAN/Bond 自动化

ifacer 在 Pod 创建时按配置动态创建 VLAN 子接口或 Bond 接口,作为 Macvlan/ipvlan 的父接口。省掉的是"在每台主机上手动配 VLAN"这个运维负担——几十上百台机器的集群里,这个价值不小。

5.4 双栈

所有组件和功能都支持 IPv4-only、IPv6-only、dual-stack 三种模式。


六、实战部署

6.1 安装

1
2
3
4
5
6
7
8
9
10
helm repo add spiderpool https://spidernet-io.github.io/spiderpool
helm repo update

helm install spiderpool spiderpool/spiderpool \
--namespace kube-system \
--set feature.enableIPv4=true \
--set feature.enableIPv6=false \
--set clusterDefaultPool.installIPv4IPPool=true \
--set clusterDefaultPool.ipv4Subnet="172.18.40.0/24" \
--set clusterDefaultPool.ipv4IPRanges={"172.18.40.40-172.18.40.200"}

AI 场景要同时装 SR-IOV 组件:

1
2
helm install spiderpool spiderpool/spiderpool \
-n spiderpool --set sriov.install=true

6.2 定义网卡:SpiderMultusConfig

给宿主机的两张物理网卡各建一套配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderMultusConfig
metadata:
name: macvlan-ens192
namespace: kube-system
spec:
cniType: macvlan
macvlan:
master:
- ens192
vlanID: 100
ippools:
ipv4:
- ippool-ens192-v4
---
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderMultusConfig
metadata:
name: macvlan-ens224
namespace: kube-system
spec:
cniType: macvlan
macvlan:
master:
- ens224
vlanID: 200
ippools:
ipv4:
- ippool-ens224-v4

6.3 定义 IP 池

两张网卡接的是不同子网:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderIPPool
metadata:
name: ippool-ens192-v4
spec:
ipVersion: 4
subnet: 172.18.41.0/24
ips:
- 172.18.41.10-172.18.41.100
gateway: 172.18.41.1
---
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderIPPool
metadata:
name: ippool-ens224-v4
spec:
ipVersion: 4
subnet: 172.18.42.0/24
ips:
- 172.18.42.10-172.18.42.100
gateway: 172.18.42.1

6.4 部署多网卡 Pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apps/v1
kind: Deployment
metadata:
name: multi-nic-app
spec:
replicas: 1
selector:
matchLabels:
app: multi-nic-app
template:
metadata:
annotations:
k8s.v1.cni.cncf.io/default-network: kube-system/macvlan-ens192
k8s.v1.cni.cncf.io/networks: kube-system/macvlan-ens224
ipam.spidernet.io/ippools: |-
[
{"interface": "eth0", "ipv4": ["ippool-ens192-v4"]},
{"interface": "net1", "ipv4": ["ippool-ens224-v4"]}
]
labels:
app: multi-nic-app
spec:
containers:
- name: app
image: busybox
command: ["sleep", "3600"]

结果:eth0 从 172.18.41.0/24 拿 IP,net1 从 172.18.42.0/24 拿 IP,coordinator 自动把两张网卡的策略路由配好。

6.5 AI 训练场景:SR-IOV + RDMA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderMultusConfig
metadata:
name: sriov-match-master-subnet
namespace: kube-system
spec:
cniType: sriov
sriov:
resourceName: "spidernet.io/sriov_netdevice"
enableRdma: true
ippools:
ipv4:
- rdmarail1-*
coordinator:
mode: "underlay"

rdmarail1-* 是通配符,可以匹配多个不同子网的 IPPool。配合"按 master 接口子网自动匹配"的能力,节点会自动挑中和本地网卡同子网的那个池——多轨(multi-rail)组网里这个能力很关键,否则你得为每个 rail、每个子网手写一套 annotation。


七、典型场景

场景 Spiderpool 提供什么
AI / 分布式存储 RoCE 共享 Macvlan、IB 走 SR-IOV;配合 gpu-operator 启用 GPUDirect RDMA
公有云统一 Underlay 基于 ipvlan 可跑在任意公有云;nodeName/multusName 做拓扑感知
传统固定 IP 应用 StatefulSet 单 Pod 绑定具体 IP;Deployment 固定 IP 范围
KubeVirt 虚拟机 VM 级别(而非 Pod 级别)记录固定 IP
CNI 协同 为 Calico/Weave 补齐固定 IP 能力

几个值得展开的点:

公有云为什么用 ipvlan 而不是 Macvlan。 关键在 MAC 地址——ipvlan 不会重新生成 MAC,所有子接口共用父接口的 MAC。公有云 VPC 通常会校验 MAC 合法性,Macvlan 生成的新 MAC 过不了这一关,ipvlan 则天然规避。

StatefulSet 固定 IP 默认是开的。 Pod 重启/重建后拿回同一个 IP。但公有云场景下如果 Pod 漂到新节点、原 IP 在新节点所在子网不可用,这个特性反而会让 Pod 起不来——这时候建议关掉:

1
--set ipam.enableStatefulSet=false

KubeVirt 的固定 IP 记在 VM 级别,所以 VM 重启、重建、甚至热迁移都能保持 IP 一致。两个限制要知道:passt 模式不支持热迁移;bridge 模式支持多网卡但不支持 Service Mesh。关闭用 --set ipam.enableKubevirtStaticIP=false。

Calico 协同的动机。 Calico 自己的 cni.projectcalico.org/ipAddrs 注解只在 Pod 级生效,无法防止 IP 冲突,大规模管理起来很痛苦。Spiderpool 接管 IPAM 后能补上这块。


八、运维要点

8.1 NetworkManager 干扰 veth

Fedora、CentOS 这类用 NetworkManager 的发行版上,NM 会去"管理" coordinator 创建的 veth 接口,把它们搞挂。加配置让它别管:

1
2
3
# /etc/NetworkManager/conf.d/spidernet.conf
[keyfile]
unmanaged-devices=interface-name:^veth*;interface-name:${IFACER_INTERFACE}

这个坑的表现是间歇性的 Pod 网络异常,排查起来很费劲,装机时顺手配上最省事。

8.2 自定义路由表导致 CIDR 探测失败

coordinator 默认通过查询节点现有路由来自动探测集群 CIDR(podCIDRType: auto)。但在用策略路由分离管理流量的节点上,它可能认错网卡,结果就是 Pod 访问不了 Service。

解法是显式指定,别让它猜:

1
2
3
4
5
6
apiVersion: spiderpool.spidernet.io/v2beta1
kind: SpiderCoordinator
metadata:
name: default
spec:
podCIDRType: cluster # 或 calico / cilium,按实际 CNI 选

8.3 防火墙

Underlay 下 Pod IP 直接暴露在物理网络上,防火墙需要放行 IPPool 的 IP 范围。配合 excludeIPs 精确排除不可用 IP(比如网段里已经被物理设备占用的地址)。

8.4 配置总览

spiderpool-conf ConfigMap 里几个开关:

配置项 作用
enableIPv4 / enableIPv6 双栈开关
enableStatefulSet StatefulSet 固定 IP
enableKubevirtStaticIP KubeVirt 固定 IP
enableSpiderSubnet 自动池功能
clusterSubnetDefaultFlexibleIPNumber 默认弹性 IP 数

九、小结

Spiderpool 的价值不在于发明了新的数据面技术——它一行数据面代码都没碰。它的价值在于用「IPAM 插件 + coordinator + ifacer + Multus 封装」这套组合,把 Macvlan、ipvlan、SR-IOV 这些性能好但难管理的原生 CNI,变成了运维团队能接得住的生产级方案。

三个角度看它的设计:

  • 架构上:标准 controller-agent,CRD 抽象出 IP 池 / 子网 / 网络配置,声明式管理;
  • 能力上:多网卡 IP 分配、策略路由协调、自动池、四类 GC——每一条都直击 Underlay 的具体痛点,不是泛泛的"企业级特性";
  • 生态上:与 Multus、SR-IOV Operator、RDMA device plugin 紧密协作,构成相对完整的 Underlay 栈。

如果你要在 K8s 上承载高性能网络负载(AI 训练、分布式存储、低时延中间件),或者需要保留传统的固定 IP 管理模式,Spiderpool 是目前开源社区里少有的成熟选择。

最后重申开头那个坑,因为它真的很容易中招:

podDefaultRouteNIC(CRD)≠ podDefaultRouteNic(CNI conf)。
同一个概念,两种序列化上下文,两套大小写。填错了不报错,只是静默失效。


延伸资源