
案例:某金融企业多集群落地一句话定位:两地三中心、2000 节点、等保三级、日交易千万级,一家城商行如何在 K8s 上落地金融级多集群,并通过容灾演练与合规整改把能部署变成敢上线。写在前面金融行业对 K8s 的态度过去几年很矛盾:一边是业务侧喊着要云原生、要微服务、要弹性;另一边是科技安全侧盯着等保、审计、容灾,生怕容器化后失控。我所在的团队 2023 年开始负责某城商行的容器云平台建设,从单集群试点到多集群生产,前后跑了 14 个月,踩的坑大多不在 K8s 本身,而在金融场景对架构的额外约束上。这篇文章复盘的是 2024 年 Q2 完成的多集群生产化项目:两地三中心、2000 节点、20 业务系统、日交易峰值 1200 万笔。重点不是讲 K8s 怎么装,而是讲在金融约束下,多集群的拓扑怎么设计、网络怎么互通、应用怎么分发、容灾怎么演练、合规怎么落地。这些经验对同样在金融、政企行业做 K8s 落地的同学应该有直接参考价值。金融场景的容灾不是能切就行,而是切完数据不丢、业务不断、审计可查,这三个要求把方案复杂度提升了一个数量级。案例概览维度内容客户某城商行(资产规模 8000 亿级)业务规模20 核心业务系统,日交易峰值 1200 万笔基础设施两地三中心:同城 A/B 机房 异地灾备 C 机房集群规模6 个 K8s 集群,共 2000 节点合规要求等保三级、银保监 153 号文、人行金融科技合规时间跨度2023/03 立项 → 2024/06 生产上线 → 2024/09 容灾演练通过关键挑战网络互通、数据一致性、容灾 RPO/RTO、审计合规最终效果RPO0(同步双写)、RTO5 分钟、审计 100% 覆盖集群拓扑:┌─────────────────────────────┐ │ 统一控制平面 │ │ (Karmada ArgoCD 自研) │ └──────────────┬──────────────┘ │ ┌──────────────────────────┼──────────────────────────┐ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ 同城A机房 │◀──专线10ms──▶│ 同城B机房 │◀──专线30ms──▶│ 异地C机房 │ │ k8s-prod │ │ k8s-dr │ │ k8s-gdr │ │ 800节点 │ │ 800节点 │ │ 400节点 │ │ 主交易 │ │ 同城备 │ │ 异地灾备 │ └─────┬───┘ └─────┬───┘ └─────┬───┘ │ │ │ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │ MySQL主 │◀──同步复制──▶│ MySQL从 │◀──异步复制──▶│ MySQL从 │ │ Redis主 │◀──同步复制──▶│ Redis从 │◀──异步复制──▶│ Redis从 │ └─────────┘ └─────────┘ └─────────┘一、背景与挑战1.1 业务背景该行原有核心系统跑在传统物理机 中间件上,2022 年启动分布式核心改造,新核心基于微服务架构,需容器化部署。一期上线 20 系统:核心账务、支付清算、信贷、风控、客户管理、手机银行后端等。日交易峰值 1200 万笔,其中核心账务 300 万笔,对数据库一致性要求极高。1.2 合规约束金融场景的特殊性主要体现在合规要求上,这是和互联网公司最大的区别:等保三级:三级要求安全标记 强制访问控制 安全审计 边界防护 双因子认证,映射到 K8s 上意味着 Pod 级别隔离、全量 API 审计、网络策略必须开启。银保监 153 号文:要求重要信息系统应具备同城双活 异地灾备能力,RTO ≤ 5 分钟,RPO ≤ 1 分钟。数据本地化:客户敏感数据不得跨中心明文传输,必须加密 脱敏。审计可追溯:所有运维操作、配置变更、应用部署必须有审计日志,保留 ≥ 180 天。1.3 技术挑战多集群网络互通:三个机房三种网络环境(同城 VPC peering、异地专线),CNI 跨集群 Pod 通信如何打通?应用分发一致性:20 系统、上千个微服务,如何保证多集群配置一致、版本一致?容灾切换:RPO0 要求强一致同步,但同步双写性能损耗大,如何在保证一致性的前提下控制延迟?数据一致性校验:切换后如何验证数据真的没丢?传统主从校验工具在容器化场景下不适用。二、方案设计2.1 多集群架构选型我们对比了三种方案:方案优点缺点结论单集群跨机房运维简单etcd 跨机房延迟敏感,脑裂风险否决多集群 Karmada统一调度、应用分发原生支持社区成熟度一般、金融案例少选用(控制平面)多集群 ArgoCDGitOps 成熟、审计友好不做调度、需自研应用分发选用(应用下发)最终采用Karmada(集群调度) ArgoCD(应用下发) 自研控制台(运维 审计)的组合:Git 仓库 - 单一可信源ArgoCD ApplicationSetKarmada 控制平面k8s-prod 同城Ak8s-dr 同城Bk8s-gdr 异地C自研运维控制台审计日志中心统一监控告警2.2 网络互通方案网络是金融多集群最复杂的部分。我们的设计原则是Pod 通信走 Overlay、服务通信走 Ingress、数据通信走专线:┌──────────────────────────────────────────────────────────┐ │ 应用层:Service Mesh(Istio 跨集群) │ │ - 跨集群服务发现(east-west gateway) │ │ - mTLS 加密 │ │ - 流量调度(同机房优先 故障切流) │ ├──────────────────────────────────────────────────────────┤ │ CNI 层:Calico BGP(reflect) Submariner │ │ - 同城机房 Pod 直通(BGP Peering) │ │ - 异地机房走 Submariner 隧道(IPSec 加密) │ ├──────────────────────────────────────────────────────────┤ │ 物理层:专线 VPC Peering │ │ - 同城 A-B:VPC Peering,延迟 2ms │ │ - 同城-异地:专线,延迟 30ms,带宽 10Gbps │ │ - 全程 IPSec 加密(合规要求) │ └──────────────────────────────────────────────────────────┘2.3 容灾切换设计按银保监要求,RTO ≤ 5 分钟、RPO ≤ 1 分钟。我们采用同城双活 异地灾备:同城 A/B:MySQL 主主同步(半同步复制),Redis 集群跨机房部署,应用双活负载,故障时切流不切数据。异地 C:MySQL 异步复制(RPO ≤ 30s),应用冷备(常态不接流量),仅同城双挂才启用。切换决策矩阵:故障场景切换动作RTORPOA 机房单节点故障K8s 自愈,无人工介入 30s0A 机房网络抖动切流到 B(保持数据同步) 1min0A 机房整体故障切流到 B DB 主从切换 5min0同城 A/B 双挂切到异地 C DB 提升从库 30min≤ 30s三、实施过程3.1 第一阶段:网络互通(2024/02 - 2024/03)3.1.1 同城 Pod 互通(Calico BGP)同城 A/B 机房 Pod 需要直通,走 Calico BGP reflect 方案:# A 机房 calico BGP 配置(BGP peering 到 B 机房)apiVersion:projectcalico.org/v3kind:BGPConfigurationmetadata:name:defaultspec:logSeverityScreen:InfonodeToNodeMeshEnabled:false# 关闭全互联,用 reflectasNumber:64512---apiVersion:projectcalico.org/v3kind:BGPPeermetadata:name:bgp-peer-to-b-rrspec:peerIP:10.20.0.10# B 机房 RR 节点asNumber:64513node:node-a-rr-01# A 机房 RR 节点routerID:10.10.0.10---# A 机房 Pod CIDR: 10.10.0.0/16# B 机房 Pod CIDR: 10.20.0.0/16# 路由通过 BGP 互通,Pod 直达3.1.2 异地 Pod 互通(Submariner)异地延迟 30ms,BGP 直通成本高,改用 Submariser IPSec 隧道:# 安装 submariner broker(在 Karmada 控制平面集群)subctl deploy-broker--kubeconfigkarmada.kubeconfig# A 机房集群注册subctljoin--kubeconfiga.kubeconfig\--broker-cluster karmada.kubeconfig\--clusteridprod-a\--cable-driver ipsec\--nattfalse# C 机房集群注册subctljoin--kubeconfigc.kubeconfig\--broker-cluster karmada.kubeconfig\--clusteridgdr-c\--cable-driver ipsec\--nattfalse# 验证跨集群 Pod 通信kubectl--kubeconfiga.kubeconfigexec-ittest-pod --\ping10.40.0.5# C 机房 Pod IP3.1.3 跨集群服务发现(Istio)服务层用 Istio east-west gateway 实现跨集群服务发现:# Istio 跨集群 ServiceEntryapiVersion:networking.istio.io/v1beta1kind:ServiceEntrymetadata:name:pay-svc-cross-clusternamespace:prodspec:hosts:-pay-svc.prod.svc.cluster.locallocation:MESH_INTERNALports:-number:8080name:httpprotocol:HTTPresolution:DNSendpoints:-address:pay-svc.prod.svc.cluster.locallocality:region-a/zone-a# 同城 A 优先weight:80-address:pay-svc.prod.svc.cluster.locallocality:region-a/zone-bweight:20-address:pay-gdr-svc.prod.svc.cluster.locallocality:region-c/zone-cweight:0# 异地常态不接流量3.2 第二阶段:多集群应用分发(2024/03 - 2024/04)3.2.1 GitOps ArgoCD ApplicationSet所有应用配置在 Git 仓库,ArgoCD 负责下发到多集群:# ArgoCD ApplicationSet - 同城双活下发apiVersion:argoproj.io/v1alpha1kind:ApplicationSetmetadata:name:core-bankingnamespace:argocdspec:generators:-list:elements:-cluster:prod-aurl:https://k8s-prod-a:6443weight:50-cluster:prod-burl:https://k8s-prod-b:6443weight:50template:metadata:name:core-banking-{{cluster}}spec:project:banking-prodsource:repoURL:https://git.example.com/banking/core-bankingtargetRevision:mainpath:manifests/overlays/{{cluster}}destination:server:{{url}}namespace:bankingsyncPolicy:automated:prune:trueselfHeal:truesyncOptions:-CreateNamespacetrue-PruneLasttrue3.2.2 Karmada 调度策略Karmada 负责跨集群调度,核心用SpreadByCluster保证多活:# Karmada PropagationPolicyapiVersion:policy.karmada.io/v1alpha1kind:PropagationPolicymetadata:name:pay-svc-propagationnamespace:bankingspec:resourceSelectors:-apiVersion:apps/v1kind:Deploymentname:pay-svcplacement:clusterAffinity:clusterNames:-prod-a-prod-b-gdr-creplicaScheduling:replicaSchedulingType:DividedreplicaDivisionPreference:WeightedweightPreference:staticWeightList:-targetCluster:clusterNames:[prod-a]weight:50-targetCluster:clusterNames:[prod-b]weight:50-targetCluster:clusterNames:[gdr-c]weight:0# 异地常态不调度spreadConstraints:-spreadByLabel:topology.kubernetes.io/zonemaxGroups:1minGroups:13.3 第三阶段:容灾演练(2024/05 - 2024/06)3.3.1 演练方案演练分三次,逐级加码:轮次场景范围时间第1轮切流到同城 B,数据不切应用层周末凌晨 2 点第2轮A 机房 DB 主从切换 应用切流应用 DB周末凌晨 2 点第3轮同城双挂,切异地 C全量演练环境3.3.2 切流脚本(同城 A→B)#!/bin/bash# dr_switch_a_to_b.sh - 同城 A 切流到 Bset-euopipefailLOG(){echo[$(date%F %T)]$*;}# Step 1: 健康检查 B 集群LOG检查 B 集群健康状态kubectl--kubeconfigb.kubeconfig get nodes --no-headers|\awk$2Ready|wc-l|xargs-I{}test{}-ge800||{LOGB 集群节点不足;exit1;}# Step 2: B 集群应用扩容到全量LOGB 集群应用扩容forsvcinpay-svc core-svc account-svc;dokubectl--kubeconfigb.kubeconfig scale deploy$svc-nbanking--replicas50done# Step 3: 等待 B 集群 Pod ReadyLOG等待 B 集群 Pod 就绪kubectl--kubeconfigb.kubeconfigwaitdeploy-nbanking--all\--forjsonpath{.status.readyReplicas}50--timeout180s# Step 4: 流量权重切换(Istio VirtualService)LOG切换流量到 B 集群kubectl--kubeconfigkarmada.kubeconfig apply-f-EOF apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: pay-svc-vs namespace: banking spec: hosts: [pay-svc.banking.svc.cluster.local] http: - route: - destination: host: pay-svc.banking.svc.cluster.local subset: zone-a weight: 0 - destination: host: pay-svc.banking.svc.cluster.local subset: zone-b weight: 100 EOF# Step 5: 观察业务指标 5 分钟LOG观察 5 分钟业务指标sleep300# Step 6: 确认稳定后,A 集群缩容LOGA 集群缩容forsvcinpay-svc core-svc account-svc;dokubectl--kubeconfiga.kubeconfig scale deploy$svc-nbanking--replicas10doneLOG切流完成3.3.3 数据一致性校验DB 切换后必须校验数据,我们用 pt-table-checksum 改造的方案:# 主从数据校验(基于 GTID)pt-table-checksum\--hostmaster-b\--userchecker\--password***\--recursion-methodhosts\--no-check-binlog-format\--databasescore_accounting\--tablesaccount_balance,transaction_log\--chunk-size10000\--replicatepercona.checksums# 查看差异mysql-hmaster-b-e SELECT db, tbl, chunk, this_cnt, master_cnt, this_crc master_crc AS diff FROM percona.checksums WHERE this_crc master_crc;3.4 第四阶段:合规整改(2024/04 - 2024/05)3.4.1 等保三级整改清单项要求K8s 落地身份认证双因子接入行内统一 IAM,OIDC 动态口令访问控制强制访问Pod Security Admission NetworkPolicy安全审计全量审计apiserver audit Falco 运行时审计边界防护入侵检测Calico 网络策略 入侵检测 HIDS数据加密传输 存储mTLS 存储加密(CSI 加密卷)漏洞扫描定期Trivy 镜像扫描 节点 CIS 扫描3.4.2 审计日志配置# apiserver audit policyapiVersion:audit.k8s.io/v1kind:Policyrules:# 敏感操作全量记录-level:RequestResponseresources:-group:resources:[secrets,configmaps]-level:RequestResponseverbs:[create,update,patch,delete]resources:-group:appsresources:[deployments,daemonsets,statefulsets]# 读操作只记录元数据-level:Metadataverbs:[get,list,watch]# 日志类不记录-level:Nonenamespaces:[kube-system,logging]审计日志通过 filebeat 采集到 ELK,保留 365 天,关键操作接入行内 SOC 平台。3.4.3 Pod 安全策略(Pod Security Admission)# Namespace 级别 Pod SecurityapiVersion:v1kind:Namespacemetadata:name:bankinglabels:pod-security.kubernetes.io/enforce:restrictedpod-security.kubernetes.io/audit:restrictedpod-security.kubernetes.io/warn:restricted---# NetworkPolicy - 默认拒绝,按需放通apiVersion:networking.k8s.io/v1kind:NetworkPolicymetadata:name:default-denynamespace:bankingspec:podSelector:{}policyTypes:-Ingress-Egress---apiVersion:networking.k8s.io/v1kind:NetworkPolicymetadata:name:pay-svc-allownamespace:bankingspec:podSelector:matchLabels:app:pay-svcpolicyTypes:-Ingressingress:-from:-podSelector:matchLabels:app:gateway# 只允许 gateway 访问ports:-protocol:TCPport:8080四、踩坑与应急4.1 跨集群服务调用超时(2024/03 20)现象:同城 A 集群调用 B 集群服务,偶发 2 秒超时,业务报跨机房调用慢。定位:抓包发现不是业务慢,是 DNS 解析慢。Istio 跨集群服务发现依赖 CoreDNS 的 stubdomain,但 CoreDNS 默认缓存 30 秒,且没配负缓存,导致频繁回源。修复:# CoreDNS stubdomain 缓存优化apiVersion:v1kind:ConfigMapmetadata:name:corednsnamespace:kube-systemdata:Corefile:|.:53 { cache 60 # 正缓存 60 秒 cache 30 denial # 负缓存 30 秒 forward . /etc/resolv.conf } cluster-b.local:53 { forward . 10.20.0.10 # B 集群 DNS cache 120 }同时调整 Istio sidecar 的 DNS 缓存。修复后跨集群调用 P99 稳定在 30ms。4.2 ArgoCD 同步风暴(2024/04 12)现象:某次 Git 提交后,ArgoCD 同时触发 200 Application 同步,apiserver CPU 飙到 95%,同步全部卡死。定位:ArgoCD 默认无并发限制,所有 Application 同时 sync 打爆 apiserver。修复:# argocd-cm 限制同步并发apiVersion:v1kind:ConfigMapmetadata:name:argocd-cmnamespace:argocddata:application.instanceLabelKey:argocd.argoproj.io/instance# 限制并发同步数server.sync.max.concurrent:20并改为按业务系统分批同步,每批 20 个,间隔 30 秒。4.3 容灾演练 DB 主从切换卡住(2024/05 18)现象:第 2 轮演练,MySQL A→B 主从切换,半同步复制卡住 90 秒才完成。定位:半同步复制要求至少一个从库 ACK,但 B 机房有个延迟从库(故意延迟 1 小时,用于误操作恢复),它的 ACK 永远到不了,触发半同步超时降级异步。修复:把延迟从库从半同步复制组剔除,改为基于 binlog dump 的异步独立同步。同时把半同步超时从 10 秒调到 3 秒,超过即降级,避免写请求堆积。-- 半同步配置优化SETGLOBALrpl_semi_sync_master_timeout3000;-- 3 秒SETGLOBALrpl_semi_sync_master_wait_for_slave_count1;五、复盘与改进5.1 上线效果容灾切换 RTO 实测 4 分 12 秒(目标 5 分钟)RPO0(同城)、RPO ≤ 30s(异地)资源利用率从原物理机方案的 25% 提升到 58%应用发布时间从 2 小时缩短到 15 分钟审计日志覆盖 100%,通过等保三级测评5.2 经验教训金融多集群的核心不是 K8s,是数据层:DB/中间件的跨机房一致性才是难点,K8s 只是应用分发层。容灾演练要敢切真生产:演练环境永远发现不了真问题,第 3 轮异地演练我们坚持在准生产环境做,发现 7 个隐患。GitOps 审计天然友好:所有变更都在 Git 里,审计追溯零成本,这在金融场景是巨大优势。网络方案要分层:别指望一种网络方案打天下,Pod/Service/数据三层各选各的。合规要前置:别等上线才补合规,等保三级要求从架构设计阶段就要嵌入,我们项目专门有合规 review环节。半同步复制要调参:默认参数不适合跨机房,timeout 和 wait_count 必须根据网络延迟调。5.3 长期改进推进单元化架构,把跨机房调用从尽力而为变成架构保证引入 Karmada 1.12 的故障自动迁移能力,减少人工切换审计日志接入 AI 分析,异常操作实时告警探索异地多活,把异地 C 从灾备升级为双活六、可复用产出6.1 容灾切换 SOP# 同城 A→B 切换 SOP 1. 触发条件:A 机房整体故障 / 网络中断 2 分钟 2. 决策权限:值班指挥(技术总监)确认,运维执行 3. 执行步骤: 3.1 健康检查 B 集群(kubectl get nodes) 3.2 B 集群应用扩容到全量 3.3 等待 Pod Ready(超时 3 分钟则回滚) 3.4 Istio 流量切换 A→B 3.5 观察 5 分钟业务指标 3.6 DB 主从切换(若 A 整体故障) 3.7 数据一致性校验 3.8 通知业务方确认 4. 回滚条件:B 集群 5 分钟内未恢复业务指标 80% 5. 演练频率:每季度 1 次6.2 合规整改清单(金融 K8s 专版)类别检查项工具/配置身份集群 API 双因子OIDC OTP身份kubeconfig 定期轮换脚本 审计访问RBAC 最小权限Namespace 级 Role访问Pod Security restrictedPSA网络NetworkPolicy 默认拒绝Calico网络跨集群流量加密mTLS/IPSec审计apiserver 全量审计audit policy审计运行时行为审计Falco加密Secret 加密存储etcd KMS加密存储卷加密CSI KMS镜像私有仓库 扫描Harbor Trivy镜像只读根文件系统securityContext合规节点 CIS 扫描kube-bench合规定期渗透测试第三方思考题同城双活场景下,如果 DB 半同步复制持续降级为异步,你的告警和应急策略是什么?Karmada 和 ArgoCD 在多集群分发上职责如何划分?能否只用其中一个?金融场景下,Pod 安全合规和研发效率如何平衡?restricted 策略会不会影响业务?延伸阅读Karmada 官方文档:https://karmada.io/docs/银保监《商业银行信息科技风险管理指引》Istio 跨集群服务网格:Multi-Cluster Install等保 2.0 三级要求与 K8s 落地对照Submariner 跨集群网络:https://submariner.io/