文章

【笔记】阿里云 ENI Trunking 的 Pod 数据路径

【笔记】阿里云 ENI Trunking 的 Pod 数据路径

ENI Trunking 解决的问题不是“怎样给 Pod 再分一个 IP”,而是“怎样让一台节点有限的物理网卡承载更多具有独立云网络身份的 Pod”。VLAN、Linux datapath 和策略路由都是为了把 Pod 流量正确映射回对应 Member ENI。

本文依据 Terway 官方文档与 2026-08-01 GitHub main 源码快照。Terway 支持多种 datapath,内核对象和规则会随配置与版本变化;文中的 tc/VLAN link 细节不能外推为所有 ACK 集群的固定形态。

1. 中心模型:云上身份与节点内转发分层

flowchart LR
    P[Pod network namespace] --> D[节点内 datapath]
    D --> T[Trunk ENI]
    T --> H[云虚拟化网络]
    H --> M[Member ENI 身份]
    M --> V[VPC vSwitch / 路由 / 安全组]

每层回答不同问题:

责任
Pod使用自己的 IP 和默认路由收发包
Terway datapath在 Pod、节点路由和承载网卡之间转发
Trunk ENI在一条节点侧 ENI 通道上复用多个 Member ENI
云虚拟化网络按 Member ENI/VLAN 关联恢复云上网络身份
VPC执行路由、安全组和交换

关键不是包最终从哪张 Linux 设备发出,而是离开节点时携带的信息能否让云侧识别“它属于哪个 Member ENI”。

2. Trunk ENI 与 Member ENI

2.1 Trunk ENI 是承载通道

Trunk ENI 附着到 ECS 节点,Guest OS 能看到对应网络设备。它承担多个 Pod 网络身份的聚合流量,而不是给所有 Pod 提供同一个不可区分的身份。

2.2 Member ENI 是 Pod 的云侧身份

阿里云和 Terway 文档中常见 Member ENI,也可能在讨论中被称为 Branch ENI。它记录独立的 ENI ID、MAC、IP、vSwitch 与安全组配置,并关联到某个 Trunk ENI。

PodENI CR 会记录 Pod 使用的 ENI allocation,以及当前绑定的 ECS instance 和 trunk ENI ID。它是控制面期望与状态记录,不是 Linux network namespace 内的一张同名实体设备。

2.3 VLAN ID 是复用标签

云侧把一个 Member ENI 绑定到 Trunk ENI 时,会分配 VLAN ID。Trunk 链路上的 802.1Q tag 让同一承载通道中的帧可归属到不同 Member ENI。

1
2
Pod A → VLAN 101 → Member ENI A
Pod B → VLAN 205 → Member ENI B

VLAN ID 是链路内映射键,不是 Pod IP,也不是安全组 ID。它的意义来自控制面建立的 trunk + VLAN ↔ member ENI 关系。

3. 为什么只看 Pod IP 不够

IP 能帮助节点和 VPC 做三层路由,但 ENI 身份还关联 MAC、vSwitch、安全组和生命周期。若一条节点链路上只有源 IP,没有与 Member ENI 的明确绑定,云侧就难以按独立 ENI 身份执行这些能力。

Trunking 把两个维度分开:

  • Pod IP 表示三层端点;
  • VLAN/Member ENI 关联表示流量所属的云网络接口。

同一个 Pod packet 在节点内可能以普通 IP 包被路由,在 Trunk 边界才加上或保留 VLAN 语义。

4. 控制面如何把 Pod 映射到网络身份

4.1 PodNetworking 描述选择规则

用户通过 PodNetworking CR 指定:

  • Pod 与 Namespace selector;
  • 可用 vSwitch;
  • security group IDs;
  • IP allocation type 与释放策略。

控制面根据 selector 为 Pod 选择网络配置。一个 Pod 应避免被多个互相冲突的配置重复匹配。

4.2 PodENI 记录分配与绑定状态

控制面为匹配 Pod 创建或维护 PodENI,其中可看到:

1
2
3
Pod → Member ENI ID / MAC / IP
Member ENI → Trunk ENI ID / ECS instance
Pod → IP allocation lifecycle

节点侧 Terway 等待该资源进入可用绑定状态,再将 allocation 转换成 CNI datapath 配置。

4.3 CNI ADD 落地 Linux 网络

Pod 调度到节点后,Terway CNI 根据 allocation 和 datapath 配置创建接口、地址、路由、规则和 VLAN 处理。控制面决定“该用哪个云身份”,节点面负责“怎样把 Pod 包送到该身份”。

5. 先确认 datapath,不能只背一张拓扑

5.1 Terway 不是只有 veth + tc

当前源码在解析 CNI 配置时会根据 IP type、是否 trunk,以及 vlan_strip_type 等条件选择 datapath,包括 IPVlanVlan、policy route 等路径。

因此以下断言都过于绝对:

  • Pod IP 一定只在 veth 端;
  • Trunking 一定没有 VLAN 子接口;
  • 所有版本都由同一组 tc filter 完成全部转发;
  • 每个 Pod 一定有同样的 policy routing table。

5.2 Filter 路径

当采用 filter 方式处理 VLAN 时,当前源码会:

  • 在 trunk device ingress 上确保 VLAN untagger;
  • 使用 tc U32/filter action 处理 VLAN;
  • 按 IPv4/IPv6 源地址创建 egress VLAN push 规则;
  • 为失效 Pod IP 清理旧 filter。

这条路径适合用一张 trunk device 配合动态 filter 表示多个 Pod 到 VLAN 的映射。

vlan_strip_type 选择 VLAN datapath 时,当前源码会创建 netlink.Vlan 设备,并把具体 VLAN ID 放进 link 配置。此时 Linux 802.1Q 子接口参与 tag 的处理。

所以“tc 与 VLAN 子接口二选一”的判断要放在具体配置和版本下,而不是当作 ENI Trunking 的产品定义。

5.4 IPVlan 与 veth 是另一个维度

Pod 侧可以通过 IPVlan 或其他 virtual interface 连接节点网络。它回答“Pod namespace 怎样接入 host”,VLAN 处理回答“host 怎样在 trunk 链路标识 Member ENI”。两者处在不同层,不应合并成一个开关理解。

6. Filter 路径中的 tc 位置

6.1 Ingress:把 trunk 帧交给节点协议栈

云侧送入节点的 trunk frame 带有对应 Member ENI 的 VLAN tag。filter 路径在 trunk device ingress 执行 pop,让后续节点协议栈按普通 IP packet 路由到 Pod。

1
2
3
4
5
云侧带 tag 帧
  → trunk device ingress
  → tc vlan pop
  → host routing
  → Pod link

当前 EnsureVlanUntagger 会检查/创建 clsact qdisc 和 ingress filter,并使用 VLAN pop action。它是当前 Terway filter datapath 的实现事实。

6.2 Egress:按 Pod 源地址恢复 Member ENI 标签

Pod 流量经节点路由选中 trunk device 后,egress filter 按 source IP 匹配,并执行 VLAN push:

1
2
3
4
5
6
Pod packet
  → host routing
  → trunk device egress
  → src PodIP 对应的 tc U32 rule
  → vlan push <VID>
  → 云侧映射为 Member ENI

当前源码为 IPv4 和 IPv6 使用不同 protocol 与 filter priority,说明 tag rule 是按协议族显式构造的。不能假设一条 IPv4 filter 会自然覆盖 IPv6。

6.3 tc 只处理本层职责

tc action 可以加减 VLAN tag,却不替代:

  • Pod IPAM;
  • 路由选择;
  • Member ENI 控制面绑定;
  • 云侧安全组求值。

看到 trunk 上有 tag 规则,不等于完整数据面已经正确。

7. 路由与策略路由在解决什么

7.1 路由先决定下一跳和出接口

Linux 发送 packet 时先根据 policy rules 和 route tables 选择路径,之后才到对应 netdevice 的 egress tc。故 egress tag 并不能替代“让 Pod 流量走到正确 trunk device”的路由配置。

7.2 源地址规则维持返回路径

多 ENI 或多网关节点里,main table 的默认路由可能指向主 ENI。若 Pod 从 trunk 收到流量,却通过另一个 ENI 返回,会破坏源地址、云身份或反向路径预期。

Terway 会按 ENI index 等信息生成 route table,并为相关 Pod 源地址安装规则,使返回流量选择对应 ENI gateway。

1
2
ip rule:  from <PodIP> lookup <eni-table>
ip route: default via <eni-gateway> dev <trunk-device> table <eni-table>

实际规则还可能包含 Pod host route、IPv6、onlink 和优先级等细节,应以节点现场为准。

7.3 local table 不等于所有 Pod 包都走 INPUT

Linux policy routing 通常先查 local table,用于本机 local、broadcast 等路由。Pod IP 配置在 Pod namespace 或特定虚拟设备后,host 如何判断 local/forward 取决于实际 datapath、路由和 namespace。

不能仅凭“Pod IP 曾出现在节点配置中”断言 packet 必然进入 INPUT;应结合:

1
2
3
4
ip rule show
ip route show table all
ip netns exec <ns> ip addr
ip netns exec <ns> ip route

8. 入方向完整链路

以 filter datapath 的概念链路为例:

sequenceDiagram
    participant V as VPC
    participant H as 云虚拟化网络
    participant T as Trunk device
    participant K as Host routing
    participant P as Pod

    V->>H: 目标为 Member ENI IP
    H->>H: 查 Member ENI 与 VLAN 关联、执行云侧策略
    H->>T: 通过 trunk 注入带 VLAN tag 的帧
    T->>T: ingress tc pop
    T->>K: 普通 IP packet
    K->>P: 按 Pod route 转发

这里至少有三次映射:目标 IP 找 Member ENI、Member ENI 找 trunk/VLAN、节点 route 找 Pod link。任何一层缺失都会表现为 Pod 不通,但排障位置完全不同。

9. 出方向完整链路

sequenceDiagram
    participant P as Pod
    participant K as Host routing
    participant T as Trunk device
    participant H as 云虚拟化网络
    participant V as VPC

    P->>K: src=PodIP 的 packet
    K->>K: ip rule + ENI route table
    K->>T: 选中 trunk device
    T->>T: egress tc 按 src IP push VLAN
    T->>H: 带 VLAN tag 的帧
    H->>H: 还原 Member ENI 身份并执行云侧策略
    H->>V: 进入 VPC 转发

若采用 VLAN link datapath,tag 的具体加减位置会变化,但“Pod packet 必须映射到正确 Member ENI 身份”的不变量不变。

10. 安全组在哪一层生效

10.1 安全组附着到 Member ENI 配置

PodNetworking.spec.securityGroupIDs 参与创建或选择 Pod 对应 Member ENI。它让不同 Pod 可以获得不同的云侧安全组边界。

10.2 Guest 内 tc 不是安全组执行器

tc filter 在这里负责 VLAN 标记,不负责解释阿里云 security group rules。安全组由云平台数据面基于 Member ENI 身份执行。

因此:

  • tag 正确不代表安全组一定 Allow;
  • Linux iptables/eBPF Allow 不代表云安全组一定 Allow;
  • 云安全组 Allow 也不代表 Kubernetes NetworkPolicy 或应用监听一定 Allow。

10.3 NetworkPolicy 是独立控制层

Terway 还可以实现 Kubernetes NetworkPolicy,但它与 ENI security group 的主体、规则模型和执行位置不同。排障时应分别验证 Pod/Node 内策略和云侧 ENI 策略。

11. 现场排障按映射链检查

11.1 控制面

1
2
3
kubectl get podnetworkings.network.alibabacloud.com -o yaml
kubectl get podenis.network.alibabacloud.com -A -o yaml
kubectl get node <node> -o yaml

确认 Pod 命中了哪个配置、Member ENI allocation、trunk ID、binding phase 和 security groups。

11.2 Pod namespace

1
2
3
ip addr
ip route
ip link

确认 Pod IP、接口类型、默认路由和邻居关系。不要先假设一定是 veth 或 IPVlan。

11.3 Node datapath

1
2
3
4
5
6
ip -d link show
ip rule show
ip route show table all
tc qdisc show
tc filter show dev <trunk> ingress
tc filter show dev <trunk> egress

先识别当前 datapath,再看对应对象。若配置走 VLAN link,却只检查 tc egress filter,会得到错误结论。

11.4 云侧

核对 trunk 与 Member ENI 绑定、VLAN ID、vSwitch、IP、security groups 和实例归属。节点内配置看起来正确但云侧绑定未完成时,packet 仍无法以预期身份进入 VPC。

12. 常见误区

12.1 “Branch ENI 会作为独立设备出现在 Guest”

Trunk 模式的价值正是用一条承载通道复用 Member ENI。Guest 中看到的 Linux link 是 Terway datapath 的实现对象,不应简单与云控制面 Member ENI 一一等同。

12.2 “VLAN ID 就是 Pod 网络隔离策略”

VLAN 主要标识 trunk 内逻辑通道。安全组、NetworkPolicy 和路由分别在其他层表达策略。

当前 Terway 同时存在 filter 与 VLAN datapath 分支。必须先看 vlan_strip_type 和现场 link/filter。

12.4 “tc 打 tag 后无需策略路由”

tc egress 只有 packet 已被路由到该 device 才会运行。多 ENI 环境仍需正确的 source-based routing 与返回路径。

12.5 “Pod 通则所有策略都正确”

连接可能只覆盖同节点、同安全组或 IPv4。需要分别验证跨节点、出 VPC、IPv6、NetworkPolicy 和安全组边界。

13. 复习索引

  1. Trunk ENI 是承载通道,Member ENI 是 Pod 对应的云侧网络身份;
  2. VLAN ID 把 trunk frame 映射到 Member ENI,不等于 Pod IP 或安全组;
  3. PodNetworking 选择 vSwitch/安全组/IP 策略,PodENI 记录 allocation 与绑定;
  4. Terway 有多种 datapath,当前既有 tc filter,也有 Linux VLAN link 路径;
  5. 路由决定 packet 去哪张设备,VLAN 处理决定它以哪个 Member ENI 身份离开;
  6. 安全组在云侧按 ENI 身份执行,和节点内 NetworkPolicy 是两层控制;
  7. 排障应沿 Pod → node datapath → trunk/member binding → VPC 逐层验证。

14. 核验入口

  • Terway docs/terway-trunk.md:trunk mode、PodNetworkingPodENI
  • plugin/terway/cni.go:按 IP type、trunk 和 vlan_strip_type 选择 datapath;
  • plugin/driver/utils/utils_linux.go:当前 tc VLAN pop/push 实现;
  • plugin/driver/vlan/vlan.go:Linux VLAN link datapath;
  • daemon/rule_linux.goplugin/datapath:policy route 生成与同步;
  • 阿里云 ACK/Terway 官方文档:实例规格、功能开关、限制和生产配置。
本文由作者按照 CC BY 4.0 进行授权