Skip to content

Calico 深度解析:Pod IP 如何实现、Pod 之间如何通信

本章以 Calico 为例,系统讲解 Kubernetes 中 Pod IP 的分配机制、Pod 网络接口的创建过程,以及同节点/跨节点 Pod 通信的完整数据包路径。


1. Kubernetes 对容器网络的要求

在深入 Calico 之前,先明确 Kubernetes 网络模型对 Pod 通信的三个基本要求:

  1. 所有 Pod 之间可以不通过 NAT 直接通信
  2. 节点上的 agent(kubelet)可以与所有 Pod 直接通信
  3. Pod 看到的自己的 IP 与其他 Pod 看到的它的 IP 相同(即没有端口映射 / NAT 隐藏)。

这意味着每个 Pod 必须有一个真实的、在集群范围内可路由的 IP


2. Calico 架构概览

Calico 是一个开源的网络和网络策略引擎,支持 Kubernetes、OpenShift 等编排平台。它的核心组件包括:

组件作用
CNI Plugin (calico)在 Pod 创建/删除时被调用,负责配置 Pod 网络接口和路由
Felix运行在每个节点上的 agent,负责路由、ACL(NetworkPolicy)、IPAM 等
BIRDBGP 路由守护进程,负责在节点之间交换路由信息
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,而是:

  1. 每个节点启动时,Felix 从 IPPool 中申请一个 IP Block(例如 /26,64 个 IP)。
  2. 当该节点上创建 Pod 时,Calico CNI 直接从本节点的 IP Block 中分配一个未使用的 IP。
  3. 这个 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 eth0
bash
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 bird
  • 192.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 可以使用 IPIPVXLAN 封装。

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(无封装)直接路由✅ 需要最好裸金属、私有云、支持自定义路由的环境
IPIPIP in IP 封装❌ 不需要较好公有云 VPC、底层只允许节点 IP
VXLANUDP 封装❌ 不需要较好对 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 eth0

9. 关键命令与排障

查看 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 route

10. 总结

问题答案
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. 本章练习

  1. 查看你集群中的 Calico IPPool 和节点分配的 IP Block。
  2. 进入一个 Pod,查看它的 eth0 和路由表。
  3. 在节点上查看 Pod 对应的 cali 接口和路由。
  4. 测试同节点和跨节点 Pod 通信,并用 tcpdump 观察数据包路径。
  5. 对比 BGP 模式和 IPIP 模式下数据包的差异。