上一篇我们拆解了 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 程序,实现网络与安全的一体化。
这篇文章会覆盖以下内容:
- Calico 架构四大组件:Felix、BIRD、confd、Typha,各自的职责和协作方式
- 路由模式详解:纯 BGP、IP-in-IP、VXLAN、CrossSubnet 四种模式的选型
- Felix 策略引擎:iptables 规则生成、workload endpoint 管理
- eBPF 数据面:替代 iptables 和 kube-proxy,与 Cilium 的异同
- IPAM:Calico IPAM 与 host-local 的对比,block affinity 机制
- 大规模部署:Typha 作为 Felix 的 API 代理、Route Reflector 配置
- 实验: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 代理。
Felix:策略引擎和路由编程
Felix 是 Calico 的核心大脑。它从 datastore(etcd 或 Kubernetes API)订阅所有与本节点相关的对象 – 包括 Pod、NetworkPolicy、BGPPeer、IPPool 等 – 然后在本机执行两类操作:
- 路由编程:往内核路由表里写路由条目,确保发往远端 Pod CIDR 的包知道下一跳在哪
- 策略执行:把 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 的职责:
- 与其他节点的 BIRD 实例建立 BGP peer 连接
- 把 Felix 写入内核路由表的本地 Pod CIDR 通过 BGP UPDATE 消息广播出去
- 接收其他节点广播的路由,写入内核路由表
默认情况下,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(如
BGPPeer、BGPConfiguration)声明式管理,confd
自动完成配置同步。
Typha:API 代理和扇出
Typha 是可选的,但在大规模集群里是必须的。
问题:如果有 500 个节点,就有 500 个 Felix 实例各自 watch Kubernetes API。每次有一个 Pod 创建或 NetworkPolicy 变更,API server 要给 500 个 watcher 各发一份通知。这会给 API server 带来巨大的连接和内存压力。
Typha 的解决方案:
- Typha 作为一个 Deployment 运行(通常 3-5 个副本)
- Typha 代替 Felix 去 watch Kubernetes API,只维护少量连接
- Felix 不再直接 watch API server,而是连接到 Typha
- Typha 把变更事件扇出给连接到它的所有 Felix 实例
Kubernetes API --watch--> Typha (3 replicas)
|---> Felix (Node 1)
|---> Felix (Node 2)
|---> ...
|---> Felix (Node 500)
官方建议:超过 50 个节点就应该启用 Typha。每个 Typha 实例可以服务约 200 个 Felix。
二、路由模式详解
Calico 支持四种路由模式。选择哪种取决于你的底层网络拓扑。
纯 BGP 模式(无封装)
这是 Calico 的旗舰模式,也是性能最好的模式。
工作原理:
- Felix 在内核路由表里为本地 Pod 写一条直连路由
- BIRD 把本地 Pod CIDR 通过 BGP 广播给其他节点
- 收到 BGP 路由的节点往路由表里写一条
via <next-hop>路由 - 包到了直接查路由表,纯三层转发
# 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 模式要求底层网络(物理交换机、路由器)允许转发目标 IP 不属于本子网的包。在二层直连的场景下没问题(同子网内直接 ARP);跨子网时需要物理路由器知道 Pod CIDR 的路由,或者使用 BGP peering 让 ToR 交换机参与路由通告。
优势:
- 零封装开销,MTU 不需要减小
- 延迟最低,吞吐最高
- 标准路由协议,对网络团队友好,容易排查
劣势:
- 对底层网络有要求,不是所有环境都能用
- 跨子网时需要额外的 BGP 配置
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: trueIP-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: trueVXLAN 模式下,Calico 不需要 BIRD,因为路由信息直接从 datastore 获取,由 Felix 编程到内核的 VXLAN FDB 表里。这简化了架构,但失去了 BGP 的灵活性。
CrossSubnet 模式
CrossSubnet 是一个务实的折衷方案:
- 同子网内:使用纯 BGP 直接路由(无封装)
- 跨子网时:使用 IP-in-IP 或 VXLAN 封装
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/0Felix 为每个 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: 80Felix 把它翻译成类似这样的 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.3Calico 扩展策略: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 在大规模场景下有几个已知问题(我们在第三篇详细讨论过):
- 规则数量膨胀:每个 Service 都需要一组 iptables 规则,数千个 Service 意味着数万条规则
- 线性匹配:iptables 的规则匹配是 O(n) 的,规则越多越慢
- 更新代价高:每次更新都要重写整张表,在大规模场景下更新延迟可达秒级
- conntrack 瓶颈:高连接数场景下,conntrack 表本身成为瓶颈
Calico eBPF 模式的架构
启用 eBPF 数据面后,Calico 会:
- 在 veth 设备上挂载 TC BPF 程序:处理 ingress/egress 策略
- 在 connect/sendmsg 系统调用上挂载 cgroup BPF 程序:实现 client-side Service 负载均衡
- 完全绕过 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),节点内顺序分配。但它有几个问题:
- 子网浪费:每个节点分配一个
/24(256 个 IP),实际可能只用 20 个 - 不支持跨节点 IP 迁移:Pod 从 Node A 漂移到 Node B,IP 一定会变
- 无法精细控制:不能按 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 的好处:
- 路由聚合:BIRD 只需要广播
/26粒度的路由,而不是每个 Pod 一条/32路由 - 按需分配:节点用完一个 block 才申请下一个,减少浪费
- 借用机制:如果某个节点的 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 1dFelix 会 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-typhaTypha
副本数的经验公式: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关键观察:
proto bird的路由是 BIRD 通过 BGP 学到并写入内核的- 每条路由是
/26粒度(一个 IP block),指向对应节点的物理 IP - 本地 Pod 路由是
/32粒度,指向cali*veth 设备
验证跨节点 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 的核心设计:
- 架构:Felix(策略 + 路由编程)、BIRD(BGP 路由分发)、confd(配置模板渲染)、Typha(API 扇出代理)四个组件各司其职
- 路由模式:纯 BGP 性能最优但要求底层网络配合;IP-in-IP / VXLAN 提供封装兜底;CrossSubnet 是生产首选的折衷方案
- 策略引擎:Felix 用 iptables + ipset 实现 NetworkPolicy,ipset 保证 O(1) 查找;eBPF 数据面可以完全替代 iptables 和 kube-proxy
- IPAM:block affinity 机制以
/26为粒度分配 IP block,平衡了路由聚合和地址利用率- 大规模: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 插件是什么样的。
参考资料
- “Project Calico: A Scalable and Secure Virtual Networking Approach,” A. Sherwood, 2018
- RFC 4271: “A Border Gateway Protocol 4 (BGP-4)”
- RFC 4456: “BGP Route Reflection: An Alternative to Full Mesh IBGP”
- Calico 官方文档:架构概述
- Calico 官方文档:eBPF 数据面
- Calico 官方文档:配置 BGP peering
- BIRD Internet Routing Daemon
- Calico IPAM 设计文档
- eBPF 网络编程
- BGP 基础
- netfilter 与 iptables
- CNI 规范与插件机制