资讯动态

Istio多集群跨地域高可用方案实践:控制面多活与配置中心设计

发布时间:2026/9/8 11:56:56 来源:尧图企业网站定制
聊多集群部署之前先说说我为什么折腾这个事。项目从单集群迁移到多集群最直接的动力其实是容灾我们核心业务服务原先跑在一个K8s集群里某次机房级别的网络抖动导致整个集群进入异常状态控制面不可用、业务Pod健康检查频繁失败那一次故障整整影响了线上三个多小时。后来复盘的时候大家达成一致单集群就是在赌命控制面和数据面同生共死一旦集群级故障发生什么优雅降级都是后话。但单集群往上走并不只是“再搭一套K8s再装一遍Istio”那么简单。跨集群的服务发现、流量调度、证书体系、配置同步每一个环节都有一堆坑等着你。这篇就基于我最近在灾备项目里落地的一套Istio多集群跨地域高可用方案把整体架构设计、核心细节、实操过程和踩坑记录完整拆一遍给正准备做多集群改造的团队一个可参考的样本。1. 整体设计与思路拆解先想清楚“多集群”到底要解决什么问题1.1 为什么必须做控制面高可用先抛一个观点很多人理解里的“多集群高可用”其实是“业务多副本多地部署”但Istio架构里真正容易成为单点的其实是控制面。一个K8s集群部署一套Istiod所有Sidecar都向这个Istiod发起长连接获取配置那么只要这个Istiod挂了或者承载Istiod的集群出问题所有业务Pod的流量管理、证书轮换、配置更新全部瘫痪。我见过不少团队业务部分做了双集群双活但Istio控制面仍然只有一个集群在跑另一个集群的所有业务流量也依赖这一个控制面下发配置。这种架构严格来说叫“数据面多活控制面单点”出故障的时候照样一脸懵。所以我们这次设计的第一原则是控制面必须真正多活任意一个集群的Istiod挂掉其他集群的业务不受影响且不需要人工干预切换。1.2 三种多集群部署模式怎么选Istio官方支持的多集群架构可以笼统分为primary-remote和multi-primary两种模式再往下细分还有单一控制面、多控制面、配置中心等不同变体。我用一张表格把核心差异列一下这是选型时最关键的部分模式控制面位置数据面集群配置同步方式适用场景单控制面primary-remote仅一个primary集群remote集群只运行Sidecar和网关remote集群通过API Server代理访问primary的Istiod集群数量少、网络延迟低、可容忍控制面单点多控制面multi-primary每个集群都有完整Istiod各集群独立控制面各集群通过东西向网关同步服务发现信息跨地域、追求控制面高可用、网络隔离场景多控制面配置中心config cluster每个集群有Istiod另有一个config集群专门分发配置各集群独立控制面config集群作为source向其他target集群同步配置和证书多集群、多地域、需要统一配置管理我们最终选的是第三种两个primary集群各自管理本区域的业务再加一个config集群专门负责证书签发和配置分发。为什么这么选后面第2章展开说这里先给结论——这样既保证了控制面多活又避免了多个集群各自为政导致配置漂移。1.3 跨地域场景下“配置中心”模式的取舍在动手之前我把multi-primary模式下最简单的“两个primary直接互联”也测试过。架构很清爽两个集群各装一套Istio通过各自的东西向网关east-west gateway交换MCP信息彼此能发现对方集群的服务。但实操中遇到一个让我很难受的点每个集群的Istio配置比如VirtualService、DestinationRule、Sidecar资源是相互独立的你在primary-A改了一个路由规则primary-B里不会自动同步要做就得两套配置分开维护或者上一套GitOps去分别发布。在跨地域场景下配置漂移是致命的。两个区域的网关行为不一致用户流量被分到不同区域后表现不一样这种问题线上排查起来异常痛苦。所以我们引入了config cluster作为统一的配置分发源配置只在一个地方改其他集群自动同步同时各集群的Istiod仍然独立运行互不依赖。这个方案从根本上解决了多控制面场景下配置一致性的难题。2. 核心细节解析与实操要点2.1 证书体系多集群互联的信任基础先讲最容易被忽视、但出错代价最高的证书体系。Istio多集群之间要交换服务发现信息service-to-service的mTLS要能跨集群验证靠的是Webhook证书、Istiod证书、以及根证书的统筹设计。官方默认情况下每个集群独立安装Istio时会各自生成一套自签名的根证书。这样带来的问题是集群A的Pod无法验证集群B的Pod证书跨集群的mTLS根本建立不起来。因此在多集群部署里所有集群必须共享同一个根CA。我这里的方案是在config集群上先生成一套根CA然后把根证书以cacerts密钥的方式分发到每个primary集群的istio-system命名空间。关键步骤如下# 在config集群生成根CA和中间CA mkdir -p certs openssl req -newkey rsa:4096 -nodes -keyout certs/ca.key -x509 -days 3650 -out certs/ca.crt -subj /CNistio-ca # 为每个集群生成中间CA以cluster-a为例 openssl req -newkey rsa:4096 -nodes -keyout certs/cluster-a.key -x509 -days 3650 -out certs/cluster-a.crt -subj /CNistio-ca/cluster-a # 把中间CA组装成Istio要求的cacerts格式 kubectl create namespace istio-system --contextctx-cluster-a kubectl create secret generic cacerts -n istio-system --contextctx-cluster-a \ --from-fileca-cert.pemcerts/cluster-a.crt \ --from-fileca-key.pemcerts/cluster-a.key \ --from-fileroot-cert.pemcerts/ca.crt \ --from-filecert-chain.pemcerts/cluster-a.crt这里有个细节Istio要求secret里必须有四个固定字段名ca-cert.pem中间CA证书、ca-key.pem中间CA私钥、root-cert.pem根证书、cert-chain.pem完整证书链。少一个字段istiod启动时就不会加载这套CA配置而会反过来自己生成一套新的自签根证书非常坑。提示如果你的集群已经是先安装了Istio再补这个cacerts需要重启istiod才能重新加载证书配置否则你看到的依然是一套全新的自签根证书跨集群mTLS还是不通。检查方法是依次执行kubectl get secret cacerts -n istio-system -o jsonpath{.data}确认该secret存在后再重启istiod。2.2 东西向网关East-West Gateway为什么是必需品多集群之间要互相发现服务本质上是istiod需要获取其他集群的service endpoint信息。官方推荐的方式是借助istio-eastwestgateway这个专用的入口网关而不是直接用普通的Ingress Gateway。原因有两个第一东西向网关专门用于集群间的控制面通信和服务发现和南北向业务流量完全隔离便于独立做安全策略和网络策略控制 第二东西向网关的端口规划是按Istio约定来的Istiod之间的MCP连接、跨集群的Envoy服务发现都走固定端口如果混合使用普通网关配置上会麻烦非常多。安装东西向网关时有一个参数很容易踩坑ISTIO_META_REQUESTED_NETWORK_VIEW。在多网络mult-network场景下必须为每个集群指定这个环境变量值是该集群所属的网络名否则服务发现会把多个网络的所有IP都纳入导致Sidecar路由混乱。另外关于东西向网关暴露端口完整的配置长这样# eastwest-gateway.yaml apiVersion: install.istio.io/v1alpha1 kind: IstioOperator metadata: name: eastwest-gateway namespace: istio-system spec: profile: empty components: ingressGateways: - name: istio-eastwestgateway label: istio: eastwestgateway app: istio-eastwestgateway enabled: true k8s: service: ports: - name: status-port port: 15021 targetPort: 15021 - name: tls port: 15443 targetPort: 15443 - name: tls-istiod port: 15012 targetPort: 15012 - name: tls-webhook port: 15017 targetPort: 15017 env: - name: ISTIO_META_REQUESTED_NETWORK_VIEW value: network-a注意status-port: 15021这是很多人在配置云负载均衡安全组时最容易漏掉的端口。云服务商的安全组规则里如果不放行15021的健康检查流量负载均衡会把网关实例判定为不健康直接导致东西向网关的流量全部失败。2.3 配置同步config cluster到primary集群的单向流引入config cluster后配置同步这个环节反而成了整个方案里逻辑最重的部分。Istio官方的做法是通过istioctl x create-remote-secret为每个target集群生成访问config集群的远程secret然后由config集群的istiod通过这个secret将配置推送到target集群。实操时需要注意这个同步方向是单向的config集群是source其他primary集群是target配置流向是source到target。也就是说target集群上的成员配置例如ServiceEntry、VirtualService会被source集群覆盖和统一管理target集群内不应该直接修改这类配置否则同步冲突会导致难以排查的配置漂移。我在实际执行时给配置同步定了两条铁律所有跨集群访问的服务发现规则、流量路由规则、外部服务定义一律在config集群声明target集群只允许修改本集群特有的DestinationRule子集和本地网关配置。每次配置变更走GitOps流程先在config集群apply等同步完成后在线验证target集群的配置生效情况再对外切流量。同时config集群本身的istiod也必须能够发现target集群的服务。方法是使用istioctl x create-remote-secret --contextctx-cluster-a生成remote secret并apply到config集群。这个secret本质上是一个带有target集群kubeconfig信息的资源让config集群的istiod能够用该身份访问target集群的API Server从而获取service和endpoint信息。2.4 跨地域流量治理从“只读集群”到“就近接入”跨地域高可用方案里业务Pod并不是只在自己的集群内被调用还要具备跨集群故障转移的能力。Istio中实现跨集群负载均衡主要有两条路径一条是依赖「自动服务发现」在不同集群的同名Service之间做负载均衡另一条是显式声明ServiceEntry并指定endpoint地址。自动服务发现模式下所有集群中位于同一命名空间的同名Service会被视为同一个逻辑服务SidecarEnvoy会自动把流量负载均衡到所有endpoint上默认会做跨集群的负载均衡。我们实际落地时结合Istio的locality优先级设置让每个区域的流量优先访问本区域的Pod只有本区域endpoint不可用时才跨区域转发apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: dr-product namespace: prod spec: host: product.prod.svc.cluster.local trafficPolicy: connectionPool: tcp: maxConnections: 100 loadBalancer: localityLbSetting: enabled: true failover: - from: region-a to: region-b - from: region-b to: region-a这段配置的意思是将网络拓扑中的region-a和region-b看作两个故障域正常情况下流量只落在本region的Pod上一旦本region所有endpoint都不可用流量自动failover到另一个region。这里localityLbSetting是全局的建议放在Mesh级别配置里而不是只加在个别DestinationRule上否则每个服务都要写一遍维护量巨大。3. 实操过程与核心环节实现3.1 环境准备三个集群一台跳板机方案落地前的环境规划这方面直接给出可复用的组网清单角色集群名称地域Kubernetes版本Istio版本网络配置中心config-cluster地域-c独立IDCv1.271.20独立VPC业务集群Aprimary-a地域-av1.271.20独立VPC业务集群Bprimary-b地域-bv1.271.20独立VPC需要特别强调所有集群的K8s版本和Istio版本必须保持完全一致尤其是Istio版本。版本不一致会导致istiod之间通过gRPC交换状态时出现兼容性问题报错隐蔽且难以排查。我遇到过1.18的istiod和1.20的istiod互相通信时MCP同步一直pending但控制面日志里没有任何明显异常最后通过对比istiod的debug日志才发现是版本不匹配导致的协议差异。网络层面三个集群之间至少需要保证以下路径的连通性config集群的istiod可以访问每个primary集群的API Server用于获取服务信息每个primary集群的istiod可以访问config集群的API Server用于获取配置信息每个primary集群的东西向网关可以访问其他集群的东西向网关用于流量转发这层网络打通是前提如果你们的集群不在同一VPC建议先把云厂商的VPC Peering或云间专线配置好再继续下面的步骤。3.2 搭建config-cluster让配置中心先跑起来先安装config集群的Istio。这里有一个关键点配置中心集群自身的Istio必须开启多集群复用能力否则后续target集群无法通过remote secret来关联它。我用istioctl install时额外指定了ISTIO_MULTIROOT和ISTIO_MESHCONFIG相关的MeshConfig配置istioctl install --contextctx-config -y \ --set profiledemo \ --set meshConfig.rootNamespaceistio-system \ --set values.global.meshIDmesh-global \ --set values.global.multiCluster.clusterNameconfig-cluster \ --set values.global.networknetwork-configmeshID必须全局统一这是多集群可纳管的前提条件。clusterName每个集群都要不同这里config集群就叫config-cluster。network字段用于多网络场景下的网络隔离如果三个集群不在同一网络平面这个字段必须正确设置。安装完成后验证config集群的istiod状态kubectl get po -n istio-system --contextctx-config -l appistiod # 预期结果1/1 Running3.3 加入primary-a完整的集群接入流程接下来是核心中的核心把primary-a接入config集群让它既保持控制面独立又能接受config集群的配置下发。这里建议按照我下面的顺序执行第一步生成远程访问secret并apply到config集群istioctl x create-remote-secret \ --contextctx-primary-a \ --nameprimary-a \ | kubectl apply --contextctx-config -f -这个secret的作用刚才提过是让config集群的istiod可以“看到”primary-a集群的服务。执行完可以检查一下config集群的istiod日志应该能看到与primary-a建立连接的记录。第二步在primary-a安装Istio控制面注意这里不能再像单集群那样用默认配置了必须显式开启外部控制面的支持能力并告诉primary-a的istiod配置源头是config集群istioctl install --contextctx-primary-a -y \ --set profiledemo \ --set meshConfig.rootNamespaceistio-system \ --set values.global.meshIDmesh-global \ --set values.global.multiCluster.clusterNameprimary-a \ --set values.global.networknetwork-a \ --set values.global.configClustertrue \ --set values.pilot.env.CONFIG_CLUSTER_NAMEconfig-clusterCONFIG_CLUSTER_NAME是一个容易漏掉的环境变量。如果不设置primary-a的istiod默认还是只会从自身集群读取配置不会同步config集群的资源。第三步创建东西向网关这一步和3.1中的网关配置基本一致只需要把ISTIO_META_REQUESTED_NETWORK_VIEW改成network-a。安装完成后务必确认网关的External IP已经拿到并且安全组规则里放行了15012、15017、15443、15021四个端口。这里针对云上部署有个小细节如果用的是阿里云/腾讯云的负载均衡注意选择公网私网监听共存的模式否则东西向网关之间无法通过内网流量互通。第四步在primary-a注入Sidecar这里不细展开和单集群一致重点确认命名空间打了istio-injectionenabled的标签后新起的Pod能拿到Sidecar且Sidecar能正常启动。3.4 加入primary-b对称重复但域名别搞错primary-b的接入流程和primary-a完全一致把clusterName和network对应修改即可。但这里有个我踩过的坑提醒一下remote secret的名称不要混淆。当你执行istioctl x create-remote-secret --nameprimary-b时这个secret在config集群里其实会生成一个名为istio-remote-secret-primary-b的资源。如果你在多个集群之间复制粘贴命令很容易把名字搞混导致secret里的kubeconfig指向错误集群istiod反复连接一个不存在的集群。3.5 状态验证清单整个链路搭建完别急着部署业务先把以下验证项逐条过一遍确保基础环境是正常的# 1. 检查各集群istiod状态 kubectl get po -n istio-system --contextctx-config -l appistiod kubectl get po -n istio-system --contextctx-primary-a -l appistiod kubectl get po -n istio-system --contextctx-primary-b -l appistiod # 2. 检查三个集群的cacerts证书是否一致 kubectl get secret cacerts -n istio-system --contextctx-config -o jsonpath{.data.root-cert\.pem} | base64 -d /tmp/config-ca.pem kubectl get secret cacerts -n istio-system --contextctx-primary-a -o jsonpath{.data.root-cert\.pem} | base64 -d /tmp/a-ca.pem diff /tmp/config-ca.pem /tmp/a-ca.pem # 3. 检查config集群是否能看到primary-a和primary-b的endpoint istioctl proxy-status --contextctx-configistioctl proxy-status的输出里如果能看到所有集群的Sidecar和网关都处于Synced状态说明控制面连通正常。如果某个条目一直显示Not Ready大概率是证书问题或网络连通问题可以直接跳到第4章的排查清单对照处理。我在实际验证环境时还习惯增加一步跨集群连通性测试分别在primary-a和primary-b里面跑一个测试Pod互相curl一下对方集群的Service。这个测试能够提前发现东西向网关转发链路的问题避免后面业务上线时才发现流量根本过不去。3.6 故障注入测试验证真正的“高可用”配置全部落定后接下来就是验证这套方案的成色到底怎么样。我用的是“直接干死一个集群的istiod”这种比较暴力的方式。首先在primary-b集群中部署一个测试服务test-svc然后在primary-a集群中部署一个消费端test-client确认正常情况下请求能正常往返。接下来在primary-b集群中执行kubectl delete deploy istiod -n istio-system --contextctx-primary-b观察primary-a集群中client的调用情况。预期的结果是primary-b的本地业务和istiod同时不可用时primary-a中的client不会被影响因为primary-a的istiod还在正常工作Sidecar还能正常获取到primary-b集群的endpoint信息并做故障转移。此时如果primary-a和primary-b之间有副本流量会自动全部切到primary-a的副本上。这里有一个要点pod的readinessProbe和headless service的关系。如果你的业务服务使用headless service做服务发现很多团队在localityLbSetting生效上吃过亏。我是在实际压测中发现headless service模式下Envoy不会自动剔除不可用的Pod因为endpoint controller不会把它当作普通ClusterIP service那样处理。建议在多集群流量治理场景尽量使用ClusterIP service避免这个hidden trap。4. 常见问题与排查技巧实录这套架构从搭建到稳定运行我整理了6个出现频率最高的问题每个问题都附上了当时的排查思路和最终解决方案。4.1 集群加入后一直是Not Ready表现在config集群执行istioctl proxy-statusprimary-a的条目显示Not Ready。排查路径先用kubectl logs -n istio-system -l appistiod --contextctx-config查看istiod日志。如果出现类似Failed to connect to remote cluster: primary-a先确认remote secret是否应用成功kubectl get secrets -n istio-system --contextctx-config如果secret存在但连接仍然失败基本可以断定是网络连通或kubeconfig权限问题。我遇到过一次是因为remote secret里包含的kubeconfig使用了集群内网域名而config集群所在的网络无法解析primary-a的集群内网域名导致istiod无法建立连接。解决办法是在生成remote secret时通过--kubeconfig参数指定一个使用公网endpoint的kubeconfig。4.2 跨集群mTLS握手失败表现两个集群的Pod互相访问时Sidecar日志中报TLS handshake error: certificate verify failed。排查路径90%的情况是证书环节出了问题。先确认两个集群的cacertssecret里root-cert.pem的指纹一致方法在第3.5节已经有现成命令。另一个隐蔽的原因是证书轮换过期。Istio默认的CA证书有效期为一年在生产环境如果跑了一年多集群的根CA如果没做自动轮换就会在新密钥生效后出现大规模握手失败。建议在规划时就给根CA设长有效期我这次直接设置了10年并为中间CA设置自动轮换策略。4.3 跨集群访问时出现503/502表现配置好ServiceEntry或自动服务发现后跨集群调用时部分请求返回503或502。排查路径这种情况第一个要查的是东西向网关的端口是否全部开放。我的经验是不能只放行1544315012、15017、15021这三个端口任何一个不通都可能导致服务发现或健康检查失败最终表现为跨集群转发请求时出现间歇性503。第二个排查点是target集群的Pod标签和Endpoint。执行kubectl get endpoints -n namespace确认想要跨集群访问的Service有正确的endpoint列表。如果endpoint列表为空看看Pod是否有异常如果endpoint列表有数据但请求仍然失败去查Sidecar配置里的cluster节点确认路由目标是否包含远端endpoint。4.4 配置在target集群不生效表现在config集群创建的VirtualServiceprimary-a集群中没有任何反应。排查路径先确认primary-a的istiod是否启用了CONFIG_CLUSTER_NAME。执行以下命令kubectl get deploy istiod -n istio-system --contextctx-primary-a -o jsonpath{.spec.template.spec.containers[0].env} | grep CONFIG_CLUSTER_NAME如果输出为空说明istiod根本没启用config集群的功能。我当时就是在这一步踩坑重新安装了primary-a的istiod并加上环境变量后配置同步才恢复。加了之后还需要确认primary-a的istiod日志中出现类似GetConfiguration from config-cluster的记录才算真正建立连接。4.5 同步冲突target集群手工修改的配置被覆盖表现在primary-a手工改了某个VirtualService过了一段时间配置自动回到了config集群里的旧版本看起来像“被回滚”。排查结果这个不是故障这就是config cluster模式的预期行为。配置方向是单向的source到target。如果target集群内手工修改了由source管控的配置下次同步周期到来时会被强制覆盖回source的期望值。因此我在第2.3节特意强调配置变更只允许在config集群做这一点在团队规范里需要达成共识否则就是灾难。4.6 故障转移后Pod一直处于Pending或CrashLoopBackOff表现某个集群宕机后另一个集群的副本能正常启动但故障集群恢复后部分Pod出现Pending状态。排查路径这类问题通常不是Istio本身的问题而是K8s调度层面的资源不足。我遇到过一次是故障集群恢复后Sidecar同时注入到大量Pod导致集群CPU/内存资源被瞬时打满部分Pod无法调度。解决思路是提前给istiod和Sidecar设置合理的request和limit并开启HPA避免故障恢复时的资源洪峰。5. 多集群方案的运维注意事项补充5.1 监控与告警多集群环境别做“单集群监控”跨地域高可用这套方案跑起来后有一个非常容易被忽略的新风险监控和告警自身的高可用。如果监控系统只部署在其中一个集群那个集群故障时整个监控体系也会失明。我当时是把Prometheus和Grafana做了联邦部署每个集群独立采集指标Grafana从多个数据源聚合展示这样单个集群故障时监控面板仍然可以展示另一个集群的状态。告警规则也要覆盖多集群场景。至少要有三条任意一个集群的istiod不可用恢复时间目标RT0即自动恢复东西向网关健康检查失败表示跨集群链路异常config集群的配置同步延迟超过5分钟表示跨集群配置管理链路异常5.2 证书轮换和过期风险管理多集群证书的轮换比单集群更复杂因为涉及多个集群同步。我实际操作时采用了这样的流程在config集群生成新的根CA和中间CA在全部集群中的cacerts secret中更新root-cert.pem保持root证书一致依次重启各集群的istiod确保加载新证书滚动升级所有Sidecar让它们重新获取新证书链在非关键集群先验证跨集群mTLS握手确认没问题后再全量推进。整个过程需要协调好窗口期不建议在流量高峰期操作。5.3 版本升级策略先升级config集群再升级业务集群多集群加上config集群后版本升级的顺序和单集群完全不同。我的建议是先把config集群升级到目标版本验证配置同步仍然正常再逐个升级primary集群每次升级一个观察业务流量无异常再继续下一个整个过程中Sidecar的版本和istiod的版本不要跨太多小版本否则可能产生协议兼容问题。这里有一个相对保险的做法用istioctl upgrade之前先在测试集群上完整跑一遍升级流程确认CRD变更和配置兼容性没问题后再在生产集群动手。这次多集群方案做完我个人最大的感受是不能只盯着“装起来”这个目标多集群的真正难点在统一配置管理、证书互通、流量治理策略的一致性和故障演练这四个模块任何一个做不好方案就只能停留在“PPT高可用”层面。如果你正在规划多集群建议先从小规模验证开始两三个集群把链路完全打通做一次真正的kill故障演练再决定要不要铺开到更多的地域节点。控制面高可用这件事靠的不是玄学而是把证书、网络、配置同步、故障转移这几个环节做到滴水不漏。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价