Kubernetes跨集群通信

最近遇到的一个问题:需要把老集群的部分服务迁移到新集群,要求新老集群里面的Pod可以互相通信调用。踩了几天的坑,磕磕绊绊总算是完成了。

公司的Kubernetes环境存在新老两套集群:

  • 老集群:自己购买云ECS服务器,手动搭建的Kubernetes集群,使用Calico网络插件
  • 新集群:直接使用云托管的Kubernetes服务,一键创建即用

为了完成最小改动代码迁移任务,两个集群的服务都注册到同一个Nacos注册中心,需要实现跨集群的服务调用。

2.1 老集群架构(自建K8s + Calico

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
┌─────────────────────────────────────────────────────────────┐
│ 老集群(自建Kubernetes) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 物理层:云ECS实例 │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Node网络:10.0.0.0/22(VPC默认子网) │ │
│ │ │ │
│ │ Node列表: │ │
│ │ • 10.0.0.3 (master01) │ │
│ │ • 10.0.0.19 (web02/docker-mgr) │ │
│ │ • 10.0.0.22 (web服务器03) │ │
│ │ • 其他节点... │ │
│ └────────────────────────────────────────────────────┘ │
│ ↓ │
│ Overlay层:Calico IPIP网络 │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Pod网络:172.26.0.0/16(虚拟网络) │ │
│ │ │ │
│ │ 特点: │ │
│ │ • 使用IPIP隧道(tunl0)进行跨节点通信 │ │
│ │ • 每个Node分配一个/26子网(64个IP) │ │
│ │ • Pod IP对VPC不可见,只在集群内部路由 │ │
│ │ │ │
│ │ 示例Pod: │ │
│ │ • 172.26.139.114:9400 → 注册到Nacos │ │
│ │ • 172.26.95.0/26 → 分配给Node 10.0.0.19 │ │
│ │ • 172.26.241.64/26 → 分配给Node 10.0.0.3 │ │
│ └────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

关键点:

  • Pod IP(172.26.x.x)是Overlay网络地址,不在VPC路由表中
  • Pod之间通过IPIP隧道通信
  • 服务注册到Nacos时使用Pod IP

2.2 新集群架构(云托管K8s)

云托管K8s在创建时会自动创建三个子网:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌─────────────────────────────────────────────────────────────┐
│ 新集群(云托管K8s) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 自动创建的子网架构: │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 1. Node子网:10.0.40.0/22 │ │
│ │ 路由表:k8s-axxxxxxxb-node-rt-0 │ │
│ │ 用途:K8s节点网络 │ │
│ │ Node示例:10.0.40.15 │ │
│ └────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 2. Pod子网:10.0.32.0/21(重点) │ │
│ │ 路由表:k8s-axxxxxxxb-pod-rt-0 │ │
│ │ 特点: │ │
│ │ • Pod IP直接从VPC分配 │ │
│ │ • 对VPC可见,可直接路由 │ │
│ │ • 使用VPC-CNI插件 │ │
│ │ │ │
│ │ 示例Pod: │ │
│ │ • 10.0.32.21:9400 → 注册到Nacos │ │
│ └────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 3. NAT子网:10.0.44.0/23 │ │
│ │ 路由表:k8s-axxxxxxxb-nat-rt-0 │ │
│ │ 用途:Pod访问公网的出口 │ │
│ └────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

关键点:

  • Pod IP(10.0.32.x)是VPC内的真实IP
  • 无需Overlay网络,性能更好

三、问题分析

3.1 初始状态

当服务部署到两个集群后,Nacos服务列表呈现如下状态:

image-20260122164204232

3.2 通信测试结果

通信方向 结果 说明
新集群Node → 老集群Node 都在VPC内,可直接通信
老集群Node → 新集群Node 都在VPC内,可直接通信
老集群Pod → 新集群Pod 10.0.32.x在VPC路由表中可识别
新集群Pod → 老集群Pod 不通 172.26.x不在VPC路由表中

3.3 问题根源分析

为什么老集群Pod可以访问新集群Pod?

1
2
3
4
5
6
7
8
9
10
11
12
13
老集群Pod (172.26.139.114) 发起请求

目标地址:10.0.32.21

老集群Node查询本地路由表

10.0.32.21 不在本地,走默认路由到 VPC 网关

VPC 识别 10.0.32.0/21 是合法子网(Pod子网)

路由到新集群Pod子网

成功到达目标Pod

为什么新集群Pod无法访问老集群Pod?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
新集群Pod (10.0.32.21) 发起请求

目标地址:172.26.139.114

查询Pod子网路由表 (k8s-axxxxxxxb-pod-rt-0)

路由表内容:
- local → local(VPC内部流量)
- 0.0.0.0/0 → NAT网关(默认路由)
- 没有 172.26.0.0/16 的路由!

匹配默认路由,流量发送到 NAT 网关

NAT网关用于公网访问,无法到达集群内部地址

四、解决方案

4.1 方案设计思路

核心思路:让新集群知道如何到达老集群的Pod网络(172.26.0.0/16)

需要解决两个问题:

  1. 路由问题:告诉新集群Pod子网,172.26.0.0/16的流量该发到哪里
  2. 信任问题:让老集群的Calico接受来自新集群的请求

4.2 步骤一:配置Pod子网路由表

在云控制台操作:

  1. 进入 VPC → 路由表
  2. 找到 k8s-axxxxxxxb-pod-rt-0
  3. 添加路由策略:
1
2
3
4
5
目的端:172.26.0.0/16
下一跳类型:云主机
下一跳:i-bddddddd5 (web服务器02 / 10.0.0.19)
路由类型:静态
备注:新集群pod通讯老集群pod

配置后的路由表:

1
2
3
local          → local                     # VPC内部流量
0.0.0.0/0 → NAT网关 # 公网流量
172.26.0.0/1610.0.0.19(新增) # 老集群Pod流量

4.3 步骤二:配置中转节点NAT规则

为什么需要NAT?因为Calico会验证源IP,只信任本集群Node的IP。需要将新集群的请求伪装成老集群Node发出的。

在 10.0.0.19 节点上执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 1. 开启IP转发
echo 1 > /proc/sys/net/ipv4/ip_forward
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p

# 2. 添加NAT规则
iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -d 172.26.0.0/16 -j MASQUERADE

# 3. 创建systemd服务(确保重启后规则仍然生效)
cat > /etc/systemd/system/custom-nat.service << 'EOF'
[Unit]
Description=Custom NAT Rules for Cross-Cluster Communication
After=network.target calico-node.service
Before=kubelet.service

[Service]
Type=oneshot
ExecStart=/usr/sbin/iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -d 172.26.0.0/16 -j MASQUERADE
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

# 4. 启用服务
systemctl daemon-reload
systemctl enable custom-nat.service
systemctl start custom-nat.service

五、流量路径详解

5.1 配置后的完整通信流程

去程路径(新集群Pod → 老集群Pod)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
┌─────────────────────────────────────────────────────────────┐
│ 新集群Pod → 老集群Pod 请求流程 │
├─────────────────────────────────────────────────────────────┤
│ │
1. 新集群Pod发起请求 │
│ ┌────────────────────────────────────────────────┐ │
│ │ Pod: 10.0.32.21 │ │
│ │ 目标: 172.26.139.114:9400 │ │
│ └──────────────────┬─────────────────────────────┘ │
│ ↓ │
2. 查询Pod子网路由表 │
│ ┌────────────────────────────────────────────────┐ │
│ │ 匹配路由规则: │ │
│ │ 172.26.0.0/1610.0.0.19 │ │
│ └──────────────────┬─────────────────────────────┘ │
│ ↓ │
3. VPC路由到中转节点 (10.0.0.19) │
│ ┌────────────────────────────────────────────────┐ │
│ │ 数据包到达10.0.0.19的eth0网卡 │ │
│ │ 源IP: 10.0.32.21 │ │
│ │ 目标IP: 172.26.139.114 │ │
│ └──────────────────┬─────────────────────────────┘ │
│ ↓ │
4. NAT地址伪装(关键步骤) │
│ ┌────────────────────────────────────────────────┐ │
│ │ iptables规则匹配并执行MASQUERADE │ │
│ │ 源IP: 10.0.32.2110.0.0.19(伪装) │ │
│ │ 目标IP: 172.26.139.114(不变) │ │
│ │ NAT表记录连接信息用于回程 │ │
│ └──────────────────┬─────────────────────────────┘ │
│ ↓ │
5. Calico路由转发 │
│ ┌────────────────────────────────────────────────┐ │
│ │ 10.0.0.19查询本地Calico路由表 │ │
│ │ 172.26.139.64/2610.0.0.36 via tunl0 │ │
│ │ 通过IPIP隧道转发 │ │
│ └──────────────────┬─────────────────────────────┘ │
│ ↓ │
6. IPIP隧道封装传输 │
│ ┌────────────────────────────────────────────────┐ │
│ │ 外层: 10.0.0.1910.0.0.36 │ │
│ │ 内层: 10.0.0.19172.26.139.114:9400 │ │
│ └──────────────────┬─────────────────────────────┘ │
│ ↓ │
7. 到达目标Pod并处理请求 │
│ ┌────────────────────────────────────────────────┐ │
│ │ Pod: 172.26.139.114 │ │
│ │ 看到的源IP: 10.0.0.19(老集群Node IP) │ │
│ │ 处理业务请求,准备返回响应 │ │
│ └────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

回程路径(老集群Pod → 新集群Pod 响应)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
┌─────────────────────────────────────────────────────────────┐
│ 老集群Pod → 新集群Pod 响应流程 │
├─────────────────────────────────────────────────────────────┤
│ │
8. 老集群Pod发送响应 │
│ ┌────────────────────────────────────────────────┐ │
│ │ Pod: 172.26.139.114 │ │
│ │ 响应目标: 10.0.0.19(看到的源IP) │ │
│ │ 实际最终目标: 10.0.32.21(NAT会处理) │ │
│ └──────────────────┬─────────────────────────────┘ │
│ ↓ │
9. 通过Calico路由返回 │
│ ┌────────────────────────────────────────────────┐ │
│ │ 172.26.139.11410.0.0.36(本地Node) │ │
│ │ 10.0.0.36 查询路由: │ │
│ │ 目标10.0.0.19是本集群Node,直接路由 │ │
│ └──────────────────┬─────────────────────────────┘ │
│ ↓ │
10. IPIP隧道封装返回 │
│ ┌────────────────────────────────────────────────┐ │
│ │ 外层: 10.0.0.3610.0.0.19 │ │
│ │ 内层: 172.26.139.11410.0.0.19 │ │
│ └──────────────────┬─────────────────────────────┘ │
│ ↓ │
11. 中转节点NAT反向转换(关键步骤) │
│ ┌────────────────────────────────────────────────┐ │
│ │ 10.0.0.19收到响应包 │ │
│ │ 查询NAT连接跟踪表 │ │
│ │ 找到原始连接:10.0.32.2110.0.0.19 │ │
│ │ 执行反向NAT: │ │
│ │ 源IP: 172.26.139.114(保持不变) │ │
│ │ 目标IP: 10.0.0.1910.0.32.21(还原) │ │
│ └──────────────────┬─────────────────────────────┘ │
│ ↓ │
12. VPC路由转发回新集群 │
│ ┌────────────────────────────────────────────────┐ │
│ │ 10.0.0.19查询路由表 │ │
│ │ 目标10.0.32.21属于新集群Pod子网 │ │
│ │ 通过VPC路由转发 │ │
│ └──────────────────┬─────────────────────────────┘ │
│ ↓ │
13. 响应到达新集群Pod │
│ ┌────────────────────────────────────────────────┐ │
│ │ Pod: 10.0.32.21 │ │
│ │ 收到响应:源IP显示为172.26.139.114 │ │
│ │ 完成一次完整的跨集群通信 │ │
│ └────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

连接跟踪机制说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
NAT连接跟踪表(conntrack)工作原理:

初始请求建立连接:
┌──────────────────────────────────────┐
│ 原始连接: │
10.0.32.21:45678172.26.139.114:9400
│ │
│ NAT后连接: │
10.0.0.19:52341172.26.139.114:9400
│ │
└──────────────────────────────────────┘

响应包自动反向NAT:
┌──────────────────────────────────────┐
│ 收到响应: │
172.26.139.114:940010.0.0.19:52341
│ │
│ 查表反向转换: │
172.26.139.114:940010.0.32.21:45678
│ │
│ 转发到原始请求方 │
└──────────────────────────────────────┘

5.2 关键技术点解析

NAT规则详解

1
iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -d 172.26.0.0/16 -j MASQUERADE

参数说明:

  • -t nat:操作NAT表
  • -A POSTROUTING:在POSTROUTING链添加规则(数据包离开前的最后处理)
  • -s 10.0.0.0/8:源地址匹配(包含新集群的10.0.32.x)
  • -d 172.26.0.0/16:目标地址匹配(老集群Pod网络)
  • -j MASQUERADE:动态源地址伪装(自动使用出口网卡IP)

为什么选择10.0.0.19作为中转节点?

  1. 稳定性:该节点是老集群的一部分,Calico路由完整
  2. 信任关系:作为老集群Node,天然被Calico信任
  3. 网络位置:在VPC内,新集群可以直接访问

七、方案优缺点

优点

  1. 最小化改动:不需要修改应用代码,对业务透明
  2. 配置简单:只需配置路由表和NAT规则

缺点

  1. 单点依赖:依赖中转节点,存在单点故障风险
  2. 扩展性:流量都经过中转节点,可能成为瓶颈

改进建议

  1. 配置多个中转节点,这个需要子网路由表那边配置相同目的端,不同的下一跳云主机

    image-20260122163759231


Kubernetes跨集群通信
https://lililib.github.io/Kubernetes跨集群通信方案/
作者
煨酒小童
发布于
2026年1月22日
许可协议