cailico 迁移到 cilium: cilium pod 基于目的 ip 判定走了 calico 的 ipip 隧道方向路由

40 阅读13分钟

首先环境



root@k8s-work2:~# kgp | grep python
default       calico-python-daemon-8p6zt                 1/1     Running   0          4h54m   10.222.219.6     k8s-work1   <none>           <none>
default       calico-python-daemon-b5xgr                 1/1     Running   0          4h54m   10.222.160.6     k8s-work2   <none>           <none>
default       calico-python1-daemon-74hcx                1/1     Running   0          4h54m   10.222.160.1     k8s-work2   <none>           <none>
default       calico-python1-daemon-9wbq6                1/1     Running   0          4h54m   10.222.219.1     k8s-work1   <none>           <none>
default       calico-python2-daemon-8v689                1/1     Running   0          4h54m   10.222.160.2     k8s-work2   <none>           <none>
default       calico-python2-daemon-mdn45                1/1     Running   0          4h54m   10.222.219.2     k8s-work1   <none>           <none>
default       calico-python3-daemon-2jlc5                1/1     Running   0          4h54m   10.222.160.3     k8s-work2   <none>           <none>
default       calico-python3-daemon-mlc68                1/1     Running   0          4h54m   10.222.219.3     k8s-work1   <none>           <none>
default       calico-python4-daemon-fl6kn                1/1     Running   0          4h54m   10.222.219.4     k8s-work1   <none>           <none>
default       calico-python4-daemon-w6nhd                1/1     Running   0          4h54m   10.222.160.4     k8s-work2   <none>           <none>
default       calico-python5-daemon-h79cp                1/1     Running   0          4h54m   10.222.219.5     k8s-work1   <none>           <none>
default       calico-python5-daemon-vqx6z                1/1     Running   0          4h54m   10.222.160.5     k8s-work2   <none>           <none>
default       cilium-python-daemon-4x7zw                 1/1     Running   0          3h24m   10.222.2.82      k8s-work2   <none>           <none>
default       cilium-python-daemon-gglvj                 1/1     Running   0          3h24m   10.222.1.239     k8s-work1   <none>           <none>
default       cilium-python-no-check-daemon-g78qf        1/1     Running   0          3h24m   10.222.1.245     k8s-work1   <none>           <none>
default       cilium-python-no-check-daemon-hz6rq        1/1     Running   0          3h24m   10.222.2.181     k8s-work2   <none>           <none>
default       cilium-python1-daemon-k2zkl                1/1     Running   0          3h24m   10.222.1.48      k8s-work1   <none>           <none>
default       cilium-python1-daemon-mbrqv                1/1     Running   0          3h24m   10.222.2.159     k8s-work2   <none>           <none>
root@k8s-work2:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.16.189.2    0.0.0.0         UG    0      0        0 ens160
10.222.0.0      10.222.2.80     255.255.255.0   UG    0      0        0 cilium_host
10.222.0.18     172.16.189.100  255.255.255.255 UGH   0      0        0 tunl0
10.222.1.0      10.222.2.80     255.255.255.0   UG    0      0        0 cilium_host
10.222.1.160    172.16.189.101  255.255.255.255 UGH   0      0        0 tunl0
10.222.2.0      10.222.2.80     255.255.255.0   UG    0      0        0 cilium_host
10.222.2.80     0.0.0.0         255.255.255.255 UH    0      0        0 cilium_host
10.222.12.0     172.16.189.100  255.255.255.0   UG    0      0        0 tunl0
10.222.160.0    0.0.0.0         255.255.255.0   U     0      0        0 *
10.222.160.1    0.0.0.0         255.255.255.255 UH    0      0        0 cali87be4f2366d
10.222.160.2    0.0.0.0         255.255.255.255 UH    0      0        0 cali7354d967ee8
10.222.160.3    0.0.0.0         255.255.255.255 UH    0      0        0 cali7cd4eff55c7
10.222.160.4    0.0.0.0         255.255.255.255 UH    0      0        0 cali1055b94bacf
10.222.160.5    0.0.0.0         255.255.255.255 UH    0      0        0 califdfa0cb4427
10.222.160.6    0.0.0.0         255.255.255.255 UH    0      0        0 cali5a868a043b0
10.222.219.0    172.16.189.101  255.255.255.0   UG    0      0        0 tunl0
172.16.189.0    0.0.0.0         255.255.255.0   U     0      0        0 ens160
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
root@k8s-work2:~#

可以看到我有一批部署 calico 之后就部署的 pod

也有一批 部署 cilium 之后创建的 pod

1. 问题

先说正常的结果:

  • 同节点的(calico 和 cilium) 新旧两批 pod 互相 ping 都是通的
  • 跨节点只能实现 calico 和 calico 的 pod 是通的, cilium 和 cilium 的 pod 是通的

也即是说

  • 跨节点 cilium ping calico 的 pod 是不通的

2. 问题分析

先说结论: calico 的 network policy iptables 策略导致的丢包

2.1 路由


root@k8s-work2:~# route -n | grep tunl0
10.222.0.18     172.16.189.100  255.255.255.255 UGH   0      0        0 tunl0
10.222.1.160    172.16.189.101  255.255.255.255 UGH   0      0        0 tunl0
10.222.12.0     172.16.189.100  255.255.255.0   UG    0      0        0 tunl0 # 
10.222.219.0    172.16.189.101  255.255.255.0   UG    0      0        0 tunl0
root@k8s-work2:~# route -n | grep cilium
10.222.0.0      10.222.2.80     255.255.255.0   UG    0      0        0 cilium_host
10.222.1.0      10.222.2.80     255.255.255.0   UG    0      0        0 cilium_host
10.222.2.0      10.222.2.80     255.255.255.0   UG    0      0        0 cilium_host
10.222.2.80     0.0.0.0         255.255.255.255 UH    0      0        0 cilium_host
root@k8s-work2:~#

cilium 和 calico 的路由思路是一致的,基于目的路由判断,cilium 发出的包就会经过 tunl0

2.2 iptables

拿一个接口的 iptables 规则来看



root@k8s-work2:~# route -n | grep cali7cd4eff55c7
10.222.160.3    0.0.0.0         255.255.255.255 UH    0      0        0 cali7cd4eff55c7

root@k8s-work2:~# iptables-save | grep cali7cd4eff55c7
:cali-fw-cali7cd4eff55c7 - [0:0]
:cali-sm-cali7cd4eff55c7 - [0:0]
:cali-tw-cali7cd4eff55c7 - [0:0]
-A cali-from-endpoint-mark -m comment --comment "cali:-XK1s_VTErdsCJtI" -m mark --mark 0xd9b00000/0xfff00000 -g cali-fw-cali7cd4eff55c7
-A cali-from-wl-dispatch-7 -i cali7cd4eff55c7 -m comment --comment "cali:st9dOv2F3h7sA4M5" -g cali-fw-cali7cd4eff55c7
-A cali-fw-cali7cd4eff55c7 -m comment --comment "cali:VQN-OFZyerQtZBEn" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A cali-fw-cali7cd4eff55c7 -m comment --comment "cali:u292TzXDexF2Ict6" -m conntrack --ctstate INVALID -j DROP
-A cali-fw-cali7cd4eff55c7 -m comment --comment "cali:YwYmsk0jfYuWiajI" -j MARK --set-xmark 0x0/0x10000
-A cali-fw-cali7cd4eff55c7 -p udp -m comment --comment "cali:HWlfQ2zqu9wCTQ-Z" -m comment --comment "Drop VXLAN encapped packets originating in workloads" -m multiport --dports 4789 -j DROP
-A cali-fw-cali7cd4eff55c7 -p ipencap -m comment --comment "cali:9NvRbBeNCLKHDxin" -m comment --comment "Drop IPinIP encapped packets originating in workloads" -j DROP
-A cali-fw-cali7cd4eff55c7 -m comment --comment "cali:ebjVwiHeMZvKiKFY" -j cali-pro-kns.default
-A cali-fw-cali7cd4eff55c7 -m comment --comment "cali:GAIptoj1ZMCwh18V" -m comment --comment "Return if profile accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-A cali-fw-cali7cd4eff55c7 -m comment --comment "cali:3YT1psFK5wku9bce" -j cali-pro-ksa.default.default
-A cali-fw-cali7cd4eff55c7 -m comment --comment "cali:zcLRYSQ3ImT0wOP0" -m comment --comment "Return if profile accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-A cali-fw-cali7cd4eff55c7 -m comment --comment "cali:opk4g-ppHGHwfQcq" -m comment --comment "Drop if no profiles matched" -j DROP
-A cali-set-endpoint-mark-7 -i cali7cd4eff55c7 -m comment --comment "cali:Odss9VHU85V30TJC" -g cali-sm-cali7cd4eff55c7
-A cali-sm-cali7cd4eff55c7 -m comment --comment "cali:Q8C_0b-CTy2I_fHR" -j MARK --set-xmark 0xd9b00000/0xfff00000
-A cali-to-wl-dispatch-7 -o cali7cd4eff55c7 -m comment --comment "cali:QI5N3ve2MqWvQZog" -g cali-tw-cali7cd4eff55c7
-A cali-tw-cali7cd4eff55c7 -m comment --comment "cali:GUg67y3reVUdAvdo" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A cali-tw-cali7cd4eff55c7 -m comment --comment "cali:kZieBSTYFnhhzV85" -m conntrack --ctstate INVALID -j DROP
-A cali-tw-cali7cd4eff55c7 -m comment --comment "cali:O9nqOgxsBvDWpwMN" -j MARK --set-xmark 0x0/0x10000
-A cali-tw-cali7cd4eff55c7 -m comment --comment "cali:sSPf_ATXC8dLcV7c" -j cali-pri-kns.default
-A cali-tw-cali7cd4eff55c7 -m comment --comment "cali:QK9pHStGx3z7st31" -m comment --comment "Return if profile accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-A cali-tw-cali7cd4eff55c7 -m comment --comment "cali:7TKz5idGlNYQQgn5" -j cali-pri-ksa.default.default
-A cali-tw-cali7cd4eff55c7 -m comment --comment "cali:Bd-ZvIsH71_qbiYC" -m comment --comment "Return if profile accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-A cali-tw-cali7cd4eff55c7 -m comment --comment "cali:NY1yjTePbQ1ldQ-J" -m comment --comment "Drop if no profiles matched" -j DROP
root@k8s-work2:~# ip a | grep cali7cd4eff55c7
237: cali7cd4eff55c7@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default qlen 1000
root@k8s-work2:~#

root@k8s-work2:~# iptables-save  | grep IPIP
-A cali-INPUT -p ipencap -m comment --comment "cali:PajejrV4aFdkZojI" -m comment --comment "Allow IPIP packets from Calico hosts" -m set --match-set cali40all-hosts-net src -m addrtype --dst-type LOCAL -j ACCEPT
-A cali-INPUT -p ipencap -m comment --comment "cali:_wjq-Yrma8Ly1Svo" -m comment --comment "Drop IPIP packets from non-Calico hosts" -j DROP
-A cali-OUTPUT -p ipencap -m comment --comment "cali:AJBkLho_0Qd8LNr3" -m comment --comment "Allow IPIP packets to other Calico hosts" -m set --match-set cali40all-hosts-net dst -m addrtype --src-type LOCAL -j ACCEPT

突然 感觉 iptables 的数量真的很多,比 kube-ovn 要多的多,主要包括:

  1. cali-from-endpoint-mark 只有 calico 出来,才会 mark 0xd9b00000/0xfff00000
  2. cali-fw-cali7cd4eff55c7 ,然后是防火墙规则,比如 drop vxlan, 但我们 cilium 使用的 geneve,
  3. 经过 fw 之后会统一打上 0x10000/0x10000 标签

也不是这里丢的包

跨节点的 pod 通信,最终肯定会经过 IPIP,封包后会经过这三条规则。

不是这里丢的包

root@k8s-work2:~# iptables-save  | grep IPIP
-A cali-INPUT -p ipencap -m comment --comment "cali:PajejrV4aFdkZojI" -m comment --comment "Allow IPIP packets from Calico hosts" -m set --match-set cali40all-hosts-net src -m addrtype --dst-type LOCAL -j ACCEPT
-A cali-INPUT -p ipencap -m comment --comment "cali:_wjq-Yrma8Ly1Svo" -m comment --comment "Drop IPIP packets from non-Calico hosts" -j DROP
-A cali-OUTPUT -p ipencap -m comment --comment "cali:AJBkLho_0Qd8LNr3" -m comment --comment "Allow IPIP packets to other Calico hosts" -m set --match-set cali40all-hosts-net dst -m addrtype --src-type LOCAL -j ACCEPT

丢包的位置,我认为是这里


root@k8s-work2:~# iptables-save | grep profile | grep cali7cd4eff55c7
-A cali-fw-cali7cd4eff55c7 -m comment --comment "cali:GAIptoj1ZMCwh18V" -m comment --comment "Return if profile accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-A cali-fw-cali7cd4eff55c7 -m comment --comment "cali:zcLRYSQ3ImT0wOP0" -m comment --comment "Return if profile accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-A cali-fw-cali7cd4eff55c7 -m comment --comment "cali:opk4g-ppHGHwfQcq" -m comment --comment "Drop if no profiles matched" -j DROP
-A cali-tw-cali7cd4eff55c7 -m comment --comment "cali:QK9pHStGx3z7st31" -m comment --comment "Return if profile accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-A cali-tw-cali7cd4eff55c7 -m comment --comment "cali:Bd-ZvIsH71_qbiYC" -m comment --comment "Return if profile accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-A cali-tw-cali7cd4eff55c7 -m comment --comment "cali:NY1yjTePbQ1ldQ-J" -m comment --comment "Drop if no profiles matched" -j DROP
root@k8s-work2:~#

这里的 profile 到底是什么东西?

3. calico 的 profile

你注意到 cali-fw-*cali-tw-* 链中出现的 "Return if profile accepted""Drop if no profiles matched" 这些注释,确实是理解 Calico 网络策略(policy)和 iptables 实现逻辑的核心。

下面我会详细讲清楚 profile 的来源、生成机制、在 iptables 中的作用、与 NetworkPolicy 的关系、以及为什么有 “Return if profile accepted / Drop if no profiles matched” 这些规则。


🧩 一、什么是 Calico Profile?

在 Calico 中,Profile 是一种策略对象(Policy Object),定义了允许/拒绝哪些流量通过。
它存在的意义类似于:

  • 命名空间 (Namespace)WorkloadEndpoint (Pod) 自动生成的默认策略;
  • 或者是用户显式定义的 NetworkPolicy 最终编译结果的一部分。

换句话说:

Calico profile = NetworkPolicy 编译后的底层实现单元。

二、Profile 的来源(谁创建的)

Calico 的组件 Felix 会自动创建和维护 profile:

来源对应的 Profile 名称创建者说明
Kubernetes Namespacekns.<namespace>Felix 自动创建每个命名空间都会有一个 profile
每个 WorkloadEndpoint(Pod)ksa.<serviceaccount>k8sns.<namespace>Felix 自动生成用于默认 ingress/egress 控制
用户定义的 Calico Policyprofile 内嵌匹配规则由 API server → Felix 编译生成用户 NetworkPolicy 被编译成 profile 链(和 policy 链)

查看命令示例:

calicoctl get profile

输出大概会有:

NAME
kns.default
kns.kube-system
kns.monitoring

🧱 三、Profile 作用机制(在 iptables 中体现)

每个 Profile 都会对应两类链:

方向链名模式功能
ingress(进入工作负载)cali-pri-<profile>检查入站包是否被 profile 允许
egress(从工作负载发出)cali-pro-<profile>检查出站包是否被 profile 允许

然后,在工作负载 veth 设备对应的主机端链中(如 cali-fw-caliXXXXcali-tw-caliXXXX),会依次调用这些 profile 链


🧩 四、你看到的这些规则的结构

以你的例子:

-A cali-fw-cali7cd4eff55c7 -m comment --comment "Return if profile accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-A cali-fw-cali7cd4eff55c7 -m comment --comment "Drop if no profiles matched" -j DROP

结构逻辑是:

  1. cali-fw-cali7cd4eff55c7
    这是 workload veth 设备对应的 forward 链,名字对应于 host 端 veth(如 cali7cd4eff55c7)。

  2. Felix 在生成 iptables 时,会这样展开:

    cali-fw-cali7cd4eff55c7
      ↓
      调用与该 workload 关联的 profile 链(cali-pri-XXX, cali-pro-XXX)
      ↓
      如果任何一个 profile 规则允许包通过,会设置 mark 0x10000
      ↓
      “Return if profile accepted” 规则检查 mark == 0x10000,则 RETURN
      ↓
      如果没有任何 profile 匹配(即 mark 没被置位),执行 “Drop if no profiles matched”
    
  3. cali-tw-cali7cd4eff55c7 同理,只是针对 转发/反向方向


⚙️ 五、Profile 是如何与 Pod 绑定的

Felix 在启动时会监听 Kubernetes API,对每个 Pod 生成一个 WorkloadEndpoint 对象,内容大致包括:

apiVersion: projectcalico.org/v3
kind: WorkloadEndpoint
metadata:
  name: node1-k8s-podname-eth0
  namespace: default
spec:
  orchestrator: k8s
  node: node1
  workload: podname
  interfaceName: cali7cd4eff55c7
  profiles:
  - kns.default
  - ksa.default
  - your-custom-profile

上面的 profiles: 字段指明了这个 Pod 使用哪些 profile。
Felix 根据这些 profile 自动生成并链接到 cali-fw-cali7cd4eff55c7 链。


📊 六、一个完整包流过程(结合 profile)

PodA -> PodB(同节点)为例:

  1. 包从 PodA 出发 → 进入主机端 veth → 命中 cali-tw-caliXXXX 链;

  2. 调用该 workload 对应的 egress profile 链 (cali-pro-kns.default);

    • 若允许,则 mark 0x10000;
    • 若不允许,则继续;
  3. “Return if profile accepted” 检查该 mark,如果已设置 → RETURN;

  4. 如果所有 profile 都没置位 mark → “Drop if no profiles matched” → 丢弃。

同理,进入 PodB 时的入站包走 cali-fw-caliYYYY → ingress profiles (cali-pri-kns.default)。


🧩 七、为什么叫 “profile accepted”

Calico 的实现逻辑:

  • Profile 中的“allow”规则会执行:

    -j MARK --set-xmark 0x10000/0x10000
    -j RETURN
    
  • 所以被 profile 允许的包就会带上标志 0x10000;

  • 上层链看到该标志后,就直接 RETURN

  • 如果没有被任何 profile 允许,mark 仍为 0,最终被“Drop if no profiles matched”丢弃。


🧠 八、与 NetworkPolicy 的关系

Calico 对象对应 k8s 概念生效范围关系
Profile (kns.<ns>)Namespace 的默认策略命名空间基础安全边界
Profile (ksa.<sa>)ServiceAccountPod 所属身份精细化
NetworkPolicyKubernetes NetworkPolicy可自定义匹配编译后成为若干 profile/policy 链并链接到工作负载链中

所以在实际运行中:

Profile 是默认存在的策略层(namespace + SA),
NetworkPolicy 则是用户定义的附加规则层,二者共同决定允许/拒绝。


🧩 九、实际可验证命令

你可以直接看某个 Pod 对应的 profiles:

# 查 workloadendpoint
calicoctl get workloadendpoint -o yaml | grep -A5 profiles

输出示例:

profiles:
- kns.default
- ksa.default

查看 profile 内容:

calicoctl get profile kns.default -o yaml

会看到类似:

spec:
  ingress:
  - action: Allow
    source:
      selector: all()
  egress:
  - action: Allow
    destination:
      selector: all()

这些内容最终编译成 iptables 链:

cali-pri-kns.default
cali-pro-kns.default

并嵌入到 workload 的 fw/tw 链中。


✅ 十、总结一句话

Profile 是 Calico 自动生成或由策略编译得到的安全策略单元,用于定义 Pod(或命名空间)的默认 ingress/egress 行为。
每个 Pod 的 veth 对应链(如 cali-fw-cali7cd4eff55c7)会调用这些 profile 链来检查是否允许流量。
“Return if profile accepted” 意味着某个 profile 已经允许该包(标志 0x10000 已置位),
“Drop if no profiles matched” 则是防止没有任何 profile 允许的包被放行。