主题
Calico 深度解析:Pod IP 如何实现、Pod 之间如何通信
本章以 Calico 为例,系统讲解 Kubernetes 中 Pod IP 的分配机制、Pod 网络接口的创建过程,以及同节点/跨节点 Pod 通信的完整数据包路径。
1. Kubernetes 对容器网络的要求
在深入 Calico 之前,先明确 Kubernetes 网络模型对 Pod 通信的三个基本要求:
- 所有 Pod 之间可以不通过 NAT 直接通信。
- 节点上的 agent(kubelet)可以与所有 Pod 直接通信。
- Pod 看到的自己的 IP 与其他 Pod 看到的它的 IP 相同(即没有端口映射 / NAT 隐藏)。
这意味着每个 Pod 必须有一个真实的、在集群范围内可路由的 IP。
2. Calico 架构概览
Calico 是一个开源的网络和网络策略引擎,支持 Kubernetes、OpenShift 等编排平台。它的核心组件包括:
| 组件 | 作用 |
|---|---|
CNI Plugin (calico) | 在 Pod 创建/删除时被调用,负责配置 Pod 网络接口和路由 |
| Felix | 运行在每个节点上的 agent,负责路由、ACL(NetworkPolicy)、IPAM 等 |
| BIRD | BGP 路由守护进程,负责在节点之间交换路由信息 |
| etcd / Kubernetes API (KDD) | 存储 Calico 的配置和状态数据 |
| Typha(可选) | 减轻 Felix 对 Kubernetes API 的压力,作为 API 数据缓存层 |
| calico-kube-controllers | 控制器,处理 Pod/Namespace/Service 变化同步 |
text
┌─────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ │ ┌─────┐ │ │ ┌─────┐ │ │ ┌─────┐ │ │
│ │ │Pod A│ │ │ │Pod B│ │ │ │Pod C│ │ │
│ │ │IP:1 │ │◄──►│ │IP:2 │ │◄──►│ │IP:3 │ │ │
│ │ └──┬──┘ │ │ └──┬──┘ │ │ └──┬──┘ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ Felix │ │ Felix │ │ Felix │ │
│ │ BIRD │◄──►│ BIRD │◄──►│ BIRD │ (BGP 交换路由) │
│ └──┬────┘ └───────┘ └───────┘ │
│ │ │
│ └──────────────────────────────────────────► Kubernetes API / etcd
│ (存储 IP 池、节点路由、策略等)
└─────────────────────────────────────────────────────────────┘3. Pod IP 是如何分配的
3.1 IP 池(IPPool)
Calico 通过 IPPool 定义集群可用的 Pod IP 范围:
yaml
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: default-pool
spec:
cidr: 192.168.0.0/16
blockSize: 26
natOutgoing: true
disabled: false
nodeSelector: all()cidr:整个 Pod 网络可用的 IP 段。blockSize:每个节点预分配的 IP 块大小,/26表示每个节点最多有 64 个 IP。natOutgoing:是否对访问集群外部的流量做 SNAT。
3.2 按节点分配 IP 块(IPAM)
Calico 的 IP 地址管理(IPAM)不是每次创建 Pod 都去中心请求一个 IP,而是:
- 每个节点启动时,Felix 从 IPPool 中申请一个 IP Block(例如
/26,64 个 IP)。 - 当该节点上创建 Pod 时,Calico CNI 直接从本节点的 IP Block 中分配一个未使用的 IP。
- 这个 IP Block 信息会记录到 Calico 数据存储中。
text
IPPool: 192.168.0.0/16
│
├── Node 1 分配 192.168.1.0/26(64 个 IP)
├── Node 2 分配 192.168.1.64/26
├── Node 3 分配 192.168.1.128/26
└── ...3.3 为什么按块分配?
- 减少中心存储压力:每个节点管理自己的 IP 块,不需要每次创建 Pod 都访问 etcd/API。
- 便于路由聚合:节点只需要对外宣告自己的
/26网段,而不是每个 Pod IP 都宣告一条路由。
4. Pod 网络接口是如何创建的
当 kubelet 创建一个 Pod 时,会调用 Calico 的 CNI 插件。整个过程大致如下:
4.1 CNI 调用流程
text
kubelet
│
▼
CNI 配置 /etc/cni/net.d/10-calico.conflist
│
▼
calico CNI 插件
│
├─ 调用 Calico IPAM 分配 Pod IP
│
├─ 在 Pod 网络命名空间创建 veth pair
│ ├─ 一端在 Pod 内:eth0(Pod 的网卡)
│ └─ 一端在主机上:calixxxxx
│
├─ 配置 Pod 内路由、默认网关
│
└─ 更新主机路由表4.2 veth pair 是什么
veth pair 是 Linux 提供的一种虚拟网络设备,总是成对出现:
text
Pod 网络命名空间 主机网络命名空间
┌─────────────┐ ┌─────────────┐
│ eth0 │◄────────────────►│ calixxxxx │
│ 10.244.1.10 │ │ (no IP) │
└─────────────┘ └─────────────┘- 一端放在 Pod 里,作为 Pod 的
eth0。 - 另一端放在主机上,名称通常是
cali<hash>。 - 数据包从一端进入,必然从另一端出来。
4.3 Pod 内的网络视图
进入一个 Pod,可以看到:
bash
kubectl exec -it <pod-name> -- ip addr
# 1: lo: <LOOPBACK> ...
# 2: eth0@if123: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
# inet 192.168.1.5/32 scope global eth0bash
kubectl exec -it <pod-name> -- ip route
# default via 169.254.1.1 dev eth0
# 169.254.1.1 dev eth0 scope link注意:
- Pod IP 通常是
/32主机路由。 - 默认网关
169.254.1.1是一个链路本地地址,实际指向主机端的 veth 接口。
4.4 主机上的路由
在节点上查看路由:
bash
ip route | grep 192.168.1
# 192.168.1.5 dev calixxxxx scope link
# 192.168.1.0/26 via <node1-ip> proto bird192.168.1.5 dev calixxxxx:本节点上的 Pod 通过对应的 veth 接口访问。192.168.1.0/26 via <node1-ip>:其他节点上的 Pod 网段通过 BGP 学习到的路由访问。
5. Pod 之间如何通信
5.1 同节点 Pod 通信
假设 Node 1 上有两个 Pod:
text
Pod A: 192.168.1.5
Pod B: 192.168.1.6数据包路径:
text
Pod A
│
│ eth0 发出,目的 192.168.1.6
▼
caliA(主机端 veth)
│
▼
主机路由表:192.168.1.6 dev caliB
│
▼
caliB(主机端 veth)
│
▼
Pod B 的 eth0关键点:
- 数据包不需要离开 Node 1。
- 主机内核根据路由表直接转发。
- 不需要 NAT,Pod 看到对方的真实 Pod IP。
5.2 跨节点 Pod 通信(BGP 模式)
假设:
text
Pod A: 192.168.1.5 在 Node 1 上
Pod B: 192.168.2.10 在 Node 2 上BGP 路由宣告
Calico 默认在每个节点上运行 BIRD,节点之间通过 BGP 交换路由:
text
Node 1 宣告:192.168.1.0/26 可达
Node 2 宣告:192.168.2.0/26 可达BGP 连接方式:
- Full Mesh:所有节点两两建立 BGP 邻居,适合小规模集群。
- Route Reflector(RR):指定一些节点作为路由反射器,其他节点只与 RR 建立连接,适合大规模集群。
数据包路径
text
Pod A (192.168.1.5)
│
▼
caliA
│
▼
Node 1 路由表:192.168.2.0/26 via Node 2 IP
│
▼
Node 1 物理网卡 eth0
│
│ 二层/三层网络转发到 Node 2
▼
Node 2 物理网卡 eth0
│
▼
Node 2 路由表:192.168.2.10 dev caliB
│
▼
caliB
│
▼
Pod B (192.168.2.10)关键点:
- 底层网络需要支持 Pod IP 网段的路由(通常是三层可达)。
- 不需要封装解封装,性能接近原生网络。
- 数据包中的源 IP 和目的 IP 都是真实 Pod IP,没有 NAT。
5.3 跨节点 Pod 通信(IPIP/VXLAN 封装模式)
如果底层网络不支持直接路由 Pod IP(例如公有云 VPC 只允许节点 IP 通信),Calico 可以使用 IPIP 或 VXLAN 封装。
IPIP 模式
将原始 Pod 数据包再封装一层 IP 包,外层 IP 是节点 IP:
text
原始包:
源:192.168.1.5
目的:192.168.2.10
封装后:
外层源:Node 1 IP
外层目的:Node 2 IP
内层:原始 Pod 数据包VXLAN 模式
类似 IPIP,但使用 UDP 封装,兼容性更好,支持多播网络。
数据包路径
text
Pod A
│
▼
caliA
│
▼
Node 1 路由表:192.168.2.0/26 via Node 2 IP,走 tunl0
│
▼
tunl0 / vxlan.calico 接口
│
▼
封装:外层 Node 1 IP -> Node 2 IP
│
▼
Node 2 解封装
│
▼
Node 2 路由表:192.168.2.10 dev caliB
│
▼
Pod B关键点:
- 封装模式适合底层网络不开放 Pod IP 路由的场景。
- 会有少量封装开销(CPU 和 MTU)。
- 不需要 NAT,Pod 仍然看到真实 Pod IP。
6. Calico 的三种网络模式对比
| 模式 | 路由方式 | 是否需要底层网络支持 Pod IP | 性能 | 适用场景 |
|---|---|---|---|---|
| BGP(无封装) | 直接路由 | ✅ 需要 | 最好 | 裸金属、私有云、支持自定义路由的环境 |
| IPIP | IP in IP 封装 | ❌ 不需要 | 较好 | 公有云 VPC、底层只允许节点 IP |
| VXLAN | UDP 封装 | ❌ 不需要 | 较好 | 对 IPIP 不支持的环境、需要多播 |
7. Pod 访问 Service 时的路径
前面章节讲过 Service 的 ClusterIP 是虚拟 IP,由 kube-proxy 配置 iptables/ipvs 规则。结合 Calico 后,完整路径如下:
text
Pod A
│
▼
访问 web-service:80(DNS 解析为 10.96.123.45)
│
▼
iptables/ipvs 规则命中
│
▼
DNAT 到 Pod B IP:targetPort(例如 192.168.2.10:8080)
│
▼
Calico 路由转发到 Node 2
│
▼
Pod B所以:
- Service 层决定流量发给哪个 Pod(负载均衡)。
- Calico 层决定如何把这个数据包送到那个 Pod(路由/封装)。
8. NetworkPolicy 是如何生效的
Calico 不仅负责网络连通性,还负责网络策略(NetworkPolicy)的执行。
Felix 生成 iptables/eBPF 规则
Felix 监听 Kubernetes NetworkPolicy 和 Calico 全局网络策略,生成:
- 入站规则:允许哪些源 IP/端口访问 Pod。
- 出站规则:允许 Pod 访问哪些目的 IP/端口。
这些规则通过 iptables(传统模式)或 eBPF(高性能模式)下发到主机上。
策略执行位置
策略在数据包进入/离开 Pod 的 veth 接口时执行:
text
外部流量
│
▼
主机 iptables/eBPF 钩子
│
▼
caliX 接口
│
▼
Pod eth09. 关键命令与排障
查看 Calico 节点状态
bash
calicoctl node status查看 IP 池
bash
calicoctl get ippool -o wide查看节点分配的路由块
bash
calicoctl get block查看节点路由表
bash
ip route查看 Pod 的 veth 接口
bash
# 在主机上
ip link show | grep cali抓包
bash
# 抓 Pod veth
/tcpdump -i calixxxxx -nn
# 抓物理网卡
tcpdump -i eth0 -nn proto 4 # IPIP 协议号是 4查看 BGP 邻居
bash
# 进入 calico-node 容器
kubectl exec -it -n kube-system calico-node-xxxxx -- birdcl show protocol
kubectl exec -it -n kube-system calico-node-xxxxx -- birdcl show route10. 总结
| 问题 | 答案 |
|---|---|
| Pod IP 是谁分配的? | Calico IPAM,按节点分配 /26 大小的 IP Block |
| Pod IP 绑定在哪里? | 绑定在 Pod 内的 eth0 网卡上,通过 veth pair 与主机相连 |
| 同节点 Pod 如何通信? | 通过主机路由表直接转发,不离开节点 |
| 跨节点 Pod 如何通信? | BGP 直接路由,或 IPIP/VXLAN 封装 |
| 是否需要 NAT? | Pod 之间不需要 NAT,直接看到真实 Pod IP |
| Service 和 Calico 的分工? | Service 负责负载均衡,Calico 负责路由送达 |
| 网络策略怎么生效? | Felix 生成 iptables/eBPF 规则,在 veth 接口处执行 |
11. 本章练习
- 查看你集群中的 Calico IPPool 和节点分配的 IP Block。
- 进入一个 Pod,查看它的
eth0和路由表。 - 在节点上查看 Pod 对应的
cali接口和路由。 - 测试同节点和跨节点 Pod 通信,并用
tcpdump观察数据包路径。 - 对比 BGP 模式和 IPIP 模式下数据包的差异。