模式介绍
项目文档:https://docs.tigera.io/calico/latest/networking/configuring/vxlan-ipip
IPIP (IP-in-IP) 是一种隧道封装技术,将一个 IP 包封装在另一个 IP 包中传输。
使用场景
| 场景 | 是否推荐 | 说明 |
|---|---|---|
| AWS 跨 VPC/子网 | ✅ | 子网边界无法路由 Pod IP,用 ipipMode: CrossSubnet |
| AWS 多 AZ 部署 | ✅ | 同 AZ 直通,跨 AZ 封装 |
| 公有云(无网络控制权) | ✅ | 无法配置底层路由,用 ipipMode: Always/CrossSubnet |
| 快速起步/POC | ✅ | 不折腾底层网络,直接用 ipipMode: Always |
| 混合网络环境 | ✅ | 部分支持/不支持 Pod IP 路由,用 ipipMode: CrossSubnet |
| 已有 BGP 基础设施 | ✅ | IPIP 天然集成 BGP |
| Azure 环境 | ❌ | Azure 不支持 IPIP 协议 → 用 VXLAN |
| 需要 IPv6 | ❌ | IPIP 仅支持 IPv4 → 用 VXLAN |
| 高性能/低延迟需求 | ❌ | 封装有开销 → 纯路由模式 |
| 网络密集型 workload | ❌ | 高吞吐场景受影响 → 纯路由或 eBPF |
| 底层网络支持 Pod IP 路由 | ❌ | 封装是多余 → ipipMode: Never + BGP peering |
| 裸金属/自建机房 | ❌ | 有完整网络控制权 → 纯路由模式 |
部署流程
通过 Kind 快速生成集群并部署 Calico IPIP 模式
bash
#!/bin/bash
set -v
# 1. Prepare NoCNI environment
cat <<EOF | HTTP_PROXY= HTTPS_PROXY= http_proxy= https_proxy= kind create cluster --name=calico-ipip --image=burlyluo/kindest:v1.27.3 -v=9 --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
disableDefaultCNI: true
nodes:
- role: control-plane
- role: worker
EOF
# 2. Remove taints
controller_node_ip=`kubectl get node -o wide --no-headers | grep -E "control-plane|bpf1" | awk -F " " '{print $6}'`
kubectl taint nodes $(kubectl get nodes -o name | grep control-plane) node-role.kubernetes.io/control-plane:NoSchedule-
kubectl get nodes -o wide
# 3. Collect startup message
controller_node_name=$(kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | grep control-plane)
if [ -n "$controller_node_name" ]; then
timeout 1 docker exec -t $controller_node_name bash -c 'cat << EOF > /root/monitor_startup.sh
#!/bin/bash
ip -ts monitor all > /root/startup_monitor.txt 2>&1
EOF
chmod +x /root/monitor_startup.sh && /root/monitor_startup.sh'
else
echo "No such controller_node!"
fi
# 4. Install CNI[Calico v3.23.2]
kubectl apply -f calico.yaml
# 5. Wait all pods ready
kubectl wait --timeout=100s --for=condition=Ready=true pods --all -A
bash
## calico.yaml
## https://gitee.com/rowan-wcni/wcni-kind/blob/master/LabasCode/calico/01-calico-ipip/calico.yaml
创建测试 Pod
实际就是 Nginx,仅用于后续互访时抓包
yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app: wluo
name: wluo
spec:
selector:
matchLabels:
app: wluo
template:
metadata:
labels:
app: wluo
spec:
containers:
- image: burlyluo/nettool:latest
name: nettoolbox
env:
- name: NETTOOL_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
securityContext:
privileged: true
查看部署结果
bash
root@network-demo:~# kubectl get pods -A -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE
default wluo-fd9cp 1/1 Running 0 27h 10.244.51.194 calico-ipip-control-plane
default wluo-nckpl 1/1 Running 0 27h 10.244.79.6 calico-ipip-worker
kube-system calico-kube-controllers-7bdccfc7d8-flvfz 1/1 Running 0 3d7h 10.244.79.2 calico-ipip-worker
kube-system calico-node-8rqcx 1/1 Running 0 3d7h 172.18.0.3 calico-ipip-control-plane
kube-system calico-node-rm4pj 1/1 Running 0 3d7h 172.18.0.2 calico-ipip-worker
kube-system coredns-5d78c9869d-cqm77 1/1 Running 0 3d7h 10.244.79.3 calico-ipip-worker
kube-system coredns-5d78c9869d-th2np 1/1 Running 0 3d7h 10.244.79.1 calico-ipip-worker
kube-system etcd-calico-ipip-control-plane 1/1 Running 0 3d7h 172.18.0.3 calico-ipip-control-plane
kube-system kube-apiserver-calico-ipip-control-plane 1/1 Running 0 3d7h 172.18.0.3 calico-ipip-control-plane
kube-system kube-controller-manager-calico-ipip-control-plane 1/1 Running 0 3d7h 172.18.0.3 calico-ipip-control-plane
kube-system kube-proxy-4fcfz 1/1 Running 0 3d7h 172.18.0.3 calico-ipip-control-plane
kube-system kube-proxy-6klbx 1/1 Running 0 3d7h 172.18.0.2 calico-ipip-worker
kube-system kube-scheduler-calico-ipip-control-plane 1/1 Running 0 3d7h 172.18.0.3 calico-ipip-control-plane
local-path-storage local-path-provisioner-6bc4bddd6b-2xgp8 1/1 Running 0 3d7h 10.244.79.4 calico-ipip-worker
验证效果
查询 Node 节点 BGP、路由表、网络设备信息
bash
## 节点信息
root@network-demo:~# kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP
calico-ipip-control-plane Ready control-plane 2d4h v1.27.3 172.18.0.3
calico-ipip-worker Ready <none> 2d4h v1.27.3 172.18.0.2
## Pod 信息
root@network-demo:~# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
wluo-fd9cp 1/1 Running 0 4h36m 10.244.51.194 calico-ipip-control-plane
wluo-nckpl 1/1 Running 0 4h36m 10.244.79.6 calico-ipip-worker
1.查询节点 BGP 信息
bash
## https://docs.tigera.io/calico/latest/networking/configuring/bgp
## 简单来说,可以将 BGP 理解为导航、IPIP 理解为车:
## BGP 分发路由信息(往哪走):
## - 节点之间同步路由信息,告诉本节点目标 Pod 网段在哪个 Node IP
## IPIP 封装数据包(发过去):
## - 实际数据包原/目的都是 Pod IP,底层路由器不认识 Pod 网段
## - IPIP 把数据包封装后在 Node IP 间传输
## 可以看出,与对端节点形成了 BGP Mesh
root@network-demo:~# docker exec calico-ipip-control-plane calicoctl node status
Calico process is running.
IPv4 BGP status
+--------------+-------------------+-------+------------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
+--------------+-------------------+-------+------------+-------------+
| 172.18.0.2 | node-to-node mesh | up | 2026-03-20 | Established |
+--------------+-------------------+-------+------------+-------------+
## 179 端口的 bird 就是 BGP 的守护进程,两台节点间通过 TCP 连接形成交互的路由数据库
root@network-demo:~# docker exec calico-ipip-control-plane ss -anp | awk '/bird/ && /179/'
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp LISTEN 0 8 0.0.0.0:179 0.0.0.0:* users:(("bird",pid=1486,fd=7))
tcp ESTAB 0 0 172.18.0.3:50005 172.18.0.2:179 users:(("bird",pid=1486,fd=8))
## 显示 bird 运行了哪些协议
root@network-demo:~# docker exec calico-ipip-control-plane birdc -s /run/calico/bird.ctl show protocol
BIRD v0.3.3+birdv1.6.8 ready.
name proto table state since info
static1 Static master up 2026-03-20 # 静态路由协议
kernel1 Kernel master up 2026-03-20 # 内核路由同步
device1 Device master up 2026-03-20 # 设备监控
direct1 Direct master up 2026-03-20 # 直连路由
Mesh_172_18_0_2 BGP master up 2026-03-20 Established # BGP 邻居已建立
## 查看 bird 路由表(不是内核路由表)
## 注:这里是 bird 自己维护的路由表,bird 内部路由表
root@network-demo:~# docker exec calico-ipip-control-plane birdc -s /run/calico/bird.ctl show route
BIRD v0.3.3+birdv1.6.8 ready.
0.0.0.0/0 via 172.18.0.1 on eth0 [kernel1 2026-03-20] * (10)
# 可以看出,访问 79.6 时走了这条 BGP 路由
10.244.79.0/26 via 172.18.0.2 on eth0 [Mesh_172_18_0_2 2026-03-20] * (100/0) [i]
10.244.51.194/32 dev cali2dc361fe88d [kernel1 07:12:41] * (10)
172.18.0.0/16 dev eth0 [direct1 2026-03-20] * (240)
10.244.51.192/26 blackhole [static1 2026-03-20] * (200)
10.244.51.192/32 dev tunl0 [direct1 2026-03-20] * (240)
## 显示内核路由表由 bird 添加或管理的所有路由
## Bird 将内部路由表中最优路由下发到内核的结果
root@network-demo:~# docker exec calico-ipip-control-plane ip route show proto bird
blackhole 10.244.51.192/26
10.244.79.0/26 via 172.18.0.2 dev tunl0 onlink
2.查询 Node 路由
这一步没啥意义,上一步的
ip route show proto bird已经看了,仅作为路由信息补充。
bash
root@network-demo:~# docker exec calico-ipip-control-plane route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.18.0.1 0.0.0.0 UG 0 0 0 eth0
10.244.51.192 0.0.0.0 255.255.255.192 U 0 0 0 *
10.244.51.194 0.0.0.0 255.255.255.255 UH 0 0 0 cali2dc361fe88d
10.244.79.0 172.18.0.2 255.255.255.192 UG 0 0 0 tunl0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
root@network-demo:~# docker exec calico-ipip-control-plane ip route show
default via 172.18.0.1 dev eth0
blackhole 10.244.51.192/26 proto bird
10.244.51.194 dev cali2dc361fe88d scope link
## 去 10.244.79.0/26 的流量使用 Calico bird 下发,通过 IPIP 隧道 tunl0 发到 172.18.0.2
10.244.79.0/26 via 172.18.0.2 dev tunl0 proto bird onlink
## 去 172.18.0.0/16 的流量使用内核生成的直连路由,无需网关。走主机 eth0
172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.3
3.查询 tunl0 设备
Pod 互访时 tunl0 设备进行 ipip 封装后,根据路由规则,使用 node eth0 网卡传输。
简单来讲 calico.tunl0 与 flannel.ipip 本质上是同一类东西,都是做 IPIP 封装/解封的。
bash
2: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
inet 10.244.51.192/32 scope global tunl0
valid_lft forever preferred_lft forever
root@network-demo:~# docker exec calico-ipip-control-plane ip -d link show tunl0
2: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0 promiscuity 0 minmtu 0 maxmtu 0
ipip any remote any local any ttl inherit nopmtudisc addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
查询 Pod 路由、ARP、网络设备信息
验证路由规则、calico 生成的网络设备开启 proxy_arp 后的效果
1.查询 Pod 网卡信息
bash
## Pod 所处网段子网掩码为 32,即该网段只存在 Pod IP
## /32 意味着 Pod 是"孤岛",任何其他 IP 都不在本地网段
root@network-demo:~# kubectl exec wluo-fd9cp -- ip address show eth0
10.244.51.194/32
c2:58:3e:3d:f7:1c
2.查询 Pod 路由
容器默认网关的 IP 169.254.1.1 是什么其实无所谓。因为通过
scope link配置后,这条路由被标记为本地链路路由,通信走的是二层转发,依赖的是 MAC 地址而非 IP 地址。至于为什么还需要一个 IP 地址,详见下面衍生问题回答。
bash
## 结合 Pod 网卡信息发现,Pod IP 与网关地址并不在同一网段中,所以任何目标都走默认网关
root@network-demo:~# kubectl exec wluo-fd9cp -- ip route show
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link
root@network-demo:~# kubectl exec wluo-fd9cp -- route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 169.254.1.1 0.0.0.0 UG 0 0 0 eth0
169.254.1.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0
## 验证 cali2dc361fe88d 网卡是否开启 proxy_arp
root@network-demo:~# docker exec calico-ipip-control-plane cat /proc/sys/net/ipv4/conf/cali2dc361fe88d/proxy_arp
1
3.查询 Pod ARP 表
bash
## 邻居/ARP 表(两条命令都是在查这个,推荐使用 ip 查询)
## 通过 ARP 获取 169.254.1.1 的 MAC:ee:ee:ee:ee:ee:ee
## 请求给网关时,其实是 calixxx 网口通过 proxy_arp 响应的(calixxx 网口假装自己是网关)
## 所以 169 这个 IP 是什么其实无所谓,
root@network-demo:~# kubectl exec wluo-fd9cp -- ip neighbor show
10.244.51.192 dev eth0 lladdr ee:ee:ee:ee:ee:ee STALE
169.254.1.1 dev eth0 lladdr ee:ee:ee:ee:ee:ee REACHABLE
root@network-demo:~# kubectl exec wluo-fd9cp -- arp -n
Address HWtype HWaddress Flags Mask Iface
10.244.51.192 ether ee:ee:ee:ee:ee:ee C eth0
169.254.1.1 ether ee:ee:ee:ee:ee:ee C eth0
4.查询 Pod Veth Pair 设备
可以看到 Node calixxxx 网络设备并没有 IP 地址,所以 Pod 内网关 IP 169.254.1.1 是什么才无所谓。
bash
## 查询 cali2dc361fe88d 网卡信息(验证 Mac 地址是否正确)
root@network-demo:~# docker exec calico-ipip-control-plane ip address show cali2dc361fe88d
7: cali2dc361fe88d@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netns cni-a5b10eb8-6a8c-f994-5de8-9c130c368868
inet6 fe80::ecee:eeff:feee:eeee/64 scope link
valid_lft forever preferred_lft forever
root@network-demo:~# docker exec calico-ipip-control-plane ip -d link show cali2dc361fe88d
7: cali2dc361fe88d@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netns cni-a5b10eb8-6a8c-f994-5de8-9c130c368868 promiscuity 0 minmtu 68 maxmtu 65535
veth addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
5.延伸问题
-
每个 Pod 都通过 veth pair 对应一个 cali 网卡设备,他们的 mac 地址都是 ee:ee:ee 为什么不会冲突?
- 实际上,在一个广播域中 mac 地址唯一即可。每个 Pod IP 都是 /32,所以 mac 地址不可能冲突。
-
通过 veth pair 连接 Pod eth0 与 Node calixxx 网卡,为什么还要使用 mac 地址?
- 非 点对点(P2P)设备必须符合 TCP/IP 约束。
bash## 可以在 '查询 Pod Veth Pair 设备' 步骤中看到:cali veth 设备声明自己是 BROADCAST,MULTICAST 类型的以太网设备
Pod 网卡处抓包
bash
## Pod 互访
root@network-demo:~# kubectl exec wluo-fd9cp -- curl -s 10.244.79.6
PodName: wluo-nckpl | PodIP: eth0 10.244.79.6/32


Node 网卡处抓包
验证 tunl0 ipip 封装效果
