2. Calico 架构深度解析

Calico 在 Kubernetes 中并不是一个“单进程组件”,而是一组职责清晰、通过数据存储与本地接口协作的组件集合。理解它的关键,不是记住每个组件的名字,而是看清一条路由或一条网络策略从“被声明”到“被内核执行”的完整链路。本节围绕控制平面与数据平面的分工,逐一拆解 Felix、BIRD、Confd、Typha 与 CNI 插件,并说明它们如何共同把集群意图翻译成节点上的实际转发行为。

控制平面与数据平面的职责边界

Calico 的控制平面负责“决定应该发生什么”:它从 Kubernetes API Server 或 Calico datastore 中读取 NetworkPolicy、IPPool、WorkloadEndpoint、Node 等对象,计算出每个节点应具备的路由、ACL、接口与地址状态。数据平面负责“实际让包怎么走”:它由 Linux 内核中的路由表、iptables/nftables 规则、IPSet、veth 接口以及可选的 eBPF 程序构成。控制平面不转发数据包,数据平面不自行决策,两者之间靠 Felix 这个“节点代理”完成同步。

在典型部署中,Calico 的组件分布如下:每个节点运行一个 calico-node DaemonSet,其中包含 Felix、BIRD、Confd 以及 CNI 插件所需的二进制;Typha 通常以 Deployment 形式集中部署,作为 datastore 的缓存代理;CNI 插件在 Pod 创建时被 kubelet 调用,负责分配 IP 并配置接口。下面这张图展示了各组件之间的数据流与依赖关系。

控制平面(决策层) Kubernetes API Server NetworkPolicy / Pod / Node Typha datastore 缓存代理 Confd 模板渲染 BIRD 配置 BIRD BGP 路由通告 数据平面(执行层) Felix 节点代理 / 策略同步 CNI 插件 IP 分配 / veth 配置 Linux 内核数据平面 路由表 · iptables/nftables · IPSet · veth · eBPF 写入路由 / ACL / 接口状态 创建 veth / 分配 IP 下发路由 同步策略与端点数据 控制流 数据/路由流 接口配置流 缓存同步流

Felix:节点上的策略执行引擎

Felix 是 Calico 在每台节点上的“手和脚”。它从 Typha 或 datastore 订阅 WorkloadEndpoint、NetworkPolicy、IPPool、HostEndpoint 等对象的变化,然后把这些声明翻译成内核可执行的规则。具体来说,Felix 负责:

  • 在主机网络命名空间中写入 iptables/nftables 规则链,实现入站与出站策略;
  • 维护 IPSet,把大量 Pod IP 或端口集合压缩成高效匹配结构;
  • 配置 veth 接口的 ARP、路由与代理 ARP 行为;
  • 在启用 eBPF 数据平面时,将策略编译为 eBPF 程序并挂载到 TC 钩子;
  • 上报节点健康状态与接口状态,供 calicoctl 与监控系统读取。

Felix 的同步是“最终一致”的:当策略变更时,它不会逐条增量修改,而是重新计算该节点上所有相关端点的完整规则集,再以原子方式替换。这种设计避免了规则残留,但也意味着大规模策略变更时会出现短暂的计算峰值。实际运维中,如果发现节点上 iptables 规则数量异常增长,通常需要检查 Felix 的日志中是否有重复的 WorkloadEndpoint 或策略对象。

BIRD 与 Confd:路由信息的生成与分发

BIRD 是一个开源 BGP 守护进程,Calico 用它来实现节点之间的路由通告。在默认的 IPIP 或 VXLAN 模式下,BIRD 负责把本节点上的 Pod CIDR 通过 BGP 通告给其他节点;在 BGP 直连模式下,它还会与物理路由器建立对等会话,把 Pod 网段发布到数据中心网络中。

BIRD 本身不直接读取 Kubernetes API。Confd 承担了“翻译层”的角色:它监听 datastore 中与 BGP 相关的对象(如 BGPPeer、IPPool、Node 的 AS 号与路由器 ID),按照预置模板渲染出 BIRD 的配置文件,然后触发 BIRD 重新加载。这个链路可以概括为:datastore 变更 → Confd 模板渲染 → BIRD 配置重载 → BGP 路由更新 → 内核路由表变化。如果 BGP 会话建立失败,排查顺序通常是先看 Confd 是否成功渲染配置,再看 BIRD 日志中的会话状态,最后检查节点间 179 端口与 AS 号配置。

Typha:为大规模集群减压

在没有 Typha 的部署中,每个节点的 Felix 都会直接与 datastore 建立 watch 连接。当节点数量达到数百甚至上千时,datastore 会承受大量并发 watch 请求,API Server 的负载也会显著上升。Typha 的作用是“扇出代理”:它代表所有 Felix 与 datastore 建立一个或少量的长连接,缓存全量数据,再把变更分发给各个 Felix。

Typha 并不改变数据一致性模型,它只是减少了连接数和重复的序列化开销。在 500 节点以上的集群中,启用 Typha 通常能将 API Server 的 watch 连接数降低一个数量级。需要注意的是,Typha 本身是无状态的,可以水平扩展;如果 Typha 实例全部不可用,Felix 会回退到直接连接 datastore,但此时 API Server 压力会明显上升。

CNI 插件:Pod 网络生命周期的入口

当 kubelet 创建 Pod 时,它会调用 CNI 插件。Calico 的 CNI 插件负责以下动作:

  1. 从 IPPool 中为 Pod 分配一个 IP 地址;
  2. 在主机上创建 veth pair,一端放入 Pod 的网络命名空间,另一端留在主机;
  3. 为 Pod 接口配置 IP、路由和 ARP 代理;
  4. 在 datastore 中创建 WorkloadEndpoint 对象,记录接口名、IP、节点与命名空间;
  5. 触发 Felix 同步该端点的策略规则。

CNI 插件与 Felix 之间没有直接调用关系,它们通过 datastore 中的 WorkloadEndpoint 对象解耦。CNI 写入端点,Felix 监听端点并下发规则。这种异步设计提高了可靠性,但也意味着 Pod 创建后策略生效存在毫秒到秒级的延迟。如果发现 Pod 启动后短暂无法通信,通常不是 CNI 失败,而是 Felix 尚未完成规则同步。

一次 Pod 创建中的组件协作时序

把上述组件串起来,一次典型的 Pod 创建过程如下:kubelet 调用 CNI 插件 → CNI 分配 IP 并创建 veth → CNI 写入 WorkloadEndpoint → Typha 缓存该变更 → Felix 收到端点事件 → Felix 计算策略并写入内核 → Confd 检测到 IPPool 或节点路由变化 → 渲染 BIRD 配置 → BIRD 通告新路由 → 其他节点更新路由表。整个过程没有中心控制器逐条指挥,而是各组件围绕 datastore 中的声明式对象各自收敛。

排查 Calico 网络问题时,可以按“声明 → 缓存 → 执行 → 转发”的顺序定位:先确认 datastore 中对象是否存在,再确认 Typha 是否同步,然后检查 Felix 日志与内核规则,最后用 calicoctl node status 和 ip route 验证 BGP 与路由结果。

组件协作中的常见误区

第一个误区是把 BIRD 当成策略组件。BIRD 只负责路由可达性,不负责 ACL;Pod 之间的允许或拒绝由 Felix 写入的 iptables/eBPF 规则决定。第二个误区是认为 Typha 是必需组件。小规模集群可以不用 Typha,Felix 直连 datastore 也能正常工作。第三个误区是忽略 Confd 的模板渲染失败。如果 Confd 无法渲染 BIRD 配置,BGP 会话不会建立,跨节点 Pod 通信就会中断,但同节点内容器通信仍然正常,这种“部分通”的现象很容易被误判为策略问题。

理解这些组件的分工后,再看 Calico 的配置文件与日志,就能把“哪个组件出了问题”与“哪一层没有收敛”对应起来。控制平面负责声明与计算,数据平面负责匹配与转发,Felix、BIRD、Confd、Typha 与 CNI 插件各自守住一个环节,缺一不可,但也不互相越权。