工程实践 架构心得

集群高可用:银行系统的四个九设计

从 K8s Pod 高可用到多活架构,详解银行系统如何设计 99.99% SLA 的高可用集群。

发布于 2026/03/20 更新于 2026/03/20 2 分钟

“银行系统的可用性不是锦上添花,而是监管合规的底线。“

前言

银行系统的可用性要求极高:

行业典型 SLA年可容忍停机时间
普通互联网99.9%(三个九)8.7 小时
银行核心系统99.99%(四个九)52 分钟
支付清算系统99.999%(五个九)5 分钟

每降低一个九,意味着更多的客户投诉、更大的监管风险。更关键的是:可用性不足不只是技术问题,而是业务问题——交易中断意味着真实的经济损失。

1. K8s 集群高可用:Pod 层面

1.1 Pod 反亲和(Anti-Affinity)

确保同一服务的多个 Pod 分布在不同节点:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
spec:
  replicas: 3   # 银行核心服务最少3副本
  template:
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values:
                      - payment-service
              topologyKey: kubernetes.io/hostname  # 强制分布在不同 Node
        # 软亲和:尽量分布在不同 AZ
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - payment-service
                topologyKey: topology.kubernetes.io/zone

1.2 就绪探针与存活探针

银行服务绝不应该是”重启治百病”——存活探针(liveness)设置过于激进会导致频繁重启、服务震荡。

livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  initialDelaySeconds: 60     # 银行服务启动慢,延迟要够
  periodSeconds: 15
  failureThreshold: 5          # 连续5次失败才重启 = 75秒
  timeoutSeconds: 10

readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 5
  failureThreshold: 3           # 3次失败就摘除流量
  successThreshold: 1

# 关键原则:
# livenessProbe → 判断是否需要重启(保守,宁宽勿严)
# readinessProbe → 判断是否接收流量(严格,影响用户体验)

1.3 Pod Disruption Budget(PDB)

滚动更新时,保证始终有足够 Pod 可用:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: payment-service-pdb
spec:
  minAvailable: 2    # 滚动更新时,至少2个 Pod 保持可用
  selector:
    matchLabels:
      app: payment-service

2. 多可用区(AZ)部署

单 AZ 故障会导致整个集群不可用,银行必须跨 AZ 部署:

# 节点池按可用区分布
nodePools:
  - name: payment-pool-az-a
    availabilityZone: eu-west-1a
    replicas: 3
  - name: payment-pool-az-b
    availabilityZone: eu-west-1b
    replicas: 3
  - name: payment-pool-az-c
    availabilityZone: eu-west-1c
    replicas: 3

银行关键规则:同一笔交易的写入至少要落在两个不同 AZ 上,防止单 AZ 故障导致数据丢失。

3. 数据库高可用架构

3.1 MySQL MGR(MySQL Group Replication)

银行推荐 MGR 而非传统主从复制——MGR 自动故障切换,数据强一致:

Primary(AZ-A) ←→ Replica1(AZ-B) ←→ Replica2(AZ-C)

              自动故障切换

              Primary(AZ-B)  ← 数据无丢失
-- 配置 MGR(单主模式)
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST = 'mysqlmgr1.internal',
  SOURCE_USER = 'repl_user',
  SOURCE_PASSWORD = 'xxx',
  SOURCE_AUTO_POSITION = 1,
  SOURCE_CONNECTION_TIMEOUT = 5,
  SOURCE_RETRY_COUNT = 3;

START GROUP_REPLICATION;

3.2 连接池高可用配置(Spring Boot + HikariCP)

spring:
  datasource:
    hikari:
      # 连接超时:快速失败,不让请求无限等待
      connection-timeout: 3000        # 3秒超时
      # 空闲超时:及时释放无用连接
      idle-timeout: 600000           # 10分钟
      # 最大生命周期:定期刷新连接,防止 MySQL wait_timeout 断开
      max-lifetime: 1800000          # 30分钟
      # 连接测试:每次获取连接前测试连通性
      connection-test-query: SELECT 1
      # 最大连接数
      maximum-pool-size: 20
      minimum-idle: 5

4. 流量切换:DNS 与 Load Balancer

4.1 主动-被动故障切换

正常状态:
  用户 → DNS(华东) → Primary Region(Active)

AZ 故障时(TTL=60秒自动切换):
  用户 → DNS(华东) → Secondary Region(Standby)
# Route53 健康检查 + 故障转移
aws route53 create-health-check \
  --caller-reference $(date +%s) \
  --health-check-config '{
    "Type": "HTTPS",
    "FullyQualifiedDomainName": "payment.hsbctech.internal",
    "Port": 443,
    "ResourcePath": "/actuator/health",
    "RequestInterval": 10,
    "FailureThreshold": 3
  }'

aws route53 create-traffic-policy-instance \
  --hosted-zone-id Z1234567890ABC \
  --name payment.hsbctech.internal \
  --ttl 60 \
  --traffic-policy-id xxx \
  --traffic-policy-version 1

4.2 K8s Service + 外部 Load Balancer

apiVersion: v1
kind: Service
metadata:
  name: payment-service
  annotations:
    # 华为云/阿里云多 AZ 负载均衡
    service.kubernetes.io/loadbalancer-multicloud: enabled
    # 连接耗尽超时(等待中的请求处理完再关闭)
    service.kubernetes.io/connection-drain-timeout: 300s
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local  # 保留源 IP(银行合规需要)
  healthCheckNodePort: 30000
  ports:
    - port: 443
      targetPort: 8080
  selector:
    app: payment-service

5. 故障演练:Chaos Engineering

高可用不是设计出来的,是测出来的。银行每月做一次故障演练:

演练场景预期结果通过标准
单 AZ 断电自动切换到其他 AZRTO < 5 分钟
单 Pod 崩溃K8s 自动重启新 Pod自动恢复,不丢请求
数据库主节点故障MGR 自动选主RPO = 0(零数据丢失)
网络分区 30 秒不产生脑裂/双写数据一致
# 使用 Chaos Mesh 做故障注入
kubectl apply -f - <<'EOF'
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
  name: pod-failure-test
spec:
  action: pod-failure
  mode: one
  duration: 60s
  selector:
    namespaces:
      - payment
    labelSelectors:
      app: payment-service
EOF

6. 高可用设计原则

原则说明反例
消除单点每个组件至少 2 副本,跨 AZ 分布单数据库主节点
快速失败超时设置合理,不让请求无限等待connection-timeout=0
优雅降级核心功能优先,非核心功能可暂时不可用所有功能同等优先级
数据多副本跨 AZ 复制,保证 RPO=0单 AZ 数据存储
定期演练Chaos Engineering 验证假设只设计不测试
监控先行有 SLA 就要有对应的 SLO 和告警事后才知道挂了

相关阅读:分布式系统可观测性实战 · Kubernetes 完全指南 · [Redis 实现分布式锁](/coding/Redis/Redis 实现分布式锁)