首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >K8s高可用集群部署实战:某国有银行电子商城容器化迁移全记录

K8s高可用集群部署实战:某国有银行电子商城容器化迁移全记录

原创
作者头像
搜weiranit.fun
发布2026-08-13 14:14:03
发布2026-08-13 14:14:03
1520
举报

K8s高可用集群部署实战:某国有银行电子商城容器化迁移全记录

从传统单体到金融级多活K8s架构,我们如何将99.99%可用性承诺落地为800+个YAML文件

引言

某国有银行电子商城(日活峰值120万,SKU超300万,大促期间QPS峰值突破8万)在2024年底完成了从VMware虚拟机向Kubernetes的全量迁移。该项目历时9个月,涉及6个核心交易子系统、23个中台微服务,最终交付的K8s集群横跨3个可用区(同城双活+异地灾备),集群控制面SLA达到99.99%,数据面Pod调度延迟P99控制在120ms以内

本文不聊"安装minikube"或"单机部署",直接复盘生产级高可用集群基建 + 金融级安全合规 + 大促弹性扩缩容的全套实战方案。文中所有YAML/Shell均经过生产环境脱敏验证,附赠踩坑血泪史。


1. 集群架构与硬件选型

1.1 拓扑设计(3AZ架构)

可用区

角色

节点数量

配置

网络延迟

AZ-A(主生产)

控制面 + 工作负载

5 Master + 20 Worker

16C64G / 1.6T NVMe SSD

-

AZ-B(同城备)

控制面 + 工作负载

3 Master + 15 Worker

16C64G / 1.6T NVMe SSD

< 2ms (光纤直连)

AZ-C(异地灾备)

仅数据面(冷备)

0 Master + 10 Worker

8C32G / SSD

35ms (专线)

关键设计决策

  • 采用 5 Master(AZ-A)+ 3 Master(AZ-B) 的"5+3"奇数总和控制面,不启用AZ-C的控制面以避免脑裂风险。
  • ETCD集群独立部署于3台物理机(不与Master混部),使用NVMe RAID 10磁盘,强制开启TLS双向认证
  • 操作系统统一使用 Rocky Linux 9.2,内核升级至 5.15.131-1.el9(修复CVE-2023-44487漏洞)。

1.2 基础设施前置调优(sysctl)

代码语言:javascript
复制
# /etc/sysctl.d/99-k8s-hardening.conf
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.ip_local_reserved_ports = 30000-32767   # 避免NodePort冲突
net.core.somaxconn = 32768
net.ipv4.tcp_max_syn_backlog = 8096
net.core.netdev_max_backlog = 20000
fs.file-max = 2097152
fs.inotify.max_user_instances = 8192
fs.inotify.max_user_watches = 524288

# 关闭Swap(K8s强制要求)
swapoff -a && sed -i '/swap/d' /etc/fstab

# 加载内核模块(适用于Calico eBPF模式)
modprobe br_netfilter
modprobe overlay
cat > /etc/modules-load.d/k8s.conf << EOF
overlay
br_netfilter
EOF

2. 控制面高可用:kubeadm + Keepalived + HAProxy

2.1 负载均衡器架构

我们不使用云厂商SLB,而是自建 2台独立LB节点(主备)+ 虚拟IP(VIP),作为API Server的入口。

/etc/haproxy/haproxy.cfg(关键段):

代码语言:javascript
复制
global
    log /dev/log local0
    maxconn 100000
    user haproxy
    group haproxy
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
    ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11

defaults
    mode tcp
    timeout connect 5s
    timeout client 30s
    timeout server 30s
    retries 3

frontend k8s-api
    bind *:6443
    default_backend k8s-masters

backend k8s-masters
    balance roundrobin
    option tcp-check
    # 5台Master节点
    server master-1 10.0.1.11:6443 check fall 3 rise 2 inter 5s
    server master-2 10.0.1.12:6443 check fall 3 rise 2 inter 5s
    server master-3 10.0.1.13:6443 check fall 3 rise 2 inter 5s
    server master-4 10.0.2.11:6443 check fall 3 rise 2 inter 5s  # AZ-B
    server master-5 10.0.2.12:6443 check fall 3 rise 2 inter 5s  # AZ-B

Keepalived配置(VIP 10.0.0.100/32):

代码语言:javascript
复制
vrrp_instance VI_1 {
    state MASTER              # 备机为BACKUP
    interface eth0
    virtual_router_id 51
    priority 150              # 备机降低至100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass ${KEEPALIVED_PWD}
    }
    virtual_ipaddress {
        10.0.0.100/32
    }
    track_script {
        chk_haproxy
    }
}

# 健康检查脚本
vrrp_script chk_haproxy {
    script "/usr/bin/killall -0 haproxy"
    interval 2
    weight 20
}

2.2 kubeadm初始化配置文件(带证书自定义)

kubeadm-config.yaml

代码语言:javascript
复制
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.28.8
controlPlaneEndpoint: "10.0.0.100:6443"   # VIP
apiServer:
  certSANs:
  - "10.0.0.100"
  - "k8s-api.bank.internal"
  extraArgs:
    max-requests-inflight: "400"
    max-mutating-requests-inflight: "200"
    audit-log-path: "/var/log/k8s/audit.log"
    audit-log-maxsize: "100"
    audit-log-maxage: "7"
    audit-policy-file: "/etc/kubernetes/audit-policy.yaml"
  extraVolumes:
  - name: audit
    hostPath: /var/log/k8s
    mountPath: /var/log/k8s
    readOnly: false
  - name: audit-policy
    hostPath: /etc/kubernetes/audit-policy.yaml
    mountPath: /etc/kubernetes/audit-policy.yaml
    readOnly: true
controllerManager:
  extraArgs:
    node-cidr-mask-size: "24"
    bind-address: "0.0.0.0"
    terminated-pod-gc-threshold: "5000"
scheduler:
  extraArgs:
    bind-address: "0.0.0.0"
    percentage-of-nodes-to-score: "30"
etcd:
  external:
    endpoints:
    - https://10.0.1.101:2379
    - https://10.0.1.102:2379
    - https://10.0.1.103:2379
    caFile: /etc/kubernetes/pki/etcd-ca.crt
    certFile: /etc/kubernetes/pki/etcd-client.crt
    keyFile: /etc/kubernetes/pki/etcd-client.key
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
systemReserved:
  cpu: 500m
  memory: 1Gi
kubeReserved:
  cpu: 500m
  memory: 1Gi
evictionHard:
  memory.available: "500Mi"
  nodefs.available: "5%"
  imagefs.available: "10%"

执行初始化(分阶段):

代码语言:javascript
复制
# 生成证书(有效期10年)
kubeadm init phase certs all --config=kubeadm-config.yaml
# 手工替换证书有效期(使用Go API修改,略)
# 正式初始化
kubeadm init --config=kubeadm-config.yaml --upload-certs
# 获取join命令(含certificate-key)
kubeadm init phase upload-certs --upload-certs

其他Master节点加入时需指定--control-plane,并使用--certificate-key


3. 网络方案:Calico eBPF + 严格网络策略

银行要求默认拒绝对外暴露,且东西流量必须加密(WireGuard)以及细粒度隔离

3.1 Calico安装(使用eBPF数据面)

custom-resources.yaml

代码语言:javascript
复制
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  calicoNetwork:
    ipPools:
    - blockSize: 26
      cidr: 172.20.0.0/16
      encapsulation: VXLANCrossSubnet   # 跨网段VXLAN,同网段直连
      natOutgoing: Enabled
    nodeAddressAutodetectionV4:
      interface: "eth.*|bond.*"
    linuxDataplane: BPF                 # 启用eBPF替代iptables
---
apiVersion: operator.tigera.io/v1
kind: APIServer
metadata:
  name: default
spec: {}

性能基准:eBPF模式替换kube-proxy,Service转发延迟降低28%(对比iptables),CPU占用减少15%。

3.2 零信任网络策略(NetworkPolicy)

我们为支付服务(pay-svc)只允许来自order-svcgateway-svc的访问,且限定端口8080:

代码语言:javascript
复制
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: pay-service-policy
  namespace: prod
spec:
  podSelector:
    matchLabels:
      app: pay-service
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: prod
      podSelector:
        matchExpressions:
        - {key: app, operator: In, values: [order-svc, gateway-svc]}
    ports:
    - protocol: TCP
      port: 8080
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
    ports:
    - protocol: UDP
      port: 53                 # 仅允许DNS
  - to:
    - podSelector:
        matchLabels:
          app: redis-cache
    ports:
    - protocol: TCP
      port: 6379

踩坑提醒:银行旧版应用依赖127.0.0.1本地通信,但在Pod中实际是独立网络命名空间。我们通过添加hostNetwork: true的特例容器解决了历史包袱,但严格审计了这类高危Pod。


4. 存储方案:Rook-Ceph + 本地SSD池

电子商城的商品图片(海量小文件)订单日志需要持久化,我们采用Rook-Ceph提供块存储(RWO)和共享文件系统(RWX)。

4.1 Ceph集群部署(使用裸盘)

cluster.yaml(精简):

yaml

代码语言:javascript
复制
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph
  namespace: rook-ceph
spec:
  cephVersion:
    image: quay.io/ceph/ceph:v17.2.6
  dataDirHostPath: /var/lib/rook
  mon:
    count: 5
    allowMultiplePerNode: false
  mgr:
    count: 2
  dashboard:
    enabled: true
  crashCollector:
    disable: false
  storage:
    useAllNodes: true
    useAllDevices: false
    config:
      databaseSizeMB: "10240"   # 10GB for rocksdb
      journalSizeMB: "10240"
    nodes:
    - name: "worker-01"
      devices:
      - name: "/dev/nvme1n1"
      - name: "/dev/nvme2n1"

创建StorageClass:

代码语言:javascript
复制
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: rook-ceph-block-fast
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
  clusterID: rook-ceph
  pool: replicapool
  imageFormat: "2"
  imageFeatures: layering
  csi.storage.k8s.io/fstype: ext4
  csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
  csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
reclaimPolicy: Retain
allowVolumeExpansion: true

生产调优:RBD mirror池开启fast-diffobject-map,使得快照创建从5秒降低到200ms,极大加速了CI环境的回滚速度。


5. 流量入口:高可用Ingress-Nginx + WAF前置

电商系统外部流量路径: Internet → 云WAF(硬件) → 4层SLB → Nginx Ingress → 后端Service

5.1 Nginx Ingress以DaemonSet+hostNetwork部署

values.yaml (Helm):

代码语言:javascript
复制
controller:
  kind: DaemonSet
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet
  nodeSelector:
    node-role.kubernetes.io/ingress: "true"
  service:
    type: ClusterIP        # 因为hostNetwork,无需NodePort
  publishService:
    enabled: false
  config:
    worker-processes: "auto"
    worker-connections: "20480"
    use-geoip2: "true"
    geoip2-database: /etc/nginx/geoip/GeoLite2-City.mmdb
    http-sni: "true"
    ssl-protocols: "TLSv1.2 TLSv1.3"
    ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"
    proxy-body-size: "100m"
    proxy-read-timeout: "120"
    keepalive: "3200"
    keepalive-requests: "10000"
    upstream-keepalive-timeout: "120"
    # 大促专项优化
    worker-shutdown-timeout: "300s"
    load-balance: "ewma"    # 使用最少连接加权算法
  ingressClassResource:
    name: nginx-bank
    enabled: true
    default: true

金丝雀发布支持:在Ingress注解中启用nginx.ingress.kubernetes.io/canary-weight: "10",将10%流量引至新版本,银行风控要求灰度观察至少30分钟。


6. 有状态中间件部署(MySQL + Redis 集群)

银行交易系统坚决不用云托管RDS(合规要求),我们在K8s内自建MySQL InnoDB ClusterRedis Enterprise

6.1 MySQL Operator(Oracle官方)

代码语言:javascript
复制
apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: order-db-cluster
spec:
  secretName: order-db-secret
  tlsUseSelfSigned: true
  instances: 3
  router:
    instances: 2
  datadirVolumeClaimTemplate:
    storageClassName: rook-ceph-block-fast
    accessModes: ["ReadWriteOnce"]
    resources:
      requests:
        storage: 2Ti
  podSpec:
    resources:
      limits:
        cpu: "8"
        memory: 32Gi
    affinity:
      podAntiAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchLabels:
              app.kubernetes.io/component: mysql
          topologyKey: kubernetes.io/hostname

配合Percona XtraBackup做每日全量备份到对象存储(MinIO)。

6.2 Redis集群(StatefulSet + 持久化AOF)

我们使用bitnami/redis-cluster Helm Chart,关闭appendfsync everysec改为no(由硬件BBU电池保障),换取更高吞吐。

代码语言:javascript
复制
persistence:
  enabled: true
  storageClass: "rook-ceph-block-fast"
  size: 100Gi
redis:
  configmap: |-
    maxmemory 24gb
    maxmemory-policy allkeys-lru
    save 900 1
    save 300 10
    stop-writes-on-bgsave-error no
cluster:
  nodes: 12                # 6 master + 6 replica
  replicas: 1
  updateStrategy: rollingUpdate

7. HPA + KEDA:基于RabbitMQ队列长度的弹性伸缩

大促抢购场景下,订单积压于MQ。我们引入KEDA(Kubernetes Event-driven Autoscaling)替代原生HPA,基于rabbitmq.queue_length进行精准扩容。

7.1 部署KEDA

代码语言:javascript
复制
helm repo add kedacore https://kedacore.github.io/charts
helm install keda kedacore/keda --namespace keda --version 2.12.0

7.2 ScaledObject配置

代码语言:javascript
复制
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-consumer-scaler
  namespace: prod
spec:
  scaleTargetRef:
    name: order-consumer-deploy
    kind: Deployment
    apiVersion: apps/v1
  minReplicaCount: 10
  maxReplicaCount: 80
  pollingInterval: 5        # 每5秒检查一次
  cooldownPeriod: 120       # 缩容冷静期2分钟
  triggers:
  - type: rabbitmq
    metadata:
      queueName: order.pending
      host: amqp://rabbitmq.prod.svc.cluster.local:5672
      queueLength: "100"
      protocol: http
    authenticationRef:
      name: rabbitmq-trigger-auth
---
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: rabbitmq-trigger-auth
spec:
  secretTargetRef:
  - parameter: host
    name: rabbitmq-secret
    key: host
  - parameter: username
    name: rabbitmq-secret
    key: username
  - parameter: password
    name: rabbitmq-secret
    key: password

实际压测:队列堆积至5000条时,Pod在45秒内从10个扩容至68个,完美覆盖峰值。


8. 可观测性三件套:Metrics + Logging + Tracing

8.1 Prometheus高可用(Thanos侧边车)

代码语言:javascript
复制
prometheus:
  prometheusSpec:
    replicas: 2
    retention: 30d
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: rook-ceph-block-fast
          resources:
            requests:
              storage: 5Ti
    externalLabels:
      cluster: "bank-prod-az-a"
    thanos:
      baseImage: quay.io/thanos/thanos
      version: v0.33.0
      objectStorageConfig:
        name: thanos-objstore
        key: config.yaml

8.2 Loki分布式日志(微服务聚合)

代码语言:javascript
复制
loki:
  structuredConfig:
    ingester:
      chunk_encoding: snappy
      chunk_target_size: 1572864
    querier:
      max_concurrent: 20
    storage_config:
      boltdb_shipper:
        active_index_directory: /var/loki/index
        cache_location: /var/loki/cache
      aws:
        s3: s3://loki-bucket/
        s3forcepathstyle: true
  storage:
    type: s3

成本优化:日志保留7天热存,>7天转至Glacier归档。

8.3 告警规则(金融级敏感度)

我们在Prometheus中配置了交易成功率暴跌告警(非单纯的CPU告警):

代码语言:javascript
复制
groups:
- name: business_sla
  rules:
  - alert: HighOrderFailureRate
    expr: |
      (
        sum(rate(order_service_errors_total[1m])) by (service)
        /
        sum(rate(order_service_requests_total[1m])) by (service)
      ) > 0.01
    for: 2m
    labels:
      severity: critical
      team: payment
    annotations:
      summary: "订单失败率超过1%,当前{{ $value }}"

9. 大促实战踩坑TOP 5

序号

现象

根因

解决方案

1

API Server在压测时Too many requests,Pods无法调度

max-requests-inflight默认400过小

调整为2000,同时增大kube-apiserver内存至16Gi

2

ETCD存储空间频繁报警(150%增长/天)

watch历史数据未压缩,默认压缩间隔1小时

设置--experimental-compaction-retention=1m,并每6小时执行etcdctl defrag

3

Ceph RBD PVC挂载慢(30s+)

rbd default features包含deep-flatten等导致内核态锁竞争

在StorageClass中指定imageFeatures: layering,禁用其他高级特性

4

Nginx Ingress OOM(内存暴涨)

开启geoip2且数据库加载到内存,每个Worker重复加载

设置geoip2-shared-memory: true并增加proxy-buffer-size

5

节点DiskPressure导致Pod被驱逐

容器日志/var/log/containers未轮转,单个文件超10GB

修改containerd配置max_size = 100M,配合logrotate每日切割


10. 容灾演练:每周一次的Chaos Mesh故障注入

银行合规要求每年至少2次机房级切换演练。我们使用Chaos Mesh进行常态化故障测试:

代码语言:javascript
复制
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
  name: az-a-partition
spec:
  action: partition
  mode: all
  selector:
    namespaces:
    - prod
    labelSelectors:
      topology.kubernetes.io/zone: "az-a"
  direction: to
  target:
    mode: all
    selector:
      namespaces:
      - prod
      labelSelectors:
        topology.kubernetes.io/zone: "az-b"
  duration: "5m"

通过每周注入网络分区、Pod杀、节点关机等故障,验证Topology Spread ConstraintsPodDisruptionBudget是否生效。

代码语言:javascript
复制
# PDB保证至少3个订单Pod存活
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: order-pdb
spec:
  minAvailable: 3
  selector:
    matchLabels:
      app: order-service

11. 最终交付指标

指标项

目标值

实测P99

集群可用性

99.99%

99.993%(年度故障23分钟)

API Server响应延迟

< 200ms

62ms

Pod启动时间(含拉镜像)

< 30s

21s (含Harbor加速)

跨AZ网络抖动

< 3ms

1.8ms

大促最大Pod数

-

单集群峰值 2,340 Pods

自动扩容响应

< 60s

43s(KEDA触发)


12. 致后来者:三条黄金法则

  1. 证书管理自动化:K8s默认1年证书太短,银行采用cert-manager + Venafi对接内部CA,实现证书自动轮换,避免手动续期灾难。
  2. 永不信任CNI默认配置:务必手工调试calico-nodeFELIX_IPTABLESREFRESHINTERVALFELIX_MAXIPSETSNUMBER,默认值在万级Service下会频繁导致iptables-restore锁死。
  3. 备份ETCD即备份全部:我们开发了定时脚本,每4小时将ETCD快照加密后上传至灾备站点,并定期在沙箱环境执行恢复演练——仅凭这一步,规避了2次人为误删Namespace的事故。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • K8s高可用集群部署实战:某国有银行电子商城容器化迁移全记录
    • 引言
    • 1. 集群架构与硬件选型
      • 1.1 拓扑设计(3AZ架构)
      • 1.2 基础设施前置调优(sysctl)
    • 2. 控制面高可用:kubeadm + Keepalived + HAProxy
      • 2.1 负载均衡器架构
      • 2.2 kubeadm初始化配置文件(带证书自定义)
    • 3. 网络方案:Calico eBPF + 严格网络策略
      • 3.1 Calico安装(使用eBPF数据面)
      • 3.2 零信任网络策略(NetworkPolicy)
    • 4. 存储方案:Rook-Ceph + 本地SSD池
      • 4.1 Ceph集群部署(使用裸盘)
    • 5. 流量入口:高可用Ingress-Nginx + WAF前置
      • 5.1 Nginx Ingress以DaemonSet+hostNetwork部署
    • 6. 有状态中间件部署(MySQL + Redis 集群)
      • 6.1 MySQL Operator(Oracle官方)
      • 6.2 Redis集群(StatefulSet + 持久化AOF)
    • 7. HPA + KEDA:基于RabbitMQ队列长度的弹性伸缩
      • 7.1 部署KEDA
      • 7.2 ScaledObject配置
    • 8. 可观测性三件套:Metrics + Logging + Tracing
      • 8.1 Prometheus高可用(Thanos侧边车)
      • 8.2 Loki分布式日志(微服务聚合)
      • 8.3 告警规则(金融级敏感度)
    • 9. 大促实战踩坑TOP 5
    • 10. 容灾演练:每周一次的Chaos Mesh故障注入
    • 11. 最终交付指标
    • 12. 致后来者:三条黄金法则
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档