“银行系统的可用性不是锦上添花,而是监管合规的底线。“
前言
银行系统的可用性要求极高:
| 行业 | 典型 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 断电 | 自动切换到其他 AZ | RTO < 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 实现分布式锁)