Service Mesh 多集群联邦:跨集群流量路由与证书信任链

Service Mesh 多集群联邦:跨集群流量路由与证书信任链

一、3 个 K8s 集群,各自装了 Istio,但 A 集群调 B 集群的服务直接 503

多集群 Service Mesh 的第一个坑,往往出现在跨集群通信上。单集群内,Envoy Sidecar 通过 xDS 协议从 Istiod 获取服务发现和路由配置,一切正常。跨集群时,集群 A 的 Istiod 不知道集群 B 的服务端点,Envoy 自然也无法路由。

第二个坑是 mTLS 证书信任链。每个集群的 Istiod 使用自己的 CA 签发证书,集群 A 的 Sidecar 不信任集群 B 的 CA,mTLS 握手直接失败。如果你在网段隔离的多集群环境中关掉 mTLS 来绕过——那等于把安全门敞开。

二、多集群联邦的架构选型

graph TD subgraph Cluster_A["Cluster A (主集群)"] Istiod_A["Istiod<br/>(主控制面)"] CA_A["Istio CA<br/>(根证书)"] GW_A["East-West Gateway<br/>(跨集群流量入口)"] Svc_A1["Service A1<br/>(Pod + Envoy)"] Svc_A2["Service A2<br/>(Pod + Envoy)"] end subgraph Cluster_B["Cluster B (远端集群)"] Istiod_B["Istiod<br/>(远端控制面<br/>同步配置)"] CA_B["Istio CA<br/>(中间证书<br/>由 Cluster A 签发)"] GW_B["East-West Gateway<br/>(跨集群流量入口)"] Svc_B1["Service B1<br/>(Pod + Envoy)"] Svc_B2["Service B2<br/>(Pod + Envoy)"] end Istiod_A <-->|"服务发现同步<br/>(xDS)"| Istiod_B CA_A -->|"签发中间 CA"| CA_B Svc_A1 -->|"mTLS 加密"| GW_A GW_A -->|"跨集群路由<br/>SNI: svc-b1.cluster-b"| GW_B GW_B -->|"mTLS 加密"| Svc_B1 style Istiod_A fill:#4A90D9,color:#fff style CA_A fill:#FF6B6B,color:#fff style GW_A fill:#F5A623,color:#000 style GW_B fill:#F5A623,color:#000

核心设计:

  1. 主-远端控制面模式:集群 A 的 Istiod 作为主控制面,持有完整的服务注册信息。集群 B 的 Istiod 通过 API 同步 Cluster A 的配置,包括跨集群的 ServiceEntry 和 DestinationRule。
  2. East-West Gateway:专用网关处理跨集群流量。它不是普通的 Ingress Gateway——East-West Gateway 配置了多集群 SNI 路由和跨集群 mTLS 认证,是网格联邦的"中间件"。
  3. 证书信任链:集群 B 的 CA 使用集群 A 的根 CA 签发的中间证书,这样两个集群的 Sidecar 都在同一个信任域内。mTLS 握手时,不论请求来自哪个集群,只要证书链回溯到同一个根 CA,验证就通过。

三、生产级多集群 Istio 联邦实现

集群 A(主集群)配置

# cluster-a-istio-operator.yaml # IstioOperator 配置:主集群控制面 apiVersion: install.istio.io/v1alpha1 kind: IstioOperator metadata: name: cluster-a-primary namespace: istio-system spec: profile: default # 全局配置 values: global: # 多集群模式:标记为主集群 multiCluster: clusterName: "cluster-a" # 网格网络标识 network: "network-a" # 信任域 meshID: "mesh1" trustDomain: "mesh1.local" # 启用自动 mTLS controlPlaneSecurityEnabled: true # CA 证书配置 caAddress: "" # Pilot(Istiod)配置:启用跨集群服务发现 pilot: env: # 启用跨集群网关的端点发现 ENABLE_CROSS_CLUSTER_WORKLOAD_ENTRIES: "true" PILOT_ENABLE_CROSS_CLUSTER_ENDPOINTS: "true" # 允许合并其他集群的服务发现 PILOT_SCOPE_GATEWAY_TO_NAMESPACE: "false" # Mesh 配置:定义信任域 meshConfig: trustDomain: "mesh1.local" defaultConfig: proxyMetadata: ISTIO_META_DNS_CAPTURE: "true" accessLogFile: "/dev/stdout" accessLogFormat: | [%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%" %RESPONSE_CODE% cluster=%REQ(X-ENVOY-PEER-METADATA:CLUSTER_ID)% duration=%DURATION%

East-West Gateway 配置

# east-west-gateway.yaml # East-West Gateway:处理跨集群流量 apiVersion: networking.istio.io/v1beta1 kind: Gateway metadata: name: east-west-gateway namespace: istio-system spec: selector: istio: eastwestgateway # 专用的 Gateway Pod 标签 servers: # 基础跨集群路由端口(15443) - port: number: 15443 name: tls-cross-cluster protocol: TLS tls: # AUTO_PASSTHROUGH: Envoy 根据 SNI 自动决定路由 # 为什么用 AUTO_PASSTHROUGH 而非手动配置路由: # 跨集群的服务名是动态的,手动配置跟不上变化 mode: AUTO_PASSTHROUGH hosts: - "*.local" # 匹配所有 *.mesh1.local 格式的 SNI --- # 跨集群 DestinationRule:配置 mTLS apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: cross-cluster-mtls namespace: istio-system spec: host: "*.cluster-b.svc.cluster.local" trafficPolicy: tls: # ISTIO_MUTUAL: 使用 Istio 的 mTLS # 自动使用 Sidecar 的证书与对端握手 mode: ISTIO_MUTUAL

集群 B(远端集群)注册到网格

#!/bin/bash # register-remote-cluster.sh # 将远端集群 B 注册到网格联邦 set -e PRIMARY_CLUSTER_CONTEXT="cluster-a" REMOTE_CLUSTER_CONTEXT="cluster-b" MESH_ID="mesh1" TRUST_DOMAIN="mesh1.local" echo "=== Step 1: 在主集群创建远端集群的 Secret ===" # 导出远端集群 API Server 的访问凭证 istioctl x create-remote-secret \ --context="$REMOTE_CLUSTER_CONTEXT" \ --name="cluster-b" \ | kubectl apply --context="$PRIMARY_CLUSTER_CONTEXT" -f - echo "=== Step 2: 在远端集群安装 Istio(远端模式)===" cat <<EOF | istioctl install --context="$REMOTE_CLUSTER_CONTEXT" -f - apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: profile: remote values: global: # 标记为远端集群 remotePilotAddress: "istiod.istio-system.svc.cluster.local:15012" multiCluster: clusterName: "cluster-b" network: "network-b" meshID: "$MESH_ID" trustDomain: "$TRUST_DOMAIN" pilot: env: ENABLE_CROSS_CLUSTER_WORKLOAD_ENTRIES: "true" EOF echo "=== Step 3: 配置跨集群信任域 ===" # 在集群 B 创建信任域配置 # 使用集群 A 的根 CA 签发的中间证书 # 这样集群 A 和 B 的 Sidecar 都属于同一个信任域 # Step 3a: 从集群 A 导出根证书 kubectl --context="$PRIMARY_CLUSTER_CONTEXT" \ get secret istio-ca-secret -n istio-system -o json \ | jq -r '.data["ca-cert.pem"]' | base64 -d > /tmp/root-ca.pem # Step 3b: 用根证书为集群 B 签发中间证书 # 实际生产环境应用 cert-manager 或 vault 管理此流程 openssl genrsa -out /tmp/cluster-b-intermediate.key 2048 openssl req -new -key /tmp/cluster-b-intermediate.key \ -subj "/CN=cluster-b.mesh1.local" \ -out /tmp/cluster-b-intermediate.csr openssl x509 -req -in /tmp/cluster-b-intermediate.csr \ -CA /tmp/root-ca.pem -CAkey /tmp/root-ca.key \ -CAcreateserial -out /tmp/cluster-b-intermediate.crt \ -days 365 # Step 3c: 创建证书 Secret kubectl --context="$REMOTE_CLUSTER_CONTEXT" \ create secret generic cacerts -n istio-system \ --from-file=ca-cert.pem=/tmp/root-ca.pem \ --from-file=ca-key.pem=/tmp/cluster-b-intermediate.key \ --from-file=root-cert.pem=/tmp/root-ca.pem \ --from-file=cert-chain.pem=/tmp/cluster-b-intermediate.crt # 重启 Istiod 加载新证书 kubectl --context="$REMOTE_CLUSTER_CONTEXT" \ rollout restart deployment/istiod -n istio-system echo "=== Step 4: 验证跨集群通信 ===" # 在集群 A 部署测试 Pod kubectl --context="$PRIMARY_CLUSTER_CONTEXT" \ run test-client --image=curlimages/curl -it --rm --restart=Never -- \ curl -v http://service-b1.cluster-b.svc.cluster.local:8080/ echo "多集群联邦配置完成"

跨集群流量路由策略

# cross-cluster-routing.yaml # 跨集群流量管理策略 # ServiceEntry: 将集群 B 的服务注册到集群 A apiVersion: networking.istio.io/v1beta1 kind: ServiceEntry metadata: name: cluster-b-external-svc namespace: default spec: hosts: # 哪个服务在集群 B - "service-b1.cluster-b.svc.cluster.local" # 跨集群服务:标记为 MESH_INTERNAL # 不要标记为 EXTERNAL——那会绕过 mTLS location: MESH_INTERNAL ports: - number: 8080 name: http protocol: HTTP resolution: DNS --- # DestinationRule: 跨集群的服务访问规则 apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: cluster-b-service-rules namespace: default spec: host: "service-b1.cluster-b.svc.cluster.local" trafficPolicy: # 连接池:跨集群网络延迟较高,需要更宽松的超时 connectionPool: tcp: maxConnections: 100 connectTimeout: 3s # 跨集群连接超时设 3s http: http1MaxPendingRequests: 100 http2MaxRequests: 100 maxRequestsPerConnection: 10 maxRetries: 3 # 负载均衡:跨集群适合 LEAST_REQUEST # 为什么用 LEAST_REQUEST 而非 ROUND_ROBIN: # 跨集群的网络延迟差异大,应该优先发给负载轻的集群 loadBalancer: simple: LEAST_REQUEST # 异常检测:快速剔除故障的集群 outlierDetection: consecutive5xxErrors: 3 interval: 10s baseEjectionTime: 30s maxEjectionPercent: 50 # TLS: 强制使用 mTLS 到远端集群 tls: mode: ISTIO_MUTUAL

四、多集群联邦的复杂性与边界

缺点:

  1. 跨集群延迟不可消除:光速限制。如果集群在 us-east 和 ap-southeast,跨集群 RTT 约 200ms。Service Mesh 无法消除这个延迟,只能通过合理的负载均衡和异常检测做容错。
  2. 配置一致性维护:ServiceEntry、DestinationRule 需要在主集群维护。服务增多后,跨集群的配置量呈 O(n×m) 增长。
  3. 证书过期风险:中间证书需要定期轮转。如果集群 B 的中间证书过期,两个集群间突然无法通信,排查问题极其困难。

禁用场景:

  • 低延迟强依赖的服务(如交易系统的风控调用):跨集群通信的延迟不可接受,应保持服务在同一集群内或使用专门的专线。
  • 对隔离性要求极高的场景(如合规要求的物理隔离):共享 CA 信任域意味着一个集群的 Sidecar 获取相应权限后可以访问另一个集群的服务。

五、总结

Service Mesh 多集群联邦的两个核心问题:跨集群的服务发现和跨集群的证书信任链。主-远端控制面模式让服务配置在中心维护,East-West Gateway 作为跨集群流量的标准化入口,AUTO_PASSTHROUGH模式按 SNI 动态路由。mTLS 信任链的关键在于:让所有集群的 CA 都在同一个根证书下,通过中间证书链建立信任关系。交叉两个关键风险:跨集群延迟无法消除需要容错设计,证书轮转需要自动化避免手动过期。