【K8S 运维实战】37-金融企业多集群落地

案例:某金融企业多集群落地

一句话定位:两地三中心、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、审计合规
最终效果 RPO=0(同步双写)、RTO<5 分钟、审计 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 技术挑战

  1. 多集群网络互通:三个机房三种网络环境(同城 VPC peering、异地专线),CNI 跨集群 Pod 通信如何打通?
  2. 应用分发一致性:20+ 系统、上千个微服务,如何保证多集群配置一致、版本一致?
  3. 容灾切换:RPO=0 要求强一致同步,但同步双写性能损耗大,如何在保证一致性的前提下控制延迟?
  4. 数据一致性校验:切换后如何验证数据真的没丢?传统主从校验工具在容器化场景下不适用。

二、方案设计

2.1 多集群架构选型

我们对比了三种方案:

方案 优点 缺点 结论
单集群跨机房 运维简单 etcd 跨机房延迟敏感,脑裂风险 否决
多集群 + Karmada 统一调度、应用分发原生支持 社区成熟度一般、金融案例少 选用(控制平面)
多集群 + ArgoCD GitOps 成熟、审计友好 不做调度、需自研应用分发 选用(应用下发)

最终采用 Karmada(集群调度)+ ArgoCD(应用下发)+ 自研控制台(运维 + 审计) 的组合:
#mermaid-svg-BUkMcUBpv61whUB6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BUkMcUBpv61whUB6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BUkMcUBpv61whUB6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BUkMcUBpv61whUB6 .error-icon{fill:#552222;}#mermaid-svg-BUkMcUBpv61whUB6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BUkMcUBpv61whUB6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BUkMcUBpv61whUB6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BUkMcUBpv61whUB6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BUkMcUBpv61whUB6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BUkMcUBpv61whUB6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BUkMcUBpv61whUB6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BUkMcUBpv61whUB6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BUkMcUBpv61whUB6 .marker.cross{stroke:#333333;}#mermaid-svg-BUkMcUBpv61whUB6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BUkMcUBpv61whUB6 p{margin:0;}#mermaid-svg-BUkMcUBpv61whUB6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BUkMcUBpv61whUB6 .cluster-label text{fill:#333;}#mermaid-svg-BUkMcUBpv61whUB6 .cluster-label span{color:#333;}#mermaid-svg-BUkMcUBpv61whUB6 .cluster-label span p{background-color:transparent;}#mermaid-svg-BUkMcUBpv61whUB6 .label text,#mermaid-svg-BUkMcUBpv61whUB6 span{fill:#333;color:#333;}#mermaid-svg-BUkMcUBpv61whUB6 .node rect,#mermaid-svg-BUkMcUBpv61whUB6 .node circle,#mermaid-svg-BUkMcUBpv61whUB6 .node ellipse,#mermaid-svg-BUkMcUBpv61whUB6 .node polygon,#mermaid-svg-BUkMcUBpv61whUB6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BUkMcUBpv61whUB6 .rough-node .label text,#mermaid-svg-BUkMcUBpv61whUB6 .node .label text,#mermaid-svg-BUkMcUBpv61whUB6 .image-shape .label,#mermaid-svg-BUkMcUBpv61whUB6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-BUkMcUBpv61whUB6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BUkMcUBpv61whUB6 .rough-node .label,#mermaid-svg-BUkMcUBpv61whUB6 .node .label,#mermaid-svg-BUkMcUBpv61whUB6 .image-shape .label,#mermaid-svg-BUkMcUBpv61whUB6 .icon-shape .label{text-align:center;}#mermaid-svg-BUkMcUBpv61whUB6 .node.clickable{cursor:pointer;}#mermaid-svg-BUkMcUBpv61whUB6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BUkMcUBpv61whUB6 .arrowheadPath{fill:#333333;}#mermaid-svg-BUkMcUBpv61whUB6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BUkMcUBpv61whUB6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BUkMcUBpv61whUB6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BUkMcUBpv61whUB6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BUkMcUBpv61whUB6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BUkMcUBpv61whUB6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BUkMcUBpv61whUB6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BUkMcUBpv61whUB6 .cluster text{fill:#333;}#mermaid-svg-BUkMcUBpv61whUB6 .cluster span{color:#333;}#mermaid-svg-BUkMcUBpv61whUB6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BUkMcUBpv61whUB6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BUkMcUBpv61whUB6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-BUkMcUBpv61whUB6 .icon-shape,#mermaid-svg-BUkMcUBpv61whUB6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BUkMcUBpv61whUB6 .icon-shape p,#mermaid-svg-BUkMcUBpv61whUB6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BUkMcUBpv61whUB6 .icon-shape .label rect,#mermaid-svg-BUkMcUBpv61whUB6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BUkMcUBpv61whUB6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BUkMcUBpv61whUB6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BUkMcUBpv61whUB6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Git 仓库 - 单一可信源
ArgoCD ApplicationSet
Karmada 控制平面
k8s-prod 同城A
k8s-dr 同城B
k8s-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),应用冷备(常态不接流量),仅同城双挂才启用。

切换决策矩阵:

故障场景 切换动作 RTO RPO
A 机房单节点故障 K8s 自愈,无人工介入 < 30s 0
A 机房网络抖动 切流到 B(保持数据同步) < 1min 0
A 机房整体故障 切流到 B + DB 主从切换 < 5min 0
同城 A/B 双挂 切到异地 C + DB 提升从库 < 30min ≤ 30s

三、实施过程

3.1 第一阶段:网络互通(2024/02 - 2024/03)

3.1.1 同城 Pod 互通(Calico BGP)

同城 A/B 机房 Pod 需要直通,走 Calico BGP reflect 方案:

yaml 复制代码
# A 机房 calico BGP 配置(BGP peering 到 B 机房)
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
  name: default
spec:
  logSeverityScreen: Info
  nodeToNodeMeshEnabled: false    # 关闭全互联,用 reflect
  asNumber: 64512
---
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: bgp-peer-to-b-rr
spec:
  peerIP: 10.20.0.10              # B 机房 RR 节点
  asNumber: 64513
  node: 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 隧道:

bash 复制代码
# 安装 submariner broker(在 Karmada 控制平面集群)
subctl deploy-broker --kubeconfig karmada.kubeconfig

# A 机房集群注册
subctl join --kubeconfig a.kubeconfig \
  --broker-cluster karmada.kubeconfig \
  --clusterid prod-a \
  --cable-driver ipsec \
  --natt=false

# C 机房集群注册
subctl join --kubeconfig c.kubeconfig \
  --broker-cluster karmada.kubeconfig \
  --clusterid gdr-c \
  --cable-driver ipsec \
  --natt=false

# 验证跨集群 Pod 通信
kubectl --kubeconfig a.kubeconfig exec -it test-pod -- \
  ping 10.40.0.5   # C 机房 Pod IP
3.1.3 跨集群服务发现(Istio)

服务层用 Istio east-west gateway 实现跨集群服务发现:

yaml 复制代码
# Istio 跨集群 ServiceEntry
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: pay-svc-cross-cluster
  namespace: prod
spec:
  hosts:
    - pay-svc.prod.svc.cluster.local
  location: MESH_INTERNAL
  ports:
    - number: 8080
      name: http
      protocol: HTTP
  resolution: DNS
  endpoints:
    - address: pay-svc.prod.svc.cluster.local
      locality: region-a/zone-a    # 同城 A 优先
      weight: 80
    - address: pay-svc.prod.svc.cluster.local
      locality: region-a/zone-b
      weight: 20
    - address: pay-gdr-svc.prod.svc.cluster.local
      locality: region-c/zone-c
      weight: 0                     # 异地常态不接流量

3.2 第二阶段:多集群应用分发(2024/03 - 2024/04)

3.2.1 GitOps + ArgoCD ApplicationSet

所有应用配置在 Git 仓库,ArgoCD 负责下发到多集群:

yaml 复制代码
# ArgoCD ApplicationSet - 同城双活下发
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: core-banking
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - cluster: prod-a
            url: https://k8s-prod-a:6443
            weight: "50"
          - cluster: prod-b
            url: https://k8s-prod-b:6443
            weight: "50"
  template:
    metadata:
      name: 'core-banking-{{cluster}}'
    spec:
      project: banking-prod
      source:
        repoURL: https://git.example.com/banking/core-banking
        targetRevision: main
        path: manifests/overlays/{{cluster}}
      destination:
        server: '{{url}}'
        namespace: banking
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true
          - PruneLast=true
3.2.2 Karmada 调度策略

Karmada 负责跨集群调度,核心用 SpreadByCluster 保证多活:

yaml 复制代码
# Karmada PropagationPolicy
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: pay-svc-propagation
  namespace: banking
spec:
  resourceSelectors:
    - apiVersion: apps/v1
      kind: Deployment
      name: pay-svc
  placement:
    clusterAffinity:
      clusterNames:
        - prod-a
        - prod-b
        - gdr-c
    replicaScheduling:
      replicaSchedulingType: Divided
      replicaDivisionPreference: Weighted
      weightPreference:
        staticWeightList:
          - targetCluster:
              clusterNames: [prod-a]
            weight: 50
          - targetCluster:
              clusterNames: [prod-b]
            weight: 50
          - targetCluster:
              clusterNames: [gdr-c]
            weight: 0          # 异地常态不调度
    spreadConstraints:
      - spreadByLabel: topology.kubernetes.io/zone
        maxGroups: 1
        minGroups: 1

3.3 第三阶段:容灾演练(2024/05 - 2024/06)

3.3.1 演练方案

演练分三次,逐级加码:

轮次 场景 范围 时间
第1轮 切流到同城 B,数据不切 应用层 周末凌晨 2 点
第2轮 A 机房 DB 主从切换 + 应用切流 应用 + DB 周末凌晨 2 点
第3轮 同城双挂,切异地 C 全量 演练环境
3.3.2 切流脚本(同城 A→B)
bash 复制代码
#!/bin/bash
# dr_switch_a_to_b.sh - 同城 A 切流到 B
set -euo pipefail

LOG() { echo "[$(date '+%F %T')] $*"; }

# Step 1: 健康检查 B 集群
LOG "检查 B 集群健康状态"
kubectl --kubeconfig b.kubeconfig get nodes --no-headers | \
  awk '$2=="Ready"' | wc -l | xargs -I{} test {} -ge 800 || { LOG "B 集群节点不足"; exit 1; }

# Step 2: B 集群应用扩容到全量
LOG "B 集群应用扩容"
for svc in pay-svc core-svc account-svc; do
  kubectl --kubeconfig b.kubeconfig scale deploy $svc -n banking --replicas=50
done

# Step 3: 等待 B 集群 Pod Ready
LOG "等待 B 集群 Pod 就绪"
kubectl --kubeconfig b.kubeconfig wait deploy -n banking --all \
  --for=jsonpath='{.status.readyReplicas}'=50 --timeout=180s

# Step 4: 流量权重切换(Istio VirtualService)
LOG "切换流量到 B 集群"
kubectl --kubeconfig karmada.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 分钟业务指标"
sleep 300

# Step 6: 确认稳定后,A 集群缩容
LOG "A 集群缩容"
for svc in pay-svc core-svc account-svc; do
  kubectl --kubeconfig a.kubeconfig scale deploy $svc -n banking --replicas=10
done

LOG "切流完成"
3.3.3 数据一致性校验

DB 切换后必须校验数据,我们用 pt-table-checksum 改造的方案:

bash 复制代码
# 主从数据校验(基于 GTID)
pt-table-checksum \
  --host=master-b \
  --user=checker \
  --password=*** \
  --recursion-method=hosts \
  --no-check-binlog-format \
  --databases=core_accounting \
  --tables=account_balance,transaction_log \
  --chunk-size=10000 \
  --replicate=percona.checksums

# 查看差异
mysql -h master-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 审计日志配置
yaml 复制代码
# apiserver audit policy
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  # 敏感操作全量记录
  - level: RequestResponse
    resources:
      - group: ""
        resources: ["secrets", "configmaps"]
  - level: RequestResponse
    verbs: ["create", "update", "patch", "delete"]
    resources:
      - group: "apps"
        resources: ["deployments", "daemonsets", "statefulsets"]
  # 读操作只记录元数据
  - level: Metadata
    verbs: ["get", "list", "watch"]
  # 日志类不记录
  - level: None
    namespaces: ["kube-system", "logging"]

审计日志通过 filebeat 采集到 ELK,保留 365 天,关键操作接入行内 SOC 平台。

3.4.3 Pod 安全策略(Pod Security Admission)
yaml 复制代码
# Namespace 级别 Pod Security
apiVersion: v1
kind: Namespace
metadata:
  name: banking
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted
---
# NetworkPolicy - 默认拒绝,按需放通
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: banking
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: pay-svc-allow
  namespace: banking
spec:
  podSelector:
    matchLabels:
      app: pay-svc
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: gateway      # 只允许 gateway 访问
      ports:
        - protocol: TCP
          port: 8080

四、踩坑与应急

4.1 跨集群服务调用超时(2024/03 20)

现象:同城 A 集群调用 B 集群服务,偶发 2 秒超时,业务报"跨机房调用慢"。

定位:抓包发现不是业务慢,是 DNS 解析慢。Istio 跨集群服务发现依赖 CoreDNS 的 stubdomain,但 CoreDNS 默认缓存 30 秒,且没配负缓存,导致频繁回源。

修复:

yaml 复制代码
# CoreDNS stubdomain + 缓存优化
apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  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。

修复:

yaml 复制代码
# argocd-cm 限制同步并发
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
data:
  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 秒,超过即降级,避免写请求堆积。

sql 复制代码
-- 半同步配置优化
SET GLOBAL rpl_semi_sync_master_timeout = 3000;       -- 3 秒
SET GLOBAL rpl_semi_sync_master_wait_for_slave_count = 1;

五、复盘与改进

5.1 上线效果

  • 容灾切换 RTO 实测 4 分 12 秒(目标 5 分钟)
  • RPO=0(同城)、RPO ≤ 30s(异地)
  • 资源利用率从原物理机方案的 25% 提升到 58%
  • 应用发布时间从 2 小时缩短到 15 分钟
  • 审计日志覆盖 100%,通过等保三级测评

5.2 经验教训

  1. 金融多集群的核心不是 K8s,是数据层:DB/中间件的跨机房一致性才是难点,K8s 只是应用分发层。
  2. 容灾演练要敢切真生产:演练环境永远发现不了真问题,第 3 轮异地演练我们坚持在准生产环境做,发现 7 个隐患。
  3. GitOps 审计天然友好:所有变更都在 Git 里,审计追溯零成本,这在金融场景是巨大优势。
  4. 网络方案要分层:别指望一种网络方案打天下,Pod/Service/数据三层各选各的。
  5. 合规要前置:别等上线才补合规,等保三级要求从架构设计阶段就要嵌入,我们项目专门有"合规 review"环节。
  6. 半同步复制要调参:默认参数不适合跨机房,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 restricted PSA
网络 NetworkPolicy 默认拒绝 Calico
网络 跨集群流量加密 mTLS/IPSec
审计 apiserver 全量审计 audit policy
审计 运行时行为审计 Falco
加密 Secret 加密存储 etcd KMS
加密 存储卷加密 CSI + KMS
镜像 私有仓库 + 扫描 Harbor + Trivy
镜像 只读根文件系统 securityContext
合规 节点 CIS 扫描 kube-bench
合规 定期渗透测试 第三方

思考题

  1. 同城双活场景下,如果 DB 半同步复制持续降级为异步,你的告警和应急策略是什么?
  2. Karmada 和 ArgoCD 在多集群分发上职责如何划分?能否只用其中一个?
  3. 金融场景下,Pod 安全合规和研发效率如何平衡?restricted 策略会不会影响业务?

延伸阅读

  • Karmada 官方文档:https://karmada.io/docs/
  • 银保监《商业银行信息科技风险管理指引》
  • Istio 跨集群服务网格:Multi-Cluster Install
  • 等保 2.0 三级要求与 K8s 落地对照
  • Submariner 跨集群网络:https://submariner.io/
相关推荐
行者-全栈开发2 小时前
【Linux内核】CVE-2026-46242:Bad Epoll Linux 内核 epoll UAF 漏洞修复指南(99%可靠root的云安全噩梦)
linux·运维·服务器·cve-2026-46242·linux内核漏洞·use-after-free·云多租户安全
平安的平安2 小时前
人在外地,怎样访问办公室里的电脑和内部资源?
运维·服务器·电脑
智体工坊2 小时前
从零搭建 AI 模型中转网关:部署、渠道、定价、装修全记录
运维·服务器·人工智能·搜索引擎·自动化
信鸽爱好者2 小时前
一人多台电脑办公模式:windows 电脑A远程桌面操作ubuntu电脑B
linux·运维·ubuntu
元岳数字人小元2 小时前
易部署易运维!AI数字人一体机实现场景长效运营
运维·人工智能·人机交互·交互·源代码管理
凌虚(失业了求个工作)2 小时前
Kubernetes 编年史:从 Borg 到云原生操作系统
云原生·容器·kubernetes
深圳市益普科技有限公司2 小时前
半导体mes厂家有哪些?从自动检验与AOI集成看mes厂家的检验自动化水平
运维·自动化
1234Wu2 小时前
AI赋能:2026年金融、零售、农业、制造业与中小企业的智能化转型全景
人工智能·金融·零售
SLD_Allen3 小时前
从Cloud Native到AI Native:K8s DRA与Agent协议栈
人工智能·云原生·kubernetes·cloud native·ai native