Cilium Masquerading 故障排查记录
❝本文转自 lx1036 的博客,原文:https://juejin.cn/post/7112768193126465567/,版权归原作者所有。欢迎投稿,投稿请添加微信好友:cloud-native-yang
背景
在版本升级 cilium v1.8.1 到 v1.11.1 时,导致业务 pod 报错连接 mysql 授权错误,经过排查发现连接 mysql server 的 clientIP 是 业务 pod 所在的 nodeIP,而不是默认的 podIP,因为 mysql server 只授权了当前 K8s 集群的 pod cidr,所以报错授权问题。
矛盾点在于使用 cilium v1.8.1 时,出机器时的 IP 还是 podIP,但是 v1.11.1 却是 nodeIP,进一步发现 v1.8.2 版本也是 nodeIP。K8s 网络这块采用的是 Cilium + BGP 模式,podIP 在公司内网可达,所以希望的也是业务 pod 从当前节点出去 IP 应该是 podIP 才对,cilium 估计是做了 SNAT,把 podIP SNAT 成 nodeIP。
原因
原因在于 cilium 默认会做 podIP masq,可以参考官网文档 v1.8 :masquerading[1]
我们部署的 cilium 配置里也配置了 masquerade: true
,实际上 cilium 会默认配置值为 true
:
masquerade: 'true'
enable-bpf-masquerade: 'true'
native-routing-cidr: 10.20.30.0/24
升级 cilium v1.11.1 时我们还是用的以上配置, cilium 新版本这个老配置 masquerade: true
已经废弃,改用 enable-ipv4-masquerade: true
, cilium 默认开启 podIP masquerade,见代码:daemon_main.go#L679-L680[2]
所以升级 cilium v1.11.1 时需要改下配置就解决问题了:
enable-ipv4-masquerade: 'false'
enable-bpf-masquerade: 'false'
ipv4-native-routing-cidr: 10.20.30.0/24 # 新版本废弃 native-routing-cidr 配置,使用该配置,默认也是使用集群 pod cidr,和配置值 cluster-pool-ipv4-cidr 相同
为何 cilium v1.8.1 没有报这个问题?尽管 cilium v1.8.1 我们使用的配置是 masquerade: true
,但是这个版本有个 bug,导致配置了也不起作用,podIP Masq 也不会走对应的 ebpf SNAT 规则, 在版本 v1.8.2 里修复了这个 bug,所以 cilium v1.8.2 之后默认都是开启 podIP Masq,尽管这个不是我们想要的。bug 修复代码见:pull/12456[3]
如果 pod 访问的目标 ip 在 ipv4-native-routing-cidr 网段内,也不会走 podIP Masq,ebpf c 代码里会判断如果在该网段内就跳过不走 masq 逻辑。这样 pod 相互访问不会走 podIP Masq,只有访问集群外网络时才会这样。ebpf c 代码跳过 ipv4-native-routing-cidr 网段逻辑见:
config.go#L505-L518[4] nodeport.h#L1160-L1170[5]
然后,包从容器出来,会经过 node 上 eth0 网卡,该网卡上下发了 ebpf SNAT 逻辑,会把 podIP SNAT 成 nodeIP,SNAT 逻辑代码函数见:
nat.h#L504-L570[6] nat.h#L322-L378[7]
这里难点主要是如何使用 ebpf 代码去做 SNAT, cilium 这块代码值得学习,这里也是 cilium 核心逻辑之一。
最后,cilium 会把该 ebpf c 程序下发到 eth0 网卡 egress 出口侧(默认是 eth0 网卡,可以在 cilium daemon 里配置出口网卡), 可以在然后一台 K8s node 上执行以下命令看到 to-netdev
ebpf 程序,这里 to-netdev
可以理解为这块 ebpf c 程序的名字:
$ tc filter show dev eth0 egress
filter protocol all pref 1 bpf chain 0
filter protocol all pref 1 bpf chain 0 handle 0x1 bpf_netdev_eth0.o:[to-netdev] direct-action not_in_hw tag aed7375159f1f3a4
to-netdev ebpf c 程序可见,这是包从 eth0 网卡 egress 侧出去时走的逻辑:bpf_host.c#L1003-L1103[8]
同理,from-netdev ebpf c 程序是包进入 eth0 网卡 ingress 侧走的逻辑:bpf_host.c#L962-L987[9] , 这里主要是防火墙或者 BPF NodePort 才有用,比如我们这里的 podIP Masq 时 BPF NodePort 是开启的。
iptables snat masquerading
cilium 除了使用 ebpf 来实现 snat masq,也可以使用下发 iptables 规则来实现,可以见代码: iptables.go#L1097-L1137[10]
可以修改 cilium 配置,然后使用命令 iptables -t nat -S CILIUM_POST_nat
查看:
enable-ipv4-masquerade: 'true'
enable-bpf-masquerade: 'false'
ipv4-native-routing-cidr: 10.20.30.0/24 # 新版本废弃 native-routing-cidr 配置,使用该配置,默认也是使用集群 pod cidr,和配置值 cluster-pool-ipv4-cidr 相同
下发的 iptables 规则类似如下:
$ iptables -t nat -S POSTROUTING
-P POSTROUTING ACCEPT
-A POSTROUTING -m comment --comment "cilium-feeder: CILIUM_POST_nat" -j CILIUM_POST_nat
-A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
$ iptables -t nat -S CILIUM_POST_nat
-N CILIUM_POST_nat
-A CILIUM_POST_nat -s 20.30.137.0/25 -m set --match-set cilium_node_set_v4 dst -m comment --comment "exclude traffic to cluster nodes from masquerade" -j ACCEPT
-A CILIUM_POST_nat -s 20.30.137.0/25 ! -d 10.216.136.0/21 ! -o cilium_+ -m comment --comment "cilium masquerade non-cluster" -j MASQUERADE
-A CILIUM_POST_nat -m mark --mark 0xa00/0xe00 -m comment --comment "exclude proxy return traffic from masquerade" -j ACCEPT
-A CILIUM_POST_nat -s 127.0.0.1/32 -o cilium_host -m comment --comment "cilium host->cluster from 127.0.0.1 masquerade" -j SNAT --to-source 20.30.137.116
-A CILIUM_POST_nat -o cilium_host -m mark --mark 0xf00/0xf00 -m conntrack --ctstate DNAT -m comment --comment "hairpin traffic that originated from a local pod" -j SNAT --to-source 20.30.137.116
包走 netfilter POSTROUTING chain 时会首先跳到 CILIUM_POST_nat chain 走完该 chain 的所有 rules,再跳转到 KUBE-POSTROUTING chain 的所有 rules。CILIUM_POST_nat chain 包含的 rules 如上,podIP Masq 的 rule 主要是这条,通过 iptables 很简单就能实现 podIP SNAT 成 nodeIP:
-A CILIUM_POST_nat -s 20.30.137.0/25 ! -d 10.216.136.0/21 ! -o cilium_+ -m comment --comment "cilium masquerade non-cluster" -j MASQUERADE
当然,eBPF 因为会跳过 netfilter,包不必再去拷贝到内核里走 netfilter,性能相比 iptables 更高,所以还是使用 eBPF 来实现 podIP Masq,如果需要的话。不过,eBPF 虽然性能高,但实现复杂。
与 calico 对比
calico 也有 podIP masq 成 nodeIP 的功能,见 Configure outgoing NAT[11] , 可以通过参数 natOutgoing
配置:
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: default-ipv4-ippool
spec:
cidr: 192.168.0.0/16
natOutgoing: true
我们生产 K8s 有少量的集群,容器网络插件用的是 calico,配置都是关闭的 natOutgoing: false
。calico 默认应该是下发 iptables 规则实现的 SNAT。
总结
cilium 默认使用 podIP Masq,这样当 pod 不是访问其他 pod 时,会把 podIP SNAT 为 nodeIP,尤其在 podIP 是私网不可达且访问集群外部资源时有用。但是,由于我们采用 cilium + BGP 模式,podIP 在公司内网可达,不需要这个功能,所以需要配置关闭。
另外,一个坑是我们配置一直没有关闭这个功能,所以配置一直都是错的,只是因为 cilium v1.8.1 自己的 bug,导致 podIP Masq 没有开启而已。
待调研
cilium podIP Masq ebpf 逻辑共用的 NodePort service 实现,可以调研下 cilium 如何实现 NodePort service?
参考文献
Masquerading[12] pull/12456[13] datapath: Enable BPF MASQ for veth mode in IPv4[14]
引用链接
[1]masquerading: https://docs.cilium.io/en/v1.8/concepts/networking/masquerading/
[2]daemon_main.go#L679-L680: https://github.com/cilium/cilium/blob/v1.11.1/daemon/cmd/daemon_main.go#L679-L680
[3]pull/12456: https://github.com/cilium/cilium/pull/12456
[4]config.go#L505-L518: https://github.com/cilium/cilium/blob/v1.11.1/pkg/datapath/linux/config/config.go#L505-L518
[5]nodeport.h#L1160-L1170: https://github.com/cilium/cilium/blob/v1.11.1/bpf/lib/nodeport.h#L1160-L1170
[6]nat.h#L504-L570: https://github.com/cilium/cilium/blob/v1.11.1/bpf/lib/nat.h#L504-L570
[7]nat.h#L322-L378: https://github.com/cilium/cilium/blob/v1.11.1/bpf/lib/nat.h#L322-L378
[8]bpf_host.c#L1003-L1103: https://github.com/cilium/cilium/blob/v1.11.1/bpf/bpf_host.c#L1003-L1103
[9]bpf_host.c#L962-L987: https://github.com/cilium/cilium/blob/v1.11.1/bpf/bpf_host.c#L962-L987
[10]iptables.go#L1097-L1137: https://github.com/cilium/cilium/blob/v1.11.1/pkg/datapath/iptables/iptables.go#L1097-L1137
[11]Configure outgoing NAT: https://projectcalico.docs.tigera.io/networking/workloads-outside-cluster
[12]Masquerading: https://docs.cilium.io/en/v1.8/concepts/networking/masquerading/
[13]pull/12456: https://github.com/cilium/cilium/pull/12456
[14]datapath: Enable BPF MASQ for veth mode in IPv4: https://github.com/cilium/cilium/commit/0962c029849168da34d88f57dd7d0b73876d823b
你可能还喜欢
点击下方图片即可阅读
2022-06-25
2022-06-24
2022-06-23
云原生是一种信仰 🤘
关注公众号
后台回复◉k8s◉获取史上最方便快捷的 Kubernetes 高可用部署工具,只需一条命令,连 ssh 都不需要!
点击 "阅读原文" 获取更好的阅读体验!
发现朋友圈变“安静”了吗?