LabHub
学习 学习路径 课程

CCA — Cilium 认证助理

IP 变了策略却不动摇的原因

在 LabHub 中继续学习

一句话总结

Cilium 为 Pod 的label 集合赋予一个数字(identity),并用该数字判断 policy。Pod 重新调度后即使 IP 改变,只要 label 相同,identity 就相同;因此无需修改 policy map,只需更新 ipcache 一处。

概念图: label 集合赋予一个数字(identity) · 语义 · 与安全相关的 label · 每次重新部署 Deployment 都会改变。

为什么需要了解这一点

基于 IP 的 policy 有一个根本问题:在 Kubernetes 中,IP 是临时值。Pod 会频繁终止并重新启动,每次都会获得新地址。如果用 IP 表达 policy,每重启一个 Pod,都要重新计算所有 node 上的 rule;在这段短暂窗口中,流量可能被错误允许或错误阻止。

Cilium 从更高一个层级解决这个问题。policy 以“app=frontend 可以访问 app=backend 的 8080”这样的语义表达,并且这种语义以数字形式一直保留到内核。iptables 时代,这种语义在翻译为 IP rule 时便会丢失。

工作原理

Pod 启动后,agent 只提取与安全相关的 label来计算 identity。哪些 label 会纳入、哪些会排除,非常重要。

포함되는 라벨 (예)          제외되는 라벨 (예)
k8s:app=frontend            k8s:pod-template-hash=7d4f9c
k8s:team=payments           k8s:controller-revision-hash=...
k8s:io.kubernetes.pod.       k8s:pod-template-generation=...
     namespace=shop

排除 pod-template-hash 一类值的理由很明确:它们每次重新部署 Deployment 都会改变。 如果纳入 identity,一次 rollout 就会让所有 Pod 获得新 identity,随后必须重新计算全部 policy map。这样一来,基于 label 的模型优势就会完全消失。

identity 创建后,“IP → identity”的 mapping 会传播到所有 node 的 cilium_ipcache map。接收侧 endpoint 的 policy map 以 (소스 아이덴티티, 포트, 프로토콜, 방향) 为 key,进行 O(1) 判断。Pod 移动时只有 ipcache 改变,policy map 保持不变,这正是该设计的核心收益。

kube-proxy 替代方案通过两层 map 实现 service abstraction。先在 cilium_lb4_services_v2 中查找 frontend(IP、port),再从 cilium_lb4_backends 获取实际 Pod 地址并执行 DNAT。除此之外,还有一项名为socket load balancing的优化:Pod 内的应用调用 connect() 的瞬间,内核 socket hook 就把 service IP 改为 backend IP。数据包根本不会以 service IP 发出,因此既不需要逐包 NAT,也不需要 conntrack entry。

启用 kubeProxyReplacement 时,必须同时提供 k8sServiceHostk8sServicePort,原因也在这里。没有 kube-proxy 时,负责把 kubernetes service 的 ClusterIP(通常是 10.96.0.1)转换为实际 API server 地址的是 Cilium 自身;但在 bootstrap 时刻,Cilium 尚未启动。为了避开先有鸡还是先有蛋的问题,需要直接告诉它实际地址。

routing mode 的选择标准很简单。

标准 Tunnel(VXLAN/Geneve) Native routing
network 要求 node 之间只需开放 UDP port underlay 必须知道 PodCIDR route
overhead 约 50 字节,MTU 减小
适用环境 无法控制 underlay 的环境 可进行 BGP peering 的 on-premises、cloud ENI

在 load balancing algorithm 中,要了解 Maglev consistent hashing。backend 增加或移除时,它会尽量减少 hash table 重排,让大多数现有 flow 继续映射到同一 backend。在多个 node 通过 ECMP 接收同一 VIP 的配置中,它还能让各 node 作出相同选择,效果尤其明显。

实际工作中的表现

作者的 home lab 在 PodCIDR 10.244.0.0/16 上运行 Cilium 1.20.1,datapath 状态报告如下。

KubeProxyReplacement:   True      [enp2s0  10.0.0.117 (Direct Routing)]
Routing:                Network: Tunnel [vxlan]   Host: BPF
Masquerading:           BPF   [enp2s0]   10.244.2.0/24
Encryption:             Wireguard [cilium_wg0 (Port: 51871, Peers: 2)]
Modules Health:         Stopped(0) Degraded(0) OK(92)

需要知道如何解读。Routing: Network Tunnel[vxlan] 表示 node 间使用封装,Host: BPF 表示连 host routing 也由 eBPF 负责。Masquerading: BPF 表示 SNAT 同样由 eBPF 而不是 iptables 处理。也就是说,该 node 不需要 kube-proxy,也不需要用于 masquerade 的 iptables rule。实际 KUBE- chain 数量为 0。

k8sServiceHost 中设置了 control plane node 的真实地址 10.0.0.120。这个地址如何确定也值得吸取教训。该 cluster 原本通过 DHCP lease 使用 .111,某天却获得 .120 后停止工作。由于 apiserver certificate SAN 中没有 .120,除恢复原 IP 外很难修复;最终只能固定为 static 后从头重建。请记住:bootstrap address 是少数几个日后修改极其痛苦的设置。

下个练习将做什么

亲自编写 Cilium Helm values 文件,声明 kube-proxy replacement 与 datapath option;给 node 添加 label 以固定 workload placement;实际 apply Service 和 Deployment,确认 backend 是否建立;最后创建验证脚本,确认 kube-proxy 确实不存在。