一、Calico 纯三层网络架构核心原理
Calico 是一款面向容器与虚拟机的纯三层(Layer 3)SDN 网络解决方案。与 Flannel(VXLAN/UDP)等覆盖网络(Overlay Network)不同,Calico 默认不使用额外封包隧道,而是直接把每个宿主机视作一台分布式虚拟路由器(vRouter),将 Pod 视为三层网络终端,借助标准 BGP(Border Gateway Protocol) 协议在集群节点或物理核心交换机之间自动宣告与同步路由,实现跨主机裸路由直通。
+--------------------------------------------------------------------------+
| Pod A (Node 1) |
| IP: 192.168.166.132/32 |
| Default GW: 169.254.1.1 (Dummy) |
+--------------------------------------------------------------------------+
▲
│ veth pair (Proxy ARP)
▼
+--------------------------------------------------------------------------+
| Node 1 (vRouter / Felix) |
| Kernel Route: 192.168.166.132 dev caliXXXXXXXX |
| Remote Route: 192.168.166.133 via Node 2 (BGP / BIRD) |
+--------------------------------------------------------------------------+
▲
│ 物理二/三层交换机网络 (0 封装开销)
▼
+--------------------------------------------------------------------------+
| Node 2 (vRouter / Felix) |
+--------------------------------------------------------------------------+
主流 K8s CNI 网络插件架构对比
| 网络方案 | 网络模式 | 封装技术 | 封包开销 (MTU 损耗) | 性能水平 | 适用场景 |
|---|---|---|---|---|---|
| Calico (BGP 直通) | 纯三层无封包 | 无封装(纯路由) | 0 损耗(标准 MTU 1500) | 接近线速(最高) | 自建机房、支持二层或路由宣告的大中型 IDC |
| Calico (IPIP 模式) | 三层隧道 | IP-in-IP (封包头部 20B) | 损耗 20 字节 (MTU 1480) | 中等偏高 | 跨网段、公有云等不支持三层 BGP 路由广播的环境 |
| Flannel (VXLAN) | 二层 Overlay 隧道 | UDP + VXLAN (50B) | 损耗 50 字节 (MTU 1450) | 中等 | 中小型集群、开箱即用、无三层路由控制权限 |
| Cilium (eBPF) | eBPF + 路由/VXLAN | 可选直通 / Geneve | 损耗低至 0 | 极高(突破 iptables 瓶颈) | 现代内核高负载大集群、深度可观测性与安全防护 |
二、Calico 容器单机网络模型深入解构
我们通过运行一个轻量测试 Pod,从容器网络命名空间到宿主机内核路由层层透视。
1. 部署测试 Pod 实例
# busybox.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
nodeSelector:
kubernetes.io/hostname: node1
containers:
- name: busybox
image: busybox:1.28.4
command: ["sleep", "3600"]
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: 100m
memory: 100Mi
requests:
cpu: 100m
memory: 100Mi
restartPolicy: Always
在集群中部署并查看 Pod IP 与所处节点:
kubectl apply -f busybox.yaml
kubectl get pods -o wide
# 假设输出:Pod 运行于 node1,分配 IP 为 192.168.166.132
2. 容器内部的“特殊网关”:169.254.1.1 之谜
进入容器内部观察网卡与路由表:
# 登录 node1 节点查看容器网络
kubectl exec -it busybox -- ip addr
# 输出片段:
# 4: eth0@if37: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8980 ...
# inet 192.168.166.132/32 scope global eth0
kubectl exec -it busybox -- ip route
# 输出:
# default via 169.254.1.1 dev eth0
# 169.254.1.1 dev eth0 scope link
[!WARNING] 关键疑问:容器分配的是
/32的单播地址,默认网关是保留地址169.254.1.1,但整台主机中根本找不到拥有169.254.1.1的实体网卡。容器的数据包到底是如何走出去的?
查看容器内部的 ARP 邻居缓存表:
kubectl exec -it busybox -- ip neigh
# 169.254.1.1 dev eth0 lladdr ee:ee:ee:ee:ee:ee ref 1 used 0/0/0 probes 4 REACHABLE
可以看到网关的 MAC 地址被固定标记为虚设的 ee:ee:ee:ee:ee:ee。
3. 核心机制:代理 ARP(Proxy ARP)
Calico 本质上利用了 Linux 内核的 Proxy ARP 功能:
1. 当容器向默认网关 169.254.1.1 发送 ARP 广播请求时;
2. 宿主机侧的对端网卡(如 cali12d4a061371)开启了 proxy_arp 内核参数,内核检测到 ARP 跨网段请求后,直接用自己该虚拟接口的 MAC 地址(ee:ee:ee:ee:ee:ee)做出应答;
3. 容器后续发出的所有以太网数据帧,目标 MAC 都会直接封装为宿主机虚拟网卡的 MAC。
验证宿主机端网卡配置与内核参数:
# 1. 查看宿主机网卡与路由
ip addr show cali12d4a061371
# 37: cali12d4a061371@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8980 ...
# link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 1
ip route show 192.168.166.132
# 192.168.166.132 dev cali12d4a061371 scope link
# 2. 查看是否开启了代理 ARP 开关
cat /proc/sys/net/ipv4/conf/cali12d4a061371/proxy_arp
# 输出:1 (已开启)
使用 tcpdump 抓包确认内核代答与二三层流量交互:
tcpdump -i cali12d4a061371 -e -nn
# 抓包可见:所有流量在进入主机虚拟接口后,二层报头随即解开,直接进入宿主机的全局三层路由引擎进行转发!
三、动手纯手工模拟 Calico 跨主机路由网络
为了彻底掌握其底层实现,我们在没有安装任何 Calico 守护进程的两台物理机/虚拟机(Host0: 192.168.1.16 与 Host1: 192.168.1.32)上,仅依靠原生 Linux 网络工具(iproute2 + netns + sysctl)手动构建出与 Calico 一模一样的容器网络。
1. 模拟网络拓扑规划
| 节点 | 物理网卡 IP | 模拟容器命名空间 | 容器 IP 地址 | 虚拟对端网卡 (Host) |
|---|---|---|---|---|
| Host0 | 192.168.1.16 |
ns0 |
10.20.1.2/32 |
veth0 |
| Host1 | 192.168.1.32 |
ns1 |
10.20.1.3/32 |
veth0 |
2. 在 Host0 上构建虚拟路由环境
# 1. 创建 veth pair 设备与网络命名空间 ns0
ip link add veth0 type veth peer name eth0
ip netns add ns0
ip link set eth0 netns ns0
# 2. 配置容器侧的 IP、虚拟保留网关与默认路由
ip netns exec ns0 ip addr add 10.20.1.2/32 dev eth0
ip netns exec ns0 ip link set eth0 up
ip netns exec ns0 ip route add 169.254.1.1 dev eth0 scope link
ip netns exec ns0 ip route add default via 169.254.1.1 dev eth0
# 3. 启动主机端 veth0 并开启代理 ARP 机制
ip link set veth0 up
echo 1 > /proc/sys/net/ipv4/conf/veth0/proxy_arp
# 4. 配置宿主机本地 Pod 路由及跨主机目标路由
ip route add 10.20.1.2 dev veth0 scope link
ip route add 10.20.1.3 via 192.168.1.32 dev ens192
3. 在 Host1 上完成镜像配置
# 1. 创建 veth pair 设备与网络命名空间 ns1
ip link add veth0 type veth peer name eth0
ip netns add ns1
ip link set eth0 netns ns1
# 2. 配置容器网络
ip netns exec ns1 ip addr add 10.20.1.3/32 dev eth0
ip netns exec ns1 ip link set eth0 up
ip netns exec ns1 ip route add 169.254.1.1 dev eth0 scope link
ip netns exec ns1 ip route add default via 169.254.1.1 dev eth0
# 3. 启动并启用 Proxy ARP
ip link set veth0 up
echo 1 > /proc/sys/net/ipv4/conf/veth0/proxy_arp
# 4. 配置宿主机本地及对端路由
ip route add 10.20.1.3 dev veth0 scope link
ip route add 10.20.1.2 via 192.168.1.16 dev ens192
4. 连通性测试与通信链路追踪
在 Host0 的 ns0 中向 Host1 上的 ns1 发起连通性测试:
ip netns exec ns0 ping -c 3 10.20.1.3
# 输出验证:
# PING 10.20.1.3 (10.20.1.3) 56(84) bytes of data.
# 64 bytes from 10.20.1.3: icmp_seq=1 ttl=62 time=0.321 ms
# 64 bytes from 10.20.1.3: icmp_seq=2 ttl=62 time=0.312 ms
# 64 bytes from 10.20.1.3: icmp_seq=3 ttl=62 time=0.298 ms
四、Calico 数据包端到端全链路生命周期总结
当 Pod A(10.20.1.2)向跨节点的 Pod B(10.20.1.3)发送数据时,完整链路流转如下:
- 容器发出:Pod A 发起通信,查询路由表命中
default via 169.254.1.1,向链路发送针对169.254.1.1的 ARP 广播请求; - 内核代答:宿主机 Node 1 的虚拟网卡接收到广播,依靠
proxy_arp=1直接回送自身的虚拟 MAC; - 二层封装:Pod A 将数据包封包为
Dst MAC: ee:ee:ee:ee:ee:ee, Dst IP: 10.20.1.3,从虚拟接口流出; - 宿主机路由查询:数据帧到达 Node 1,内核剥离二层头进入三层 IP 层。查询宿主机路由表,命中路由规则
10.20.1.3 via Node2 IP; - 物理网络传输:Node 1 封装外层物理以太网报头,将纯原始 IP 数据包经由底层物理交换机以物理线速送达 Node 2(全流程 0 封装损耗);
- 对端宿主机分发:Node 2 接收到数据包,查询内核路由表命中本地路由
10.20.1.3 dev veth0 scope link; - 送达容器:数据包直接推送到 Pod B 的容器命名空间完成送达;
- 回程对称响应:Pod B 回包时执行完全对称的反向路径路由转发。
[!TIP] 生产选型指导:Calico 凭借“假网关 + Proxy ARP + BGP 路由广播”的精妙组合,将原本复杂的虚拟化二层广播彻底收敛到单机宿主机内部,彻底杜绝了 ARP 广播风暴,兼具极致的传输性能与透明的故障排查视野。