土法炼钢兴趣小组的算法知识备份

Calico:BGP + iptables/eBPF 的网络与安全一体化

目录

上一篇我们拆解了 Flannel – 最简单的 CNI 插件,VXLAN 一封就完事。Flannel 的好处是入门门槛低,但它只管网络连通,不管安全策略;只有 Overlay,没有纯三层路由选项;大规模场景下的性能和灵活度都有明显短板。

Calico 是另一个极端。它从第一天起就选择了 BGP 作为路由协议,每个节点跑一个 BIRD 守护进程,把 Pod CIDR 通过标准的 BGP UPDATE 消息广播给全集群。收到路由通告的节点直接在内核路由表里插一条 10.244.1.0/26 via 192.168.1.20 dev eth0,包到了查路由表就知道下一跳在哪 – 不需要 VXLAN 头,不需要 UDP 封装,纯三层转发,性能接近裸机。

同时,Calico 自带一个完整的策略引擎 Felix,能把 Kubernetes NetworkPolicy(以及更强大的 Calico 自定义策略)翻译成 iptables 规则或 eBPF 程序,实现网络与安全的一体化。

这篇文章会覆盖以下内容:

  1. Calico 架构四大组件:Felix、BIRD、confd、Typha,各自的职责和协作方式
  2. 路由模式详解:纯 BGP、IP-in-IP、VXLAN、CrossSubnet 四种模式的选型
  3. Felix 策略引擎:iptables 规则生成、workload endpoint 管理
  4. eBPF 数据面:替代 iptables 和 kube-proxy,与 Cilium 的异同
  5. IPAM:Calico IPAM 与 host-local 的对比,block affinity 机制
  6. 大规模部署:Typha 作为 Felix 的 API 代理、Route Reflector 配置
  7. 实验:Calico 纯 BGP 模式部署,用 calicoctl 查看 BGP peer 和路由表

本文的实验环境:Linux 6.x, x86_64。Kubernetes 1.30, Calico v3.28。


一、Calico 架构全景:四大组件

Calico 的所有节点级组件打包在一个叫 calico-node 的 DaemonSet Pod 里。这个 Pod 内部其实跑了三个进程:Felix、BIRD、confd。集群级别还有一个可选的 Typha 组件作为 API 代理。

Calico 架构:Felix / BIRD / confd / Typha

Felix:策略引擎和路由编程

Felix 是 Calico 的核心大脑。它从 datastore(etcd 或 Kubernetes API)订阅所有与本节点相关的对象 – 包括 Pod、NetworkPolicy、BGPPeer、IPPool 等 – 然后在本机执行两类操作:

  1. 路由编程:往内核路由表里写路由条目,确保发往远端 Pod CIDR 的包知道下一跳在哪
  2. 策略执行:把 NetworkPolicy 翻译成 iptables 规则(或 eBPF 程序),插到 FORWARD 链和每个 workload endpoint 的 ingress/egress 链里

Felix 用 Go 写的,核心逻辑在 felix/dataplane 包里。每次收到变更事件,Felix 会计算一个增量 diff,然后只更新需要变化的 iptables 规则和路由条目,避免全量重写的性能开销。

BIRD:BGP 守护进程

BIRD 是一个成熟的开源路由守护进程,支持 BGP、OSPF、RIP 等多种路由协议。在 Calico 里,BIRD 只用来跑 BGP。

BIRD 的职责:

默认情况下,Calico 使用 node-to-node mesh 模式 – 每对节点之间都建立一个 IBGP session。这在小集群(< 100 节点)没问题,但在大集群里 BGP session 的数量会呈 O(n^2) 增长,需要引入 Route Reflector。

confd:配置模板引擎

confd 的角色很简单:它从 datastore 监听 BGP 配置的变化(比如新增了一个 BGPPeer、修改了 AS 号),然后用模板引擎渲染出 BIRD 的配置文件,通知 BIRD 重新加载。

工作流程:

datastore 变更 --> confd watch --> 渲染 BIRD 配置模板 --> 写入 bird.cfg --> BIRD reload

这意味着你不需要手动编辑 BIRD 配置文件。所有 BGP 配置都通过 Calico 的 CRD(如 BGPPeerBGPConfiguration)声明式管理,confd 自动完成配置同步。

Typha:API 代理和扇出

Typha 是可选的,但在大规模集群里是必须的。

问题:如果有 500 个节点,就有 500 个 Felix 实例各自 watch Kubernetes API。每次有一个 Pod 创建或 NetworkPolicy 变更,API server 要给 500 个 watcher 各发一份通知。这会给 API server 带来巨大的连接和内存压力。

Typha 的解决方案:

Kubernetes API --watch--> Typha (3 replicas)
                              |---> Felix (Node 1)
                              |---> Felix (Node 2)
                              |---> ...
                              |---> Felix (Node 500)

官方建议:超过 50 个节点就应该启用 Typha。每个 Typha 实例可以服务约 200 个 Felix。


二、路由模式详解

Calico 支持四种路由模式。选择哪种取决于你的底层网络拓扑。

纯 BGP 模式(无封装)

这是 Calico 的旗舰模式,也是性能最好的模式。

工作原理:

  1. Felix 在内核路由表里为本地 Pod 写一条直连路由
  2. BIRD 把本地 Pod CIDR 通过 BGP 广播给其他节点
  3. 收到 BGP 路由的节点往路由表里写一条 via <next-hop> 路由
  4. 包到了直接查路由表,纯三层转发
# Node 1 上的路由表
$ ip route show proto bird
10.244.1.0/26 via 192.168.1.20 dev eth0
10.244.2.0/26 via 192.168.1.30 dev eth0
10.244.3.0/26 via 192.168.2.10 dev eth0

# 本地 Pod 的路由
$ ip route show | grep cali
10.244.0.1 dev cali1234abcd scope link
10.244.0.2 dev cali5678efgh scope link
BGP 路由传播流程

前提条件:纯 BGP 模式要求底层网络(物理交换机、路由器)允许转发目标 IP 不属于本子网的包。在二层直连的场景下没问题(同子网内直接 ARP);跨子网时需要物理路由器知道 Pod CIDR 的路由,或者使用 BGP peering 让 ToR 交换机参与路由通告。

优势

劣势

IP-in-IP 封装模式

当底层网络不允许直接转发 Pod IP 的包时,Calico 可以使用 IP-in-IP 隧道:

原始包:[IP: src=10.244.0.2 dst=10.244.1.5] [payload]
封装后:[IP: src=192.168.1.10 dst=192.168.1.20] [IP: src=10.244.0.2 dst=10.244.1.5] [payload]

外层 IP 头使用节点的物理 IP,底层网络只需要知道怎么路由节点 IP 就行。

# IPPool 配置
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: default-ipv4-ippool
spec:
  cidr: 10.244.0.0/16
  ipipMode: Always
  vxlanMode: Never
  natOutgoing: true

IP-in-IP 的开销比 VXLAN 小(只多 20 字节的 IP 头,VXLAN 要多 50 字节),但它不是所有云平台都支持(比如 Azure 不支持 IP-in-IP)。

VXLAN 封装模式

VXLAN 模式和 Flannel 的 VXLAN 后端类似,用 UDP 封装三层包:

apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: default-ipv4-ippool
spec:
  cidr: 10.244.0.0/16
  ipipMode: Never
  vxlanMode: Always
  natOutgoing: true

VXLAN 模式下,Calico 不需要 BIRD,因为路由信息直接从 datastore 获取,由 Felix 编程到内核的 VXLAN FDB 表里。这简化了架构,但失去了 BGP 的灵活性。

CrossSubnet 模式

CrossSubnet 是一个务实的折衷方案:

apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: default-ipv4-ippool
spec:
  cidr: 10.244.0.0/16
  ipipMode: CrossSubnet  # 或 vxlanMode: CrossSubnet
  natOutgoing: true

这是生产环境中最常用的模式。大多数数据中心里,同一个 Rack 内的节点在同一个二层域,直接路由零开销;跨 Rack 时才走隧道。

四种模式对比

模式 封装开销 底层网络要求 适用场景 是否需要 BIRD
纯 BGP 需要 L2 直连或路由器配合 性能敏感、网络可控
IP-in-IP 20 bytes 无特殊要求(Azure 除外) 通用,偏好低开销
VXLAN 50 bytes 无特殊要求 云环境兼容性优先
CrossSubnet 同子网 0 / 跨子网 20-50 无特殊要求 生产环境推荐 是(BGP 部分)

三、Felix 策略引擎:iptables 规则生成

Felix 不仅管路由,还是 Calico 的安全策略执行引擎。它把 Kubernetes 标准的 NetworkPolicy 和 Calico 扩展的 GlobalNetworkPolicy 翻译成 iptables 规则。

iptables 链的结构

Felix 在 filter 表的 FORWARD 链里插入跳转规则,把流量导入 Calico 自己的链:

# 查看 Felix 创建的 iptables 链
$ sudo iptables -t filter -L FORWARD -n --line-numbers
num  target     prot opt source    destination
1    cali-FORWARD  all  --  0.0.0.0/0  0.0.0.0/0
2    KUBE-FORWARD  all  --  0.0.0.0/0  0.0.0.0/0

$ sudo iptables -t filter -L cali-FORWARD -n
Chain cali-FORWARD (1 references)
target     prot opt source    destination
MARK       all  --  0.0.0.0/0  0.0.0.0/0  MARK and 0xfff0ffff
cali-from-hep-forward  all  --  0.0.0.0/0  0.0.0.0/0  mark match 0x0/0x10000
cali-from-wl-dispatch  all  --  0.0.0.0/0  0.0.0.0/0
cali-to-wl-dispatch    all  --  0.0.0.0/0  0.0.0.0/0
cali-to-hep-forward    all  --  0.0.0.0/0  0.0.0.0/0
cali-cidr-block        all  --  0.0.0.0/0  0.0.0.0/0

Felix 为每个 workload endpoint(Pod 的 veth 接口)创建独立的 ingress 和 egress 链:

cali-from-wl-dispatch --> cali-fw-cali1234abcd (from workload)
cali-to-wl-dispatch   --> cali-tw-cali1234abcd (to workload)

workload endpoint 链的规则逻辑

以一个允许来自 app=frontend 的 Pod 访问 TCP 80 端口的 NetworkPolicy 为例:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: backend
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 80

Felix 把它翻译成类似这样的 iptables 规则:

$ sudo iptables -t filter -L cali-tw-cali1234abcd -n
Chain cali-tw-cali1234abcd (1 references)
target     prot opt source    destination
# 允许已建立的连接
ACCEPT     all  --  0.0.0.0/0  0.0.0.0/0  ctstate RELATED,ESTABLISHED
# 允许来自 ipset 中 IP 的 TCP 80 流量
ACCEPT     tcp  --  0.0.0.0/0  0.0.0.0/0  match-set cali40s:jk29X8a-lPbW src tcp dpt:80
# 默认拒绝
DROP       all  --  0.0.0.0/0  0.0.0.0/0

注意 Felix 使用 ipset 来匹配 Pod IP。当 app=frontend 标签的 Pod 扩缩容时,Felix 只需要更新 ipset 的成员,不需要重写 iptables 规则。这是一个关键的性能优化 – ipset 的查找时间复杂度是 O(1),而逐条匹配 iptables 规则是 O(n)。

# 查看 ipset 内容
$ sudo ipset list cali40s:jk29X8a-lPbW
Name: cali40s:jk29X8a-lPbW
Type: hash:net
Members:
10.244.0.5
10.244.1.8
10.244.2.3

Calico 扩展策略:GlobalNetworkPolicy

Kubernetes 原生的 NetworkPolicy 有几个限制:它是 namespace 级别的,不支持 deny 规则(只能白名单),不支持按 service account 或全局 CIDR 匹配。

Calico 的 GlobalNetworkPolicy 补齐了这些能力:

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-external-egress
spec:
  order: 100
  selector: "env == 'production'"
  egress:
  - action: Deny
    destination:
      notNets:
      - 10.0.0.0/8
      - 172.16.0.0/12
      - 192.168.0.0/16
  - action: Allow
  types:
  - Egress

这条策略的含义:所有标签为 env=production 的 Pod,禁止访问外部(非 RFC 1918)IP。Felix 会在相关 workload endpoint 的 egress 链里插入对应的 DROP 规则。


四、eBPF 数据面:替代 iptables 和 kube-proxy

从 Calico v3.13 开始,Calico 提供了 eBPF 数据面作为 iptables 的替代方案。这不仅替代了 Felix 的 iptables 规则,还替代了 kube-proxy 的 Service 实现。

为什么要替代 iptables

iptables 在大规模场景下有几个已知问题(我们在第三篇详细讨论过):

  1. 规则数量膨胀:每个 Service 都需要一组 iptables 规则,数千个 Service 意味着数万条规则
  2. 线性匹配:iptables 的规则匹配是 O(n) 的,规则越多越慢
  3. 更新代价高:每次更新都要重写整张表,在大规模场景下更新延迟可达秒级
  4. conntrack 瓶颈:高连接数场景下,conntrack 表本身成为瓶颈

Calico eBPF 模式的架构

启用 eBPF 数据面后,Calico 会:

  1. 在 veth 设备上挂载 TC BPF 程序:处理 ingress/egress 策略
  2. 在 connect/sendmsg 系统调用上挂载 cgroup BPF 程序:实现 client-side Service 负载均衡
  3. 完全绕过 iptables 和 conntrack:包的转发路径不经过 netfilter
# 查看 Calico 挂载的 BPF 程序
$ tc filter show dev cali1234abcd egress
filter protocol all pref 1 bpf chain 0
filter protocol all pref 1 bpf chain 0 handle 0x1 calico_tc_egress direct-action

$ tc filter show dev cali1234abcd ingress
filter protocol all pref 1 bpf chain 0
filter protocol all pref 1 bpf chain 0 handle 0x1 calico_tc_ingress direct-action

启用 eBPF 数据面

# 安装 Calico operator 后,修改 Installation 资源
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  calicoNetwork:
    linuxDataplane: BPF
    # 启用 eBPF 后需要禁用 kube-proxy
---
# 删除 kube-proxy
# kubectl delete daemonset kube-proxy -n kube-system

启用后,Felix 不再生成 iptables 规则,而是编译和加载 BPF 程序。Service 的负载均衡也由 BPF 程序在 socket 层完成,消除了 DNAT 和 conntrack 的开销。

与 Cilium 的 eBPF 实现对比

Calico 和 Cilium 都使用 eBPF,但设计哲学不同:

维度 Calico eBPF Cilium
定位 eBPF 作为 iptables 的替代数据面 从第一天就围绕 eBPF 设计
路由 依赖 BGP(BIRD)或内核路由表 使用 BPF map 存储路由信息
身份模型 基于 IP + ipset 基于 identity(数字 ID 标识安全组)
Service 实现 BPF socket-level LB BPF socket-level LB + XDP
L7 策略 需要 Envoy sidecar(企业版) 内置 Envoy 集成
可观测性 依赖传统工具(tcpdump 等) Hubble 原生流可观测
成熟度 eBPF 是较新的数据面选项 eBPF 是核心架构
BGP 支持 原生支持,完整的 BGP 栈 有限支持(通过外部集成)

简单来说:如果你的环境重度依赖 BGP 和传统网络基础设施,Calico 是更自然的选择;如果你想要一个 eBPF-native 的全栈方案,Cilium 更合适。详见下一篇


五、IPAM:Calico IPAM 与 block affinity

IP 地址管理(IPAM)决定了每个 Pod 怎么拿到 IP。Calico 有自己的 IPAM 插件,也可以使用 CNI 标准的 host-local 插件。

host-local IPAM 的局限

host-local 是最简单的 IPAM:给每个节点分配一个固定的子网(比如 /24),节点内顺序分配。但它有几个问题:

  1. 子网浪费:每个节点分配一个 /24(256 个 IP),实际可能只用 20 个
  2. 不支持跨节点 IP 迁移:Pod 从 Node A 漂移到 Node B,IP 一定会变
  3. 无法精细控制:不能按 namespace 或 label 分配不同的 IP 池

Calico IPAM 的 block affinity

Calico IPAM 引入了 IP block 的概念。一个 IPPool(比如 10.244.0.0/16)被切分成多个小的 block(默认 /26,即 64 个 IP)。每个 block 有一个 affinity(亲和性),绑定到某个节点。

# 查看 IP block 分配
$ calicoctl ipam show --show-blocks
+----------+----------------+-----------+------------+-----------+
| GROUPING |     CIDR       | IPS TOTAL | IPS IN USE | IPS FREE  |
+----------+----------------+-----------+------------+-----------+
| Node     | 10.244.0.0/26  | 64        | 12         | 52        |
| node-1   | 10.244.0.64/26 | 64        | 8          | 56        |
+----------+----------------+-----------+------------+-----------+
| Node     | 10.244.1.0/26  | 64        | 15         | 49        |
| node-2   | 10.244.1.64/26 | 64        | 3          | 61        |
+----------+----------------+-----------+------------+-----------+

block affinity 的好处:

  1. 路由聚合:BIRD 只需要广播 /26 粒度的路由,而不是每个 Pod 一条 /32 路由
  2. 按需分配:节点用完一个 block 才申请下一个,减少浪费
  3. 借用机制:如果某个节点的 block 都满了,它可以从其他节点的 block “借” IP(borrowed address),不过这会导致多一条 /32 路由

多 IPPool 和 namespace 隔离

Calico 允许创建多个 IPPool,并通过 annotation 控制哪些 namespace 或 Pod 使用哪个池:

# 为特定 namespace 分配专属 IP 池
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: production-pool
spec:
  cidr: 10.245.0.0/16
  ipipMode: CrossSubnet
  natOutgoing: true
  nodeSelector: "!all()"  # 不自动分配给节点
---
# 在 namespace 上标注使用哪个池
apiVersion: v1
kind: Namespace
metadata:
  name: production
  annotations:
    cni.projectcalico.org/ipv4pools: '["production-pool"]'

Calico IPAM 内部数据结构

Calico IPAM 的状态存储在 datastore 里,用两个核心资源表示:

# 查看 IPAMBlock 资源
$ kubectl get ipamblocks -o wide
NAME                  CIDR             AFFINITY          NUM-ADDR  ALLOC
10-244-0-0-26         10.244.0.0/26    host:node-1       64        12
10-244-0-64-26        10.244.0.64/26   host:node-1       64        8
10-244-1-0-26         10.244.1.0/26    host:node-2       64        15

# 查看 IPAMHandle -- 记录每个 IP 被哪个 workload 使用
$ kubectl get ipamhandles
NAME                                                  AGE
k8s-pod-network.abc123def456.eth0                     3d
k8s-pod-network.xyz789ghi012.eth0                     1d

Felix 会 watch 这些资源,确保本节点的路由表反映最新的 IP 分配状态。


六、大规模部署:Typha 和 Route Reflector

Typha 的部署和调优

前面提到 Typha 可以减轻 API server 的压力。在生产环境部署 Typha 时,有几个关键配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: calico-typha
  namespace: calico-system
spec:
  replicas: 3  # 通常 3-5 个副本
  template:
    spec:
      containers:
      - name: calico-typha
        image: calico/typha:v3.28.0
        env:
        - name: TYPHA_MAXCONNECTIONSLOWERBOUND
          value: "200"
        - name: TYPHA_HEALTHENABLED
          value: "true"
        - name: TYPHA_LOGSEVERITYSCREEN
          value: "info"
        resources:
          requests:
            cpu: 150m
            memory: 128Mi
          limits:
            cpu: "1"
            memory: 512Mi
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: calico-typha

Typha 副本数的经验公式:ceil(节点数 / 200),但至少 3 个以确保高可用。使用 topologySpreadConstraints 确保 Typha 副本分散到不同节点。

Route Reflector 配置

在大集群里,node-to-node mesh 的 BGP session 数量是 n*(n-1)/2。100 个节点就是 4950 个 session,500 个节点是 124750 个。这不可接受。

Route Reflector(RR)是 BGP 协议本身的解决方案(RFC 4456)。我们在第五篇已经详细讨论过 RR 的原理。在 Calico 里配置 RR:

第一步:关闭 node-to-node mesh

apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
  name: default
spec:
  nodeToNodeMeshEnabled: false
  asNumber: 64512

第二步:选择 RR 节点并添加标签

# 选择 2-3 个节点作为 Route Reflector
$ kubectl label node node-rr-1 route-reflector=true
$ kubectl label node node-rr-2 route-reflector=true
$ kubectl label node node-rr-3 route-reflector=true

第三步:为 RR 节点配置 cluster ID

apiVersion: projectcalico.org/v3
kind: Node
metadata:
  name: node-rr-1
  labels:
    route-reflector: "true"
spec:
  bgp:
    ipv4Address: 192.168.1.100/24
    routeReflectorClusterID: 244.0.0.1

第四步:配置 BGPPeer – 所有节点 peer 到 RR

apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: peer-to-rr
spec:
  # 所有节点
  nodeSelector: "all()"
  # peer 到 RR 节点
  peerSelector: "route-reflector == 'true'"

配置完成后,BGP 拓扑从 full mesh 变成星形:每个节点只与 2-3 个 RR 建立 session,RR 负责把路由反射给所有 client。session 数量从 O(n^2) 降到 O(n)。

# 验证 BGP peer 状态
$ sudo calicoctl node status
Calico process is running.

IPv4 BGP status
+-----------------+---------------+-------+----------+-------------+
| PEER ADDRESS    | PEER TYPE     | STATE | SINCE    | INFO        |
+-----------------+---------------+-------+----------+-------------+
| 192.168.1.100   | node specific | up    | 08:22:10 | Established |
| 192.168.1.101   | node specific | up    | 08:22:12 | Established |
| 192.168.1.102   | node specific | up    | 08:22:11 | Established |
+-----------------+---------------+-------+----------+-------------+

与物理网络集成:ToR peering

在更高级的部署中,Calico 的 BIRD 可以直接与数据中心的 ToR(Top-of-Rack)交换机建立 BGP peer。这让物理网络直接感知 Pod 路由,实现真正的无封装端到端路由:

apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: peer-to-tor
spec:
  # 所有 Rack 1 的节点
  nodeSelector: "rack == 'rack-1'"
  peerIP: 10.0.1.1  # ToR 交换机的 IP
  asNumber: 65001    # ToR 的 AS 号(EBGP)

七、实验:Calico 纯 BGP 模式部署与验证

环境准备

用 kubeadm 搭建一个 3 节点集群,初始化时不安装 CNI:

# 在 master 节点上初始化(不指定 CNI)
$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --skip-phases=addon/kube-proxy

# 安装 Calico operator
$ kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/tigera-operator.yaml

# 创建 Installation 资源 -- 使用纯 BGP 模式
$ cat <<EOF | kubectl apply -f -
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  calicoNetwork:
    ipPools:
    - cidr: 10.244.0.0/16
      encapsulation: None
      natOutgoing: Enabled
      nodeSelector: all()
      blockSize: 26
EOF

等待所有 calico-node Pod 就绪:

$ kubectl get pods -n calico-system -w
NAME                                       READY   STATUS    RESTARTS   AGE
calico-kube-controllers-5f4747b5b4-xhz2k   1/1     Running   0          2m
calico-node-abc12                           1/1     Running   0          2m
calico-node-def34                           1/1     Running   0          2m
calico-node-ghi56                           1/1     Running   0          2m
calico-typha-7c8d9e0f1a-j2k3l              1/1     Running   0          2m

验证 BGP peering

# 安装 calicoctl
$ kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calicoctl.yaml

# 在节点上查看 BGP 状态
$ sudo calicoctl node status
Calico process is running.

IPv4 BGP status
+-----------------+-------------------+-------+----------+-------------+
| PEER ADDRESS    | PEER TYPE         | STATE | SINCE    | INFO        |
+-----------------+-------------------+-------+----------+-------------+
| 192.168.1.20    | node-to-node mesh | up    | 14:30:05 | Established |
| 192.168.1.30    | node-to-node mesh | up    | 14:30:07 | Established |
+-----------------+-------------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.

所有 peer 状态为 Established,说明 BGP session 建立成功。

查看 BGP 路由

# 查看 BIRD 学到的路由
$ sudo calicoctl node diags | grep -A 5 "BIRD"

# 直接查看内核路由表
$ ip route show proto bird
10.244.1.0/26 via 192.168.1.20 dev eth0
10.244.2.0/26 via 192.168.1.30 dev eth0

# 查看本地 Pod 路由
$ ip route show | grep cali
10.244.0.1 dev cali0ab1c2d3 scope link
10.244.0.2 dev cali4ef5g6h7 scope link
10.244.0.3 dev cali8ij9k0l1 scope link

关键观察:

验证跨节点 Pod 通信

# 创建测试 Pod
$ kubectl run test-a --image=busybox --command -- sleep 3600
$ kubectl run test-b --image=busybox --command -- sleep 3600

# 确认它们在不同节点上
$ kubectl get pods -o wide
NAME     READY   STATUS    NODE      IP
test-a   1/1     Running   node-1    10.244.0.5
test-b   1/1     Running   node-2    10.244.1.8

# 从 test-a ping test-b
$ kubectl exec test-a -- ping -c 3 10.244.1.8
PING 10.244.1.8 (10.244.1.8): 56 data bytes
64 bytes from 10.244.1.8: seq=0 ttl=62 time=0.345 ms
64 bytes from 10.244.1.8: seq=1 ttl=62 time=0.289 ms
64 bytes from 10.244.1.8: seq=2 ttl=62 time=0.301 ms

--- 10.244.1.8 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.289/0.312/0.345 ms

注意 TTL 是 62(初始 64,经过两跳:源节点 -> 目标节点 -> Pod 内核)。

抓包验证无封装

# 在 node-1 的 eth0 上抓包
$ sudo tcpdump -i eth0 -nn host 10.244.1.8
14:35:22.123456 IP 10.244.0.5 > 10.244.1.8: ICMP echo request, id 42, seq 0
14:35:22.123789 IP 10.244.1.8 > 10.244.0.5: ICMP echo reply, id 42, seq 0

可以看到抓到的包直接就是 Pod IP 之间的 ICMP,没有任何外层封装 – 这就是纯 BGP 模式的效果。如果是 VXLAN 或 IP-in-IP 模式,你会看到外层 IP 头是节点 IP,里面才是 Pod IP。

查看 IPAM 状态

$ calicoctl ipam show
+----------+--------------+-----------+------------+----------+
| GROUPING | CIDR         | IPS TOTAL | IPS IN USE | IPS FREE |
+----------+--------------+-----------+------------+----------+
| IP Pool  | 10.244.0.0/16| 65536     | 38         | 65498    |
+----------+--------------+-----------+------------+----------+

$ calicoctl ipam show --show-blocks
+----------+----------------+-----------+------------+----------+
| GROUPING | CIDR           | IPS TOTAL | IPS IN USE | IPS FREE |
+----------+----------------+-----------+------------+----------+
| Node     | 10.244.0.0/26  | 64        | 15         | 49       |
| node-1   |                |           |            |          |
+----------+----------------+-----------+------------+----------+
| Node     | 10.244.1.0/26  | 64        | 12         | 52       |
| node-2   |                |           |            |          |
+----------+----------------+-----------+------------+----------+
| Node     | 10.244.2.0/26  | 64        | 11         | 53       |
| node-3   |                |           |            |          |
+----------+----------------+-----------+------------+----------+

查看 Felix 的 iptables 规则

# 列出 Calico 的 iptables 链数量
$ sudo iptables-save | grep -c "^-A cali-"
87

# 查看某个 Pod endpoint 的策略链
$ sudo iptables -t filter -L cali-tw-cali0ab1c2d3 -n --line-numbers
num  target     prot opt source    destination
1    ACCEPT     all  --  0.0.0.0/0  0.0.0.0/0  ctstate RELATED,ESTABLISHED
2    DROP       all  --  0.0.0.0/0  0.0.0.0/0  ctstate INVALID
3    MARK       all  --  0.0.0.0/0  0.0.0.0/0  MARK and 0xfffeffff
4    cali-pri-default.allow-dns  all  --  0.0.0.0/0  0.0.0.0/0
5    RETURN     all  --  0.0.0.0/0  0.0.0.0/0  mark match 0x10000/0x10000
6    DROP       all  --  0.0.0.0/0  0.0.0.0/0

八、深入 BIRD:BGP 配置和调试

BIRD 配置文件

confd 渲染出的 BIRD 配置文件大致长这样(简化版):

# /etc/calico/confd/config/bird.cfg
router id 192.168.1.10;

protocol kernel {
    scan time 2;
    import all;
    export all;
}

protocol static {
    route 10.244.0.0/26 blackhole;  # block CIDR 聚合
}

template bgp bgp_template {
    local as 64512;
    graceful restart;
}

protocol bgp Node_192_168_1_20 from bgp_template {
    neighbor 192.168.1.20 as 64512;
}

protocol bgp Node_192_168_1_30 from bgp_template {
    neighbor 192.168.1.30 as 64512;
}

使用 birdc 调试

在 calico-node Pod 内部可以使用 birdc 命令行工具与 BIRD 交互:

# 进入 calico-node Pod
$ kubectl exec -it -n calico-system calico-node-abc12 -- /bin/sh

# 查看 BGP 协议状态
$ birdc show protocols
name     proto    table    state  since       info
kernel1  Kernel   master   up     14:30:02
static1  Static   master   up     14:30:02
Node_192_168_1_20 BGP  master  up  14:30:05  Established
Node_192_168_1_30 BGP  master  up  14:30:07  Established

# 查看从 peer 学到的路由
$ birdc show route protocol Node_192_168_1_20
10.244.1.0/26      via 192.168.1.20 on eth0 [Node_192_168_1_20 14:30:05] * (100/0)
                    Type: BGP unicast univ
                    BGP.origin: IGP
                    BGP.as_path:
                    BGP.next_hop: 192.168.1.20
                    BGP.local_pref: 100

# 查看路由表全部内容
$ birdc show route
10.244.0.0/26      blackhole [static1 14:30:02] * (200)
10.244.1.0/26      via 192.168.1.20 on eth0 [Node_192_168_1_20 14:30:05] * (100/0)
10.244.2.0/26      via 192.168.1.30 on eth0 [Node_192_168_1_30 14:30:07] * (100/0)

# 查看特定 peer 的详细信息
$ birdc show protocols all Node_192_168_1_20
Node_192_168_1_20 BGP  master  up  14:30:05  Established
  Routes:         1 imported, 1 exported
  BGP state:          Established
    Neighbor AS:      64512
    Hold timer:       90/90
    Keepalive timer:  30/30

九、总结

回顾一下 Calico 的核心设计:

  1. 架构:Felix(策略 + 路由编程)、BIRD(BGP 路由分发)、confd(配置模板渲染)、Typha(API 扇出代理)四个组件各司其职
  2. 路由模式:纯 BGP 性能最优但要求底层网络配合;IP-in-IP / VXLAN 提供封装兜底;CrossSubnet 是生产首选的折衷方案
  3. 策略引擎:Felix 用 iptables + ipset 实现 NetworkPolicy,ipset 保证 O(1) 查找;eBPF 数据面可以完全替代 iptables 和 kube-proxy
  4. IPAM:block affinity 机制以 /26 为粒度分配 IP block,平衡了路由聚合和地址利用率
  5. 大规模:Typha 解决 API server watch 压力,Route Reflector 解决 BGP full mesh 的 O(n^2) 问题

Calico 的核心竞争力在于:它是目前唯一一个同时提供 完整 BGP 栈 + 原生安全策略 + eBPF 数据面 的 CNI 插件。如果你的数据中心网络团队熟悉 BGP,Calico 几乎是不二之选。

但 Calico 并非完美。它的 eBPF 实现比 Cilium 晚了好几年,在 L7 可观测性和 service mesh 集成上也不如 Cilium 原生。如果你的场景更关注 eBPF-first 的能力,应该认真评估 Cilium。

下一篇我们会深入 Cilium:eBPF-native 的网络、安全与可观测 – 看看一个从第一天就围绕 eBPF 设计的 CNI 插件是什么样的。


参考资料


By .