本文基于 Kubernetes 1.30、Cilium 1.16、Submariner 0.18、Linux 6.x 内核。 实验环境:kind v0.22、Docker 26.x、Ubuntu 22.04。
单集群终究有天花板。当业务规模增长到一定程度,你会发现一个集群已经不够用了——不是性能不够,而是架构上不允许把所有鸡蛋放在一个篮子里。多集群是生产环境的必经之路,而多集群网络则是这条路上最难啃的骨头。
本文将系统梳理三种主流的多集群网络方案:Cilium ClusterMesh(扁平网络)、Submariner(Gateway 模式)、MCS API(DNS 联邦),从原理到实现逐一拆解,最后用 kind 搭建双集群实验验证跨集群 Service 访问。
一、为什么需要多集群
在深入技术细节之前,先回答一个根本问题:为什么不能用一个大集群解决所有问题?
高可用与容灾:单集群的 control plane 是单点。etcd 故障、API Server 过载、master 节点网络分区——任何一个都能让整个集群瘫痪。多集群提供了 blast radius isolation:一个集群挂了,其他集群继续服务。
地理分布:用户在全球各地,数据中心分布在不同 Region。把所有 Pod 放在一个集群意味着跨洲际的 Pod-to-Pod 通信,延迟不可接受。多集群让每个 Region 有独立的集群,就近服务用户。
组织隔离:不同团队、不同业务线需要独立的集群。共享集群带来的问题远不止 namespace 层面的资源配额,还有 CRD 冲突、RBAC 复杂度爆炸、升级节奏不一致等。
合规要求:GDPR 要求欧洲用户数据不出境,金融行业有数据本地化要求。合规驱动下,不同地域的数据必须在本地集群处理,但服务之间又需要互通。
规模限制:Kubernetes 官方推荐的单集群上限:5000 节点、150000 个 Pod、300000 个容器。超过这个规模,etcd 性能、API Server 吞吐、控制面延迟都会成为瓶颈。
这些场景归结为一句话:多集群是不可避免的,问题只是如何让多个集群的网络互通。
二、多集群网络的三种模式
多集群网络方案众多,但从网络模型上可以归为三种模式:
模式一:扁平网络(Flat Network)
所有集群共享一个统一的 Pod CIDR 空间,Pod IP 全局唯一可路由。集群之间的 Pod 可以直接用 IP 通信,就像在同一个集群一样。
Cluster 1: PodCIDR 10.1.0.0/16
Cluster 2: PodCIDR 10.2.0.0/16
Pod A (10.1.1.5) --> 直接路由 --> Pod D (10.2.1.8)
代表方案:Cilium ClusterMesh。
优势:延迟最低,Pod-to-Pod 直连,支持跨集群 NetworkPolicy。 限制:CIDR 不能重叠,必须统一 CNI(Cilium)。
模式二:Gateway 模式
每个集群有一个或多个 Gateway 节点,跨集群流量通过 Gateway 中转,通常走加密隧道(IPsec/WireGuard)。集群内部的 CIDR 可以重叠,Gateway 层做 NAT。
Cluster 1: PodCIDR 10.244.0.0/16
Cluster 2: PodCIDR 10.244.0.0/16 (CIDR 相同!)
Pod A --> Gateway 1 --[IPsec]--> Gateway 2 --> Pod D
| |
GlobalNet NAT GlobalNet NAT
(242.1.0.0/16) (242.2.0.0/16)
代表方案:Submariner。
优势:CIDR 可以重叠(通过 GlobalNet),CNI 无关,部署简单。 限制:Gateway 节点是瓶颈和单点,隧道增加延迟。
模式三:DNS 联邦(DNS Federation)
不共享网络平面,只共享 Service 名称。通过 DNS 解析让一个集群的 Pod 能发现另一个集群的 Service,但实际流量走底层网络(需要网络本身可达)。
Cluster 1 中的 Pod A:
dig frontend.clusterset.local
--> 返回 Cluster 2 的 Service ClusterIP 或 Endpoint IP
实际流量路径取决于底层网络是否互通。
代表方案:MCS API(KEP-1645)。
优势:Kubernetes 原生 API,标准化程度最高,CNI 和网络方案无关。 限制:只解决服务发现,不解决网络互通;功能相对基础。
三、Cilium ClusterMesh 深度拆解
ClusterMesh 是 Cilium 的多集群方案。它的核心思路是:让每个集群的 Cilium Agent 不仅 watch 本地 etcd,还 watch 远端集群的 etcd,从而获得全局视图。
前置条件
在深入架构之前,先明确 ClusterMesh 的硬性要求:
- 所有集群必须使用 Cilium 作为 CNI。
- PodCIDR 不能重叠。每个集群分配独立的 CIDR 段。
- 每个集群需要唯一的 ClusterID(1-255)。
- 集群间网络可达。至少 clustermesh-apiserver 的端口(默认 2379)可以从远端访问。
- 所有集群的 Cilium 版本一致(或兼容)。
# Cilium Helm values 示例
cluster:
name: cluster1
id: 1
clustermesh:
useAPIServer: true
apiserver:
service:
type: LoadBalancer # 或 NodePort跨集群 etcd 连接
ClusterMesh 的数据同步依赖
clustermesh-apiserver。这是一个轻量级组件,它
watch 本集群的 kube-apiserver 获取
Node、Service、Endpoint、Identity 等资源,写入自己内嵌的
etcd 实例,并通过 TLS 将 etcd 暴露给远端集群的 Cilium
Agent。
每个集群都运行一个
clustermesh-apiserver。远端集群的 Cilium Agent
连接到它,以只读方式 watch 数据变更。
Cluster 1 Cluster 2
+-----------------------+ +-----------------------+
| kube-apiserver | | kube-apiserver |
| v | | v |
| clustermesh-apiserver | <-- TLS --> | clustermesh-apiserver |
| v | | v |
| Cilium Agent | | Cilium Agent |
| watch: local+remote | | watch: local+remote |
+-----------------------+ +-----------------------+
关键设计:Cilium Agent 不直接访问远端
kube-apiserver。clustermesh-apiserver
是一个中间层,只暴露 Cilium 需要的数据,减小攻击面。
Identity 全局同步
在 Cilium 深入 中我们讲过,Cilium 用 Security Identity 来标识 Pod 的安全身份。每个 Identity 是一个数字 ID,基于 Pod 的 label 集合计算得出。
多集群场景下的问题:两个集群可能独立生成相同数字 ID 但含义不同的 Identity。ClusterMesh 通过两个机制解决:
机制一:ClusterID 前缀
每个 Identity 的高 8 位存储 ClusterID,低 16 位存储本地 Identity ID。这样即使两个集群的本地 ID 相同,加上 ClusterID 前缀后全局唯一。
全局 Identity = (ClusterID << 16) | LocalIdentityID
Cluster 1 (ID=1): identity = 0x00010001 = 65537
Cluster 2 (ID=2): identity = 0x00020001 = 131073
这也是 ClusterID 限制在 1-255 的原因——8 位能表示的最大值就是 255。
机制二:相同 label 集合共享 Identity
如果两个集群中的 Pod 有完全相同的 label 集合(例如
app=frontend, env=prod),它们会获得相同的全局
Identity。这使得跨集群的 NetworkPolicy 可以用同一个 Identity
来匹配。
# 查看某个 Pod 的 Identity
cilium identity list | grep frontend
# 查看所有集群同步过来的 Identity
cilium identity list --endpointsGlobal Service
ClusterMesh 最核心的用户可见功能是 Global Service:在多个集群中创建同名 Service,ClusterMesh 自动将它们合并为一个全局 Service,endpoints 跨集群负载均衡。
启用方式很简单——给 Service 加一个 annotation:
apiVersion: v1
kind: Service
metadata:
name: frontend
namespace: default
annotations:
service.cilium.io/global: "true"
spec:
selector:
app: frontend
ports:
- port: 80
targetPort: 8080在两个集群中分别创建这个 Service 后,任意集群中的 Pod
访问 frontend.default.svc.cluster.local
时,Cilium 的 eBPF datapath 会同时考虑本地和远端的 endpoints
进行负载均衡。
可以通过 annotation 控制更细粒度的行为:
annotations:
# 启用全局 Service
service.cilium.io/global: "true"
# 优先使用本地 endpoints,远端作为备份
service.cilium.io/shared: "true"
# 亲和性:优先本地
service.cilium.io/affinity: "local"service.cilium.io/affinity 支持三个值:
| 值 | 行为 |
|---|---|
none |
本地和远端 endpoints 平等负载均衡 |
local |
优先本地 endpoints,本地全部不可用时 failover 到远端 |
remote |
优先远端 endpoints(用于灾难恢复场景) |
# 验证 Global Service 的 endpoints
cilium service list | grep frontend
# 输出示例:
# 100 frontend.default.svc 10.1.100.10:80
# 10.1.1.5:8080 (local)
# 10.1.2.3:8080 (local)
# 10.2.1.8:8080 (remote)
# 10.2.3.1:8080 (remote)跨集群 NetworkPolicy
Global Identity 使得 CiliumNetworkPolicy 可以跨集群生效。你可以在 Cluster 1 中编写一条策略,限制只有 Cluster 2 中带特定 label 的 Pod 才能访问某个 Service。
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-cross-cluster-frontend
namespace: default
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
# 不需要指定集群!Identity 是全局的
toPorts:
- ports:
- port: "8080"
protocol: TCP由于 Cluster 1 和 Cluster 2 中 app=frontend
的 Pod 共享相同的全局 Identity,这条策略会同时允许两个集群的
frontend Pod 访问 backend。
如果你需要只允许特定集群的 Pod
访问,可以结合 io.cilium.k8s.policy.cluster
label:
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
io.cilium.k8s.policy.cluster: cluster2四、Submariner 深度拆解
Submariner 是 CNCF Sandbox 项目,定位是为任意 Kubernetes 集群提供跨集群网络互通。与 ClusterMesh 不同,它不要求统一 CNI,采用 Gateway 模式,通过加密隧道连接集群。
架构组件
Submariner 由四个核心组件组成:
Broker:集群发现和协调中心。它本身是一个 Kubernetes 集群(可以是独立的,也可以复用某个参与集群),用 CRD 存储所有集群的连接信息。各集群的 Gateway Engine 向 Broker 注册自己的 endpoint 信息,并从 Broker 获取其他集群的信息。
Gateway Engine:部署在每个集群的一个节点上(Gateway 节点)。负责建立和维护跨集群的加密隧道。支持两种隧道后端:IPsec(Libreswan,基于 IKEv2,成熟稳定)和 WireGuard(更现代,性能更好,内核级实现)。
# 查看 Gateway Engine 状态
subctl show connections
# GATEWAY CLUSTER REMOTE IP CABLE DRIVER STATUS
# worker-node-1 cluster2 10.0.0.5 libreswan connectedRoute Agent:以 DaemonSet 部署在每个节点上。负责配置路由规则,将发往远端集群 CIDR 的流量引导到 Gateway 节点;配置 iptables 规则进行 SNAT/DNAT(当使用 GlobalNet 时);维护 VXLAN overlay。
# Route Agent 添加的路由示例
ip route show table 100
# 10.244.0.0/16 via 172.18.0.5 dev vx-submarinerLighthouse
DNS:跨集群服务发现组件。每个集群运行一个 CoreDNS
实例,通过自定义插件解析 *.clusterset.local
域名。Pod 发起 DNS 查询后,lighthouse 插件查询本地
ServiceImport CRD,返回对应的 ClusterIP 或
EndpointSlice。
GlobalNet:解决 CIDR 重叠
现实中,很多集群在部署时使用了默认的 PodCIDR(如
10.244.0.0/16),导致 CIDR 重叠。Submariner 的
GlobalNet 通过分配全局唯一的虚拟 CIDR 来解决这个问题。
不使用 GlobalNet:
Cluster A PodCIDR: 10.244.0.0/16
Cluster B PodCIDR: 10.244.0.0/16
--> 路由冲突,无法互通!
使用 GlobalNet:
Cluster A PodCIDR: 10.244.0.0/16 GlobalCIDR: 242.1.0.0/16
Cluster B PodCIDR: 10.244.0.0/16 GlobalCIDR: 242.2.0.0/16
Pod A (10.244.1.5) --> SNAT to 242.1.1.5 --> 隧道 --> DNAT to 10.244.2.8 --> Pod B
GlobalNet 的工作原理:
- 每个集群分配一个 GlobalCIDR(如
242.x.0.0/16)。 - Pod 出站流量做 SNAT:源 IP 从本地 PodCIDR 改为 GlobalCIDR 中的对应地址。
- 远端入站流量做 DNAT:目标 IP 从 GlobalCIDR 还原为本地 PodCIDR。
- GlobalNet Controller 负责分配和管理
Global IP,使用
GlobalIngressIP和ClusterGlobalEgressIPCRD。
# GlobalNet 分配的全局 IP 示例
apiVersion: submariner.io/v1
kind: GlobalIngressIP
metadata:
name: frontend-ingress
namespace: default
spec:
target: Service
serviceRef:
name: frontend
status:
allocatedIP: 242.1.0.100GlobalNet 的代价是额外的 NAT 开销和调试复杂度。如果 CIDR 不重叠,建议不要开启 GlobalNet。
Submariner 安装与配置
Submariner 提供 subctl CLI
工具简化部署:
# 1. 部署 Broker(在 broker 集群上执行)
subctl deploy-broker --kubeconfig broker-kubeconfig
# 2. 将集群加入(在每个参与集群上执行)
subctl join broker-info.subm \
--clusterid cluster1 \
--kubeconfig cluster1-kubeconfig \
--nattport 4500 \
--cable-driver libreswan
subctl join broker-info.subm \
--clusterid cluster2 \
--kubeconfig cluster2-kubeconfig \
--nattport 4500 \
--cable-driver libreswan
# 3. 验证连接
subctl show all
subctl verify --kubeconfig cluster1-kubeconfig \
--toconfig cluster2-kubeconfig --verbose导出跨集群 Service
Submariner 使用 ServiceExport CRD 来声明哪些
Service 需要跨集群可见:
apiVersion: multicluster.x-k8s.io/v1alpha1
kind: ServiceExport
metadata:
name: frontend
namespace: default创建 ServiceExport 后,Submariner 的
Lighthouse 组件会:
- 在 Broker 集群中创建对应的聚合信息。
- 其他集群的 Lighthouse 从 Broker 同步,生成本地的
ServiceImportCRD。 - Lighthouse DNS 根据
ServiceImport响应 DNS 查询。
# 在 Cluster A 中导出 Service
kubectl apply -f service-export.yaml
# 在 Cluster B 中验证
kubectl get serviceimport -n default
# NAME TYPE IP AGE
# frontend ClusterSetIP 242.1.0.100 30s
# DNS 查询验证
kubectl exec -it test-pod -- nslookup frontend.default.svc.clusterset.local
# Server: 10.96.0.10
# Address: 10.96.0.10#53
# Name: frontend.default.svc.clusterset.local
# Address: 242.1.0.100五、Multi-Cluster Services API(KEP-1645)
MCS API 是 Kubernetes SIG-Multicluster 定义的标准 API,目标是为多集群服务发现提供统一的抽象。它不是一个具体实现,而是一套 CRD 和语义规范,各厂商基于此实现自己的多集群方案。
核心概念
ClusterSet:一组相关联的集群。ClusterSet 内的集群共享服务可见性,ClusterSet 外的集群不可见。
ServiceExport:声明某个 Service 需要导出到 ClusterSet。在 Service 所在的集群中创建。
ServiceImport:系统自动在 ClusterSet 的其他集群中创建,表示一个从远端导入的 Service。
Cluster 1 (provider):
Service/frontend + ServiceExport/frontend
|
| MCS Controller 同步
v
Cluster 2 (consumer):
ServiceImport/frontend --> DNS: frontend.default.svc.clusterset.local
ServiceExport 和 ServiceImport
# 在 provider 集群中创建 ServiceExport
apiVersion: multicluster.x-k8s.io/v1alpha1
kind: ServiceExport
metadata:
name: frontend
namespace: default
---
# 系统自动在 consumer 集群中创建 ServiceImport
apiVersion: multicluster.x-k8s.io/v1alpha1
kind: ServiceImport
metadata:
name: frontend
namespace: default
spec:
type: ClusterSetIP
ips:
- 10.96.100.50 # 分配的 ClusterSet VIP
ports:
- port: 80
protocol: TCPServiceImport 有两种类型:
| 类型 | 行为 |
|---|---|
ClusterSetIP |
分配一个虚拟 IP(VIP),类似 ClusterIP。DNS 返回这个 VIP。 |
Headless |
不分配 VIP,DNS 直接返回所有集群的 endpoint IP。 |
DNS 规范
MCS API 定义了标准的 DNS 命名格式:
<service>.<namespace>.svc.clusterset.local
示例:
frontend.default.svc.clusterset.local
注意是 clusterset.local,不是
cluster.local。这个域名在 ClusterSet
范围内全局可解析。
如果需要访问特定集群的 Service(而不是全局负载均衡),可以使用:
<service>.<namespace>.svc.<cluster-id>.clusterset.local
示例:
frontend.default.svc.cluster1.clusterset.local
实现现状
MCS API 本身只是 CRD 定义和语义规范,需要控制器来实现实际功能。目前主要的实现包括:
| 实现 | 状态 | 特点 |
|---|---|---|
| GKE Multi-cluster Services | GA | Google Cloud 原生,与 GKE 深度集成 |
| Submariner Lighthouse | 活跃 | 兼容 MCS API,提供完整的网络互通 |
| Cilium ClusterMesh | 部分支持 | 通过 Global Service 实现类似语义 |
| AWS Cloud Map MCS Controller | Preview | AWS EKS 集成 |
| Istio Multi-cluster | 活跃 | 基于 Istio 的服务网格实现 |
| Liqo | 活跃 | 虚拟节点方式,透明扩展 |
# 检查集群是否安装了 MCS CRD
kubectl api-resources | grep multicluster
# serviceexports multicluster.x-k8s.io/v1alpha1 true ServiceExport
# serviceimports multicluster.x-k8s.io/v1alpha1 true ServiceImportMCS API 的局限性
MCS API 解决的是服务发现问题,不解决网络互通问题。它假设底层网络已经可达(通过 VPN、对等互联、或其他方案)。这意味着在实际部署中,MCS API 通常需要配合一个底层网络方案(Submariner、ClusterMesh、云厂商 VPC Peering 等)使用。它的价值在于提供了标准化的 API 接口,避免应用层代码绑定到特定的多集群方案。
六、多集群 CIDR 规划
CIDR 规划是多集群网络中最容易被忽视、但影响最深远的决策。错误的 CIDR 分配会导致地址冲突,后期修改的成本极高。
CIDR 分配策略
一个 Kubernetes 集群涉及三个 CIDR 段:
1. PodCIDR - Pod 的 IP 地址范围
2. ServiceCIDR - Service ClusterIP 的地址范围
3. NodeCIDR - 节点网络的 IP 地址范围
多集群环境下,这三个 CIDR 段都需要规划,确保不冲突。
推荐的分配方案(以 10.0.0.0/8 为基础):
集群编号 PodCIDR ServiceCIDR NodeCIDR
--------------------------------------------------------------
Cluster 1 10.1.0.0/16 10.101.0.0/16 172.16.1.0/24
Cluster 2 10.2.0.0/16 10.102.0.0/16 172.16.2.0/24
Cluster 3 10.3.0.0/16 10.103.0.0/16 172.16.3.0/24
...
Cluster N 10.N.0.0/16 10.(100+N).0.0/16 172.16.N.0/24
这种方案支持最多约 99 个集群(10.1-10.99),每个集群 65534 个 Pod IP。对于大规模部署,可以使用更大的地址空间。
处理已有集群的 CIDR 冲突
如果已有集群使用了重叠的 CIDR,有几种处理方法:
方法一:使用 GlobalNet(Submariner)
如前文所述,Submariner 的 GlobalNet 通过 NAT 解决 CIDR 重叠,不需要修改已有集群的配置。
方法二:重新规划 CIDR
对于新建的集群,从一开始就规划好 CIDR。对于已有集群,可以通过以下步骤迁移:
# 这是破坏性操作,需要排空节点
# 1. 修改 kube-controller-manager 的 --cluster-cidr 参数
# 2. 修改 CNI 配置中的 CIDR
# 3. 逐节点排空、删除 CNI 状态、重新加入
# 4. 验证新 Pod 获得新 CIDR 范围的 IP
# 极其不推荐在生产环境直接操作,建议新建集群迁移方法三:IP Masquerade——在集群边界做 SNAT,让出站流量使用不冲突的 IP。本质上和 GlobalNet 类似,但需要手动配置。
CIDR 规划检查清单
部署多集群网络之前,务必确认:所有集群的 PodCIDR 不重叠(或使用 GlobalNet);所有集群的 ServiceCIDR 不重叠;CIDR 不与节点网络、企业内网(VPN、办公网络)冲突;预留足够的地址空间供未来扩展;记录 CIDR 分配方案,纳入配置管理。
七、实验:用 Cilium ClusterMesh 连接两个 kind 集群
下面我们用 kind 搭建两个集群,安装 Cilium 并启用 ClusterMesh,验证跨集群 Service 访问。
环境准备
# 确认工具版本
kind version # v0.22+
cilium version # CLI 0.16+
kubectl version # v1.30+
helm version # v3.14+
docker version # 26.x+创建两个 kind 集群
# cluster1.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
disableDefaultCNI: true # 禁用默认 CNI,由 Cilium 接管
podSubnet: "10.1.0.0/16"
serviceSubnet: "10.101.0.0/16"
nodes:
- role: control-plane
- role: worker
- role: worker# cluster2.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
disableDefaultCNI: true
podSubnet: "10.2.0.0/16"
serviceSubnet: "10.102.0.0/16"
nodes:
- role: control-plane
- role: worker
- role: worker# 创建集群
kind create cluster --name cluster1 --config cluster1.yaml
kind create cluster --name cluster2 --config cluster2.yaml
# 验证集群
kubectl --context kind-cluster1 get nodes
kubectl --context kind-cluster2 get nodes安装 Cilium
# 为 Cluster 1 安装 Cilium
cilium install --context kind-cluster1 \
--set cluster.name=cluster1 \
--set cluster.id=1 \
--set ipam.operator.clusterPoolIPv4PodCIDRList="10.1.0.0/16"
# 为 Cluster 2 安装 Cilium
cilium install --context kind-cluster2 \
--set cluster.name=cluster2 \
--set cluster.id=2 \
--set ipam.operator.clusterPoolIPv4PodCIDRList="10.2.0.0/16"
# 等待 Cilium 就绪
cilium status --context kind-cluster1 --wait
cilium status --context kind-cluster2 --wait启用 ClusterMesh
# 在两个集群上启用 ClusterMesh
cilium clustermesh enable --context kind-cluster1 \
--service-type NodePort
cilium clustermesh enable --context kind-cluster2 \
--service-type NodePort
# 等待 ClusterMesh 就绪
cilium clustermesh status --context kind-cluster1 --wait
cilium clustermesh status --context kind-cluster2 --wait
# 建立连接
cilium clustermesh connect \
--context kind-cluster1 \
--destination-context kind-cluster2
# 验证连接状态
cilium clustermesh status --context kind-cluster1预期输出:
ClusterMesh: ok
cluster2: connected, 3 nodes, 0 identities, 0 services
部署测试应用
# rebel-base.yaml - 在两个集群中都部署
apiVersion: apps/v1
kind: Deployment
metadata:
name: rebel-base
spec:
replicas: 2
selector:
matchLabels:
app: rebel-base
template:
metadata:
labels:
app: rebel-base
spec:
containers:
- name: rebel-base
image: docker.io/nginx:1.25-alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: rebel-base
annotations:
service.cilium.io/global: "true"
service.cilium.io/shared: "true"
spec:
selector:
app: rebel-base
ports:
- port: 80
targetPort: 80在每个集群中分别部署,并修改 nginx 默认页以区分来源:
# 在 Cluster 1 部署后,进入 Pod 写入集群标识
kubectl --context kind-cluster1 apply -f rebel-base.yaml
kubectl --context kind-cluster2 apply -f rebel-base.yaml
# 等待 Pod 就绪
kubectl --context kind-cluster1 wait --for=condition=ready \
pod -l app=rebel-base --timeout=60s
kubectl --context kind-cluster2 wait --for=condition=ready \
pod -l app=rebel-base --timeout=60s验证跨集群 Service 访问
# 在 Cluster 1 中创建测试 Pod
kubectl --context kind-cluster1 run test-pod \
--image=curlimages/curl:8.5.0 --restart=Never \
--command -- sleep 3600
# 多次访问 Global Service,观察响应来自不同集群
for i in $(seq 1 10); do
kubectl --context kind-cluster1 exec test-pod -- \
curl -s rebel-base.default.svc.cluster.local
done
# 预期输出交替出现来自两个集群的响应验证 Global Service endpoints 与 Failover
# 查看 Cilium 合并后的 Service endpoints(应包含 local + remote)
kubectl --context kind-cluster1 exec -n kube-system \
$(kubectl --context kind-cluster1 get pod -n kube-system \
-l k8s-app=cilium -o name | head -1) -- \
cilium service list | grep rebel-base
# Failover 测试:缩容 Cluster 2,流量应全部切到 Cluster 1
kubectl --context kind-cluster2 scale deployment rebel-base --replicas=0
for i in $(seq 1 5); do
kubectl --context kind-cluster1 exec test-pod -- \
curl -s rebel-base.default.svc.cluster.local
done
kubectl --context kind-cluster2 scale deployment rebel-base --replicas=2验证跨集群 NetworkPolicy
# deny-all.yaml - 在 Cluster 1 中应用
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: deny-cross-cluster
namespace: default
spec:
endpointSelector:
matchLabels:
app: rebel-base
ingress:
- fromEndpoints:
- matchLabels:
io.cilium.k8s.policy.cluster: cluster1
toPorts:
- ports:
- port: "80"
protocol: TCP# 应用策略
kubectl --context kind-cluster1 apply -f deny-all.yaml
# 从 Cluster 1 访问 -- 应该成功
kubectl --context kind-cluster1 exec test-pod -- \
curl -s --max-time 3 rebel-base.default.svc.cluster.local
# Cluster cluster1
# 从 Cluster 2 访问 Cluster 1 的 Pod -- 应该被拒绝
# (需要在 Cluster 2 中创建 test-pod 并尝试访问 Cluster 1 的 endpoint)清理实验环境
kind delete cluster --name cluster1
kind delete cluster --name cluster2八、方案选型指南
三种模式没有绝对的优劣,选择取决于你的具体场景:
选择 Cilium ClusterMesh,如果:
- 所有集群已经或计划使用 Cilium
- 需要最低延迟的跨集群通信
- 需要跨集群 NetworkPolicy
- CIDR 可以规划为不重叠
- 集群数量适中(建议不超过 255 个)
选择 Submariner,如果:
- 集群使用不同的 CNI(混合环境)
- 已有集群 CIDR 重叠,无法更改
- 需要加密隧道保护跨集群流量
- 希望快速部署,不想大规模改造网络
选择 MCS API,如果:
- 希望面向未来的标准化方案
- 已有底层网络互通(VPC Peering、VPN 等)
- 只需要服务发现,不需要网络层互通
- 使用云厂商托管 K8s(GKE、EKS 等原生支持)
在实际生产中,这些方案并不互斥。例如:
- 用 Submariner 提供底层网络互通 + MCS API 提供标准化的服务发现接口。
- 用 Cilium ClusterMesh 在同一 Region 的集群间通信 + VPN/专线连接不同 Region。
- MCS API 作为上层抽象,底层可以随时切换具体实现。
九、生产环境注意事项
监控与告警
多集群网络引入了大量新的故障点,必须建立完善的监控。关键指标包括:跨集群隧道状态、跨集群延迟(P50/P99)、clustermesh-apiserver 健康状态、etcd 同步延迟、Global Service endpoint 数量变化、跨集群 DNS 解析成功率、Gateway 节点资源使用率。
# Prometheus 告警规则示例
groups:
- name: multi-cluster-network
rules:
- alert: ClusterMeshDisconnected
expr: cilium_clustermesh_remote_cluster_status != 1
for: 5m
labels:
severity: critical
annotations:
summary: "ClusterMesh 与远端集群断连"
- alert: SubmarinerGatewayDown
expr: submariner_connections{status="error"} > 0
for: 2m
labels:
severity: critical
annotations:
summary: "Submariner Gateway 连接异常"安全加固
生产环境安全检查清单:clustermesh-apiserver 使用 mTLS 认证;Submariner 隧道启用加密(IPsec 或 WireGuard);Gateway 节点的安全组/防火墙规则最小化;跨集群 NetworkPolicy 限制不必要的访问;etcd 数据加密(at rest);定期轮换证书和密钥。
容量规划
ClusterMesh 容量参考(Cilium 1.16):
- 最大集群数:255(受 ClusterID 8-bit 限制)
- 建议:etcd 数据量 < 1GB,节点总数 < 5000
Submariner 容量参考:
- Gateway 节点是瓶颈:所有跨集群流量经过它
- 建议 Gateway 节点使用高带宽实例
- 可配置多个 Gateway 做 HA,但同一时间只有一个 active
附录 A:常用排查命令
# === Cilium ClusterMesh ===
cilium clustermesh status # 连接状态
cilium identity list | grep -v "reserved" # 远端 Identity
cilium service list --clustermesh-affinity # Global Service endpoints
cilium status --verbose | grep -A5 "ClusterMesh" # Agent 远端 etcd 连接
kubectl logs -n kube-system deploy/clustermesh-apiserver -f # apiserver 日志
# === Submariner ===
subctl show all # 总体状态
subctl diagnose all # 连接诊断
subctl verify --context cluster1 --tocontext cluster2 # 跨集群连通性
kubectl logs -n submariner-operator deploy/submariner-gateway -f
# === 通用 ===
kubectl get serviceexport,serviceimport -A # MCS CRD
kubectl exec test-pod -- nslookup frontend.default.svc.clusterset.local
kubectl exec test-pod -- curl -v --max-time 5 http://10.2.1.8:80附录 B:推荐阅读
- Cilium ClusterMesh 官方文档:ClusterMesh
- Submariner 官方文档:Submariner
- KEP-1645 Multi-Cluster Services API:KEP-1645
- SIG-Multicluster:About MCS API
- Cilium Identity 机制:Cilium 深入
- 本系列 Service 与 kube-proxy:Service 与 kube-proxy
- 上一篇:网络可观测性与故障排查
- 下一篇:跨云网络与混合云互联
- 相关:Cilium 深入
- 相关:Service 与 kube-proxy