动手:给 Pod 插第二张网卡
AI 训练和虚拟机场景都需要 Pod 直连物理网。Multus、Spiderpool、MacVLAN 的实际配法。
学完这节你能做到
- 用注解给 Pod 申请次级网卡与固定 IP 段
- 解释 MacVLAN、IPvlan、SR-IOV 三种接入方式的差异
- 判断什么场景必须 hostNetwork、什么场景该用 MacVLAN
什么时候需要第二张网卡
默认 CNI 给的那张 eth0 走软件转发路径,对业务足够。但有两类场景绕不过去:
| 场景 | 为什么默认 CNI 不行 |
|---|---|
| AI 训练 / 高性能存储 | 要用网卡的 RDMA 能力,软件转发路径帮不上忙(L2 详解) |
| 虚拟机(KubeVirt) | 虚机需要二层语义:自己的 MAC、能收发任意帧、能跑 DHCP |
做法都是给 Pod 插第二张网卡:业务流量走 eth0(默认 CNI),
高性能或二层流量走 net1(次级 CNI)。
Multus:多网卡的调度者
Multus 本身不实现网络,它是个 meta-plugin:kubelet 调它,它再按 Pod 注解去调真正的插件。
kubelet → Multus → ① 默认 CNI(Cilium)→ eth0
→ ② 按注解调 MacVLAN/SR-IOV → net1
配置载体是 NetworkAttachmentDefinition(NAD),Pod 用注解引用它:
metadata:
annotations:
k8s.v1.cni.cncf.io/networks: network-operator/hca
kubectl get network-attachment-definitions -A
# 验证生效:Pod 里应该能看到两张网卡
kubectl exec -it mypod -- ip -brief addr
lo UNKNOWN 127.0.0.1/8
eth0 UP 10.244.1.7/32 ← 默认 CNI
net1 UP 192.168.0.9/20 ← 次级网卡
Pod 的 status 里也会记录:
kubectl get pod mypod -o jsonpath='{.metadata.annotations.k8s\.v1\.cni\.cncf\.io/network-status}' | jq
实验:MacVLAN + nv-ipam 给 Pod 一张直连网卡
这是 RoCE 场景最常用的组合。前提是节点已装好 network-operator (L2 的 K8s RDMA 那节 讲部署)。
第一步:定义网络与 IP 池
apiVersion: mellanox.com/v1alpha1
kind: MacvlanNetwork
metadata:
name: hca
namespace: network-operator
spec:
networkNamespace: "default" # 允许哪个命名空间的 Pod 用
master: "bond0" # 从哪张物理卡派生
mode: "bridge"
mtu: 1500
ipam: |
{
"type": "nv-ipam",
"poolName": "hca"
}
---
apiVersion: nv-ipam.nvidia.com/v1alpha1
kind: IPPool
metadata:
name: hca
namespace: network-operator
spec:
subnet: 192.168.0.0/20
perNodeBlockSize: 8 # 每节点先领 8 个地址
gateway: 192.168.0.1
kubectl apply -f macvlan-hca.yaml
kubectl get ippool -n network-operator
kubectl get macvlannetwork -n network-operator
第二步:起一个带次级网卡的 Pod
apiVersion: v1
kind: Pod
metadata:
name: rdma-test
annotations:
k8s.v1.cni.cncf.io/networks: network-operator/hca
spec:
containers:
- name: app
image: mellanox/rping-test
command: ["sleep", "infinity"]
resources:
limits:
rdma/hca: 1 # 申请 RDMA 设备
securityContext:
capabilities:
add: ["IPC_LOCK"] # 注册内存要锁内存
第三步:验证
# 两张网卡都在
kubectl exec -it rdma-test -- ip -brief addr
# RDMA 设备可见
kubectl exec -it rdma-test -- ibv_devinfo | grep -E 'hca_id|state'
kubectl exec -it rdma-test -- ibdev2netdev
# GID 表里有 RoCEv2 条目(L2 会讲怎么用它)
kubectl exec -it rdma-test -- show_gids | grep v2
每个节点从池子里领一整块地址(例子里 8 个)。含义是:
- 单节点上用这张网的 Pod 最多 8 个,第 9 个分不到 IP,卡在
ContainerCreating - 整个池子容量 ÷ 块大小 = 能有多少个节点
和 Pod CIDR 一样,这是一次性决定、事后难改的参数 (K8s 网络模型 那节讲过同类问题)。 按未来的并发任务数算,别按当前用量算。
排查线索:kubectl describe pod 的 Events 里会写 IPAM 分配失败。
三种接入方式怎么选
L4 的 SR-IOV / MacVLAN / IPvlan 那节讲了机制差异, 这里只给选择结论:
| 方式 | 选它的理由 | 别选它的理由 |
|---|---|---|
| MacVLAN | 配置最简单,性能够用 | 交换机限制 MAC 数量时不行 |
| IPvlan | 交换机开了端口安全、只允许一个 MAC | 失去独立二层身份 |
| SR-IOV | 要极致性能、要硬件隔离 | VF 数量硬件写死、调整要重置网卡 |
hostNetwork 还是次级网卡
RDMA 场景下这是个真实的取舍,不是理论问题:
hostNetwork: true | MacVLAN 次级网卡 | |
|---|---|---|
| 额外组件 | 不需要 | 需要 Multus/Spiderpool + IP 池 |
| Pod IP | 就是宿主机 IP | 独立 IP |
| 同节点跑第二个任务 | ❌ 端口冲突 | ✅ 可以 |
| 配置复杂度 | 低 | 中 |
| 适合 | 单任务独占整机 | 共享集群、多任务并行 |
hostNetwork: true 让 Pod 直接用宿主机端口空间。NCCL、MPI 的 sshd 都要监听固定端口,
同节点起第二个任务时端口直接撞车,症状是第二个 Pod 一直 CrashLoopBackOff。
这就是共享 GPU 集群要上 MacVLAN 的实际理由:给每个任务一套独立的 IP 与端口空间。 独占整机跑单个大任务时,hostNetwork 更省事。
虚拟机场景:Kube-OVN 作次级 CNI
KubeVirt 里的虚机需要的不是高性能,是二层语义:自己的 MAC、能跑 DHCP、 能做 VLAN、最好还能保留 IP 迁移。Kube-OVN 基于 OVN/OVS,天生提供这些。
典型用法是默认 CNI 保持 Cilium(给容器),Kube-OVN 作为次级 CNI(给虚机):
kubectl get subnets.kubeovn.io
kubectl get vlans.kubeovn.io
kubectl get ips.kubeovn.io | head # 固定 IP 记录
要点是职责分离:容器网络的性能与策略交给 eBPF 数据平面,虚机的二层需求交给 OVN, 不要试图用一个 CNI 满足两类完全不同的语义。
排障清单
# 1. NAD 存在且命名空间对得上
kubectl get net-attach-def -A
# 2. Pod 注解写对了(namespace/name 格式)
kubectl get pod mypod -o jsonpath='{.metadata.annotations}' | jq
# 3. 网卡真的插上了
kubectl exec -it mypod -- ip -brief addr
# 4. IPAM 有没有分到地址(Events 里最直接)
kubectl describe pod mypod | tail -20
# 5. 节点上的 Multus 与设备插件是否健康
kubectl get pods -n network-operator -o wide
kubectl describe node <node> | grep -A5 'Allocatable' # 看 rdma/hca 数量
第 5 步里 Allocatable 没有 rdma/hca 这项,说明设备插件没生效——
那是 L2 那节要解决的问题,不是 Multus 的问题。
检查点
Pod 加了次级网卡注解,但一直 ContainerCreating。describe 显示 IPAM 分配失败。最可能的原因?
共享 GPU 集群,要在同一节点上同时跑两个 RDMA 训练任务。网络该怎么配?
集群里既有容器业务也有 KubeVirt 虚机。比较合理的网络方案是?
这节课的落点
- 只有两类场景需要第二张网卡:RDMA/高性能 与 虚拟机的二层语义
- Multus 是 meta-plugin:按 Pod 注解去调真正的插件,配置载体是 NAD
- 验证插上了没有:
ip -brief addr应该看到eth0+net1 - MacVLAN + nv-ipam 是 RoCE 场景的常用组合;Pod 侧还要
rdma/hca与IPC_LOCK perNodeBlockSize决定单节点能跑几个这种 Pod,与 Pod CIDR 同属一次性决定- MacVLAN / IPvlan / SR-IOV 的选择由交换机限制与性能要求决定
- hostNetwork 简单但同节点跑不了第二个任务;共享集群用 MacVLAN 换独立端口空间
- 虚机场景用 Kube-OVN 作次级 CNI,职责分离而不是让一个 CNI 兼顾两种语义
Allocatable里没有rdma/hca是设备插件的问题,不是 Multus 的问题
延伸资料
- k8s-in-action
network/network-operator/macvlan/README.md - k8s-in-action
network/spiderpool/ - k8s-in-action
network/kube-ovn/SKILL.md