K8s人大金仓主从高可用搭建|容器化集群自动同步+故障切换(生产完整版)

摘要

政务信创容器化改造,人大金仓主从高可用是绕不开的硬骨头。90% 的团队初次落地都会踩坑:IP 漂移后主备断连、手动搭建同步效率低、故障切换要改一堆配置、Pod 重建后集群状态错乱。很多人最后得出「容器不适合跑数据库主从」的结论,本质是没选对工作负载、没用到 K8s 原生的服务发现能力。

本文基于政务项目生产落地经验,从零实现人大金仓 V9 容器化主从高可用:基于 StatefulSet+Headless Service 实现节点自动发现,initContainer 自动完成主备初始化与流复制搭建,配合标签自动漂移实现故障切换流量无感切换。全套 YAML 配置直接复制就能用,覆盖自动同步、手动切换、集群重建全场景。

政务选金仓,金融选达梦;MySQL 迁移选金仓,Oracle 迁移选达梦。所有配置均在 K8s 1.26 + 金仓 V9 企业版实测跑通,生产可用。


目录

一、架构选型:容器化主从为什么必须走 StatefulSet + 原生流复制 二、前置环境与核心原理 三、基础资源全量配置(命名空间 / Secret/ConfigMap/Service) 四、StatefulSet 核心部署:主从集群自动搭建 五、同步状态全维度验证:确认集群真的跑稳了 六、故障切换全流程:手动 + 半自动两种生产方案 七、生产级避坑 10 条(踩过的坑直接帮你避) 八、日常运维常用命令速查 总结


一、架构选型:容器化主从为什么必须走 StatefulSet + 原生流复制

📌 核心结论:容器化金仓主从,StatefulSet + 原生物理流复制是当前最稳定、排障成本最低的方案,没有之一。

很多团队图省事用 Deployment 跑两个单实例手动配主从,最终都会遇到身份漂移、存储错乱、重启后集群失效的问题。数据库主从是典型的有状态集群,必须用 StatefulSet 保障稳定身份。

1.1 为什么 Deployment 绝对不能做主从集群

能力维度 Deployment StatefulSet 主从集群为什么必须选后者
Pod 身份 随机哈希后缀,重启即变 固定序号(kingbase-0、kingbase-1),重建身份不变 主备角色、复制槽、配置都绑定节点身份,不能乱
网络标识 无固定地址,靠 Service 负载均衡 配合 Headless Service 生成永久 DNS 域名 主备同步需要稳定的对端地址,IP 漂移不能断连
存储绑定 多副本共享卷,无独立绑定 每个 Pod 对应独立 PVC,重建自动挂载原卷 主备数据完全独立,不能混用,故障恢复不能错乱
部署顺序 并发创建销毁 有序部署(0→1)、逆序销毁 必须先启主库再启备库,否则备库连不上直接崩溃

1.2 整体架构总览

复制代码
业务应用
    ↓
kingbase-svc(ClusterIP,selector绑定role=master)
    ↓
┌─────────────────┐     WAL流复制同步     ┌─────────────────┐
│   kingbase-0    │ ───────────────────→ │   kingbase-1    │
│    主库Master   │                      │    备库Slave    │
│  独立PVC数据卷  │                      │  独立PVC数据卷  │
└─────────────────┘                      └─────────────────┘
          ↘                                ↙
   kingbase-headless(Headless Service,集群内部发现)

1.3 三种高可用方案选型对比

方案 实现方式 稳定性 运维成本 切换方式 推荐指数
原生流复制 + 手动切换 StatefulSet + 原生流复制 + 手动 promote 极高 人工操作切换 ⭐⭐⭐⭐⭐
原生流复制 + 半自动切换 原生流复制 + Sidecar 标签同步 流量自动漂移 ⭐⭐⭐⭐
repmgr 全自动化 repmgr 组件 + 自动故障裁决 自动切换 ⭐⭐⭐
Operator 全生命周期 自定义 Operator 管控 极高 自动管控 ⭐⭐

💡 落地建议:政务生产场景优先选「原生流复制 + 手动切换」,所有机制都是金仓原生能力,排障简单、稳定性最高;等团队运维能力跟上后,再叠加 Sidecar 实现流量自动切换。Operator 方案当前成熟度有限,非大规模集群不建议优先投入。


二、前置环境与核心原理

2.1 基础环境要求

  • K8s 版本:1.24+(本文基于 1.26 实测)
  • 容器运行时:containerd 1.6+
  • 存储:生产级 StorageClass,支持 ReadWriteOnce
  • 镜像:人大金仓 V9 企业版镜像,内置 sys_basebackup、sys_ctl 等全套工具
  • 网络:节点间延迟 < 3ms,保证流复制同步效率

2.2 核心原理:固定 DNS 实现自动发现

容器 IP 漂移是常态,但 StatefulSet + Headless Service 会生成永久固定的 DNS 域名,格式为:

复制代码
pod名称.service名称.namespace.svc.cluster.local

对应本方案就是:

  • 主库:kingbase-0.kingbase-headless.kingbase.svc.cluster.local
  • 备库:kingbase-1.kingbase-headless.kingbase.svc.cluster.local

无论 Pod 如何重建、IP 如何变化,域名永远不变。备库启动时直接通过域名连接主库,彻底解决 IP 漂移断连问题。

2.3 自动搭建核心逻辑

通过 initContainer 实现零人工干预自动搭建:

  1. Pod 启动时,initContainer 先执行,读取主机名提取序号
  2. 0 号节点(主库):若数据目录为空,则初始化实例、配置流复制参数、创建复制用户与复制槽
  3. 1 号节点(备库):循环等待主库就绪,通过 sys_basebackup 拉取全量数据,自动生成备库配置
  4. 数据目录非空时,直接跳过初始化,用原有数据启动,不覆盖现有数据
  5. 主容器启动数据库实例,自动建立流复制连接

三、基础资源全量配置(可直接复制)

3.1 命名空间与敏感信息 Secret

复制代码
# kingbase-base.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: kingbase
  labels:
    app: kingbase
---
# 数据库账号密码、复制用户密码统一用Secret管理
apiVersion: v1
kind: Secret
metadata:
  name: kingbase-secret
  namespace: kingbase
type: Opaque
data:
  system_password: U3lzdGVtQDEyMzQ1Ng==  # system用户密码,base64编码
  repl_password: UmVwbGljYXRpb25AMTIzNDU2  # 复制用户密码
---
# 授权文件Secret
apiVersion: v1
kind: Secret
metadata:
  name: kingbase-license
  namespace: kingbase
type: Opaque
data:
  license.dat: 你的授权文件base64内容

3.2 ConfigMap:数据库配置模板

存放主库通用配置模板,initContainer 中根据角色生成最终配置。

复制代码
# kingbase-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: kingbase-config
  namespace: kingbase
data:
  kingbase.conf.template: |
    # ========== 基础配置 ==========
    listen_addresses = '*'
    port = 54321
    max_connections = 500
    shared_buffers = 4GB
    effective_cache_size = 12GB
    work_mem = 16MB
    maintenance_work_mem = 512MB
    
    # ========== WAL与流复制 ==========
    wal_level = replica
    max_wal_senders = 10
    max_replication_slots = 10
    wal_keep_segments = 200
    hot_standby = on
    hot_standby_feedback = on
    wal_receiver_status_interval = 10s
    
    # ========== 归档配置 ==========
    archive_mode = on
    archive_command = 'cp %p /opt/kingbase/arch/%f'
    
    # ========== 日志配置 ==========
    logging_collector = on
    log_directory = '/opt/kingbase/log'
    log_filename = 'kingbase-%Y-%m-%d.log'
    log_rotation_age = 1d
    log_rotation_size = 512MB
  sys_hba.conf.template: |
    # 本地连接
    local   all             all                                     trust
    host    all             all             127.0.0.1/32            scram-sha-256
    host    all             all             ::1/128                 scram-sha-256
    
    # 业务网段访问
    host    all             all             10.0.0.0/8              scram-sha-256
    
    # 流复制权限(关键:允许集群内节点复制连接)
    host    replication     repl            10.0.0.0/8              scram-sha-256

3.3 Service 配置:双 Service 分工

复制代码
# kingbase-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: kingbase-headless
  namespace: kingbase
  labels:
    app: kingbase
spec:
  type: ClusterIP
  clusterIP: None  # Headless Service核心:无ClusterIP,直接返回Pod IP
  selector:
    app: kingbase
  ports:
    - port: 54321
      name: db
---
# 业务统一访问入口,自动绑定主库
apiVersion: v1
kind: Service
metadata:
  name: kingbase-svc
  namespace: kingbase
  labels:
    app: kingbase
spec:
  type: ClusterIP
  selector:
    app: kingbase
    role: master  # 只匹配role=master的Pod,切换后自动漂移流量
  ports:
    - port: 54321
      targetPort: 54321
      name: db

四、StatefulSet 核心部署:主从集群自动搭建

这是整套方案的核心,通过 initContainer 实现全自动主备搭建,无需人工介入。

4.1 完整 StatefulSet 配置

复制代码
# kingbase-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: kingbase
  namespace: kingbase
spec:
  serviceName: "kingbase-headless"
  replicas: 2
  selector:
    matchLabels:
      app: kingbase
  template:
    metadata:
      labels:
        app: kingbase
    spec:
      terminationGracePeriodSeconds: 120
      securityContext:
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
        runAsNonRoot: true
      # 主备强制分散节点,避免单点故障
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values: ["kingbase"]
            topologyKey: kubernetes.io/hostname
      initContainers:
        - name: init-cluster
          image: your-registry/kingbase-v9:latest
          imagePullPolicy: IfNotPresent
          command: ["/bin/bash", "-c"]
          args:
            - |
              set -e
              POD_ORDINAL=$(hostname | awk -F'-' '{print $NF}')
              DATA_DIR="/opt/kingbase/data"
              MASTER_HOST="kingbase-0.kingbase-headless.kingbase.svc.cluster.local"
              REPL_USER="repl"
              REPL_PASSWORD="${REPL_PASSWORD}"
              SYSTEM_PASSWORD="${SYSTEM_PASSWORD}"
              SLOT_NAME="slot_kingbase_${POD_ORDINAL}"

              echo "当前节点序号:${POD_ORDINAL}"

              # 数据目录已存在则跳过初始化,避免覆盖数据
              if [ -f "${DATA_DIR}/SYSTEM.DBF" ]; then
                echo "数据目录已存在,跳过初始化"
                exit 0
              fi

              # ========== 0号节点:主库初始化 ==========
              if [ "${POD_ORDINAL}" = "0" ]; then
                echo "初始化主库实例..."
                # 初始化数据库实例
                initdb -D ${DATA_DIR} -U system -W "${SYSTEM_PASSWORD}"
                
                # 拷贝配置模板
                cp /opt/kingbase/template/kingbase.conf.template ${DATA_DIR}/kingbase.conf
                cp /opt/kingbase/template/sys_hba.conf.template ${DATA_DIR}/sys_hba.conf
                
                # 临时启动实例,创建复制用户与复制槽
                sys_ctl start -D ${DATA_DIR} -w
                echo "创建复制用户..."
                ksql -U system -c "CREATE USER ${REPL_USER} WITH REPLICATION ENCRYPTED PASSWORD '${REPL_PASSWORD}';"
                echo "创建复制槽..."
                ksql -U system -c "SELECT sys_create_physical_replication_slot('slot_kingbase_1');"
                # 停止临时实例
                sys_ctl stop -D ${DATA_DIR}
                echo "主库初始化完成"

              # ========== 1号节点:备库初始化 ==========
              else
                echo "等待主库就绪..."
                until ksql -h ${MASTER_HOST} -U system -c "select 1;" > /dev/null 2>&1; do
                  sleep 5
                  echo "等待主库启动中..."
                done
                echo "主库就绪,开始拉取全量数据..."
                
                # 从主库拉取基础备份,自动生成备库配置
                sys_basebackup -h ${MASTER_HOST} -p 54321 -U ${REPL_USER} \
                  -D ${DATA_DIR} -Fp -Xs -P -R -S slot_kingbase_1
                
                # 拷贝hba配置
                cp /opt/kingbase/template/sys_hba.conf.template ${DATA_DIR}/sys_hba.conf
                echo "备库初始化完成"
              fi
          env:
            - name: SYSTEM_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: kingbase-secret
                  key: system_password
            - name: REPL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: kingbase-secret
                  key: repl_password
          volumeMounts:
            - name: kingbase-data
              mountPath: /opt/kingbase/data
            - name: kingbase-config
              mountPath: /opt/kingbase/template
      containers:
        - name: kingbase
          image: your-registry/kingbase-v9:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 54321
              name: db
          env:
            - name: PGDATA
              value: "/opt/kingbase/data"
          command: ["sys_ctl"]
          args: ["start", "-D", "/opt/kingbase/data", "-l", "/opt/kingbase/log/run.log"]
          volumeMounts:
            - name: kingbase-data
              mountPath: /opt/kingbase/data
            - name: kingbase-log
              mountPath: /opt/kingbase/log
            - name: kingbase-arch
              mountPath: /opt/kingbase/arch
            - name: license
              mountPath: /opt/kingbase/license.dat
              subPath: license.dat
          resources:
            requests:
              cpu: "4"
              memory: "16Gi"
            limits:
              cpu: "4"
              memory: "16Gi"
          livenessProbe:
            exec:
              command: ["kb_isready", "-U", "system", "-p", "54321"]
            initialDelaySeconds: 90
            periodSeconds: 15
            timeoutSeconds: 5
            failureThreshold: 3
          readinessProbe:
            exec:
              command: ["kb_isready", "-U", "system", "-p", "54321"]
            initialDelaySeconds: 30
            periodSeconds: 10
      volumes:
        - name: kingbase-config
          configMap:
            name: kingbase-config
        - name: license
          secret:
            secretName: kingbase-license
  volumeClaimTemplates:
    - metadata:
        name: kingbase-data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: "local-ssd"
        resources:
          requests:
            storage: 100Gi
    - metadata:
        name: kingbase-log
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: "local-sas"
        resources:
          requests:
            storage: 30Gi
    - metadata:
        name: kingbase-arch
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: "local-sas"
        resources:
          requests:
            storage: 100Gi

4.2 部署执行步骤

复制代码
# 1. 依次部署基础资源
kubectl apply -f kingbase-base.yaml
kubectl apply -f kingbase-config.yaml
kubectl apply -f kingbase-service.yaml

# 2. 部署StatefulSet,会按顺序先启动kingbase-0,再启动kingbase-1
kubectl apply -f kingbase-statefulset.yaml

# 3. 观察部署进度
kubectl get pods -n kingbase -w

✅ 正常状态:kingbase-0 先变为 Running,随后 kingbase-1 启动完成,两个 Pod 均为 Running 状态。


五、同步状态全维度验证:确认集群真的跑稳了

部署完成后,必须做全维度验证,确保同步真的生效了。

5.1 主库端验证

复制代码
# 进入主库Pod
kubectl exec -it kingbase-0 -n kingbase -- ksql -U system

-- 1. 查看流复制连接状态,state应为streaming
SELECT pid, usename, client_addr, state, sync_state 
FROM sys_stat_replication;

-- 2. 查看复制槽状态,active应为true
SELECT slot_name, slot_type, active, restart_lsn 
FROM sys_replication_slots;

-- 3. 查看当前WAL位置
SELECT sys_current_wal_lsn();

5.2 备库端验证

复制代码
# 进入备库Pod
kubectl exec -it kingbase-1 -n kingbase -- ksql -U system

-- 1. 确认备库处于恢复状态,返回t即为正常
SELECT sys_is_in_recovery();

-- 2. 查看备库接收重放延迟
SELECT 
  pg_size_pretty(pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn())) AS replay_delay;

5.3 数据同步验证

复制代码
-- 主库创建测试表并插入数据
CREATE TABLE test_sync(id INT, name VARCHAR(50));
INSERT INTO test_sync VALUES (1, '测试同步数据');

-- 备库查询,能查到数据即为同步正常
SELECT * FROM test_sync;

💡 注意:默认物理备库是只读的,只能查询,不能写入,这是正常现象。


六、故障切换全流程:手动 + 半自动两种生产方案

6.1 方案一:手动切换(政务生产首选,稳定可控)

适用于计划性切换、主库故障后的人工切换,全程可控、风险最低。

切换步骤
  1. 确认备库数据同步状态,确保所有日志都已重放完成

  2. 停止旧主库 (如果还能操作),避免脑裂

    复制代码
    kubectl exec -it kingbase-0 -n kingbase -- sys_ctl stop -D /opt/kingbase/data
  3. 提升备库为新主库

    复制代码
    kubectl exec -it kingbase-1 -n kingbase -- sys_ctl promote -D /opt/kingbase/data
  4. 更新 Pod 角色标签 ,让业务流量切到新主库

    复制代码
    # 移除旧主库master标签
    kubectl label pod kingbase-0 role- -n kingbase
    # 给新主库打上master标签
    kubectl label pod kingbase-1 role=master -n kingbase
  5. 业务验证读写正常,切换完成

6.2 方案二:半自动切换(Sidecar 自动同步标签)

在每个数据库 Pod 中加一个轻量 Sidecar,定期检测本地角色,自动更新 Pod 的role标签,实现流量自动漂移。

核心逻辑:

  • 每秒检测本地数据库角色,sys_is_in_recovery()返回 false 则为主库
  • 角色变化时自动更新 Pod 标签
  • Service 通过 selector 自动绑定新主库,业务无需改动

优势:切换后流量自动切走,无需人工改配置,业务感知只有秒级闪断。

6.3 旧主库重新加入集群

主库故障修复后,需要重新作为备库加入集群:

  1. 清空旧主库的数据目录(或保留备份)
  2. 在旧主库节点重新执行 sys_basebackup,从新主库拉取全量数据
  3. 启动旧主库作为新备库,自动建立流复制连接
  4. 验证同步状态正常,集群恢复一主一备

七、生产级避坑 10 条(踩过的坑直接帮你避)

⚠️ 坑 1:用 Deployment 部署主从集群

  • 后果:Pod 重启后身份错乱、存储挂载混乱、主备断连
  • 整改:必须用 StatefulSet,这是有状态集群的基础

⚠️ 坑 2:IP 写死在配置里,重启就断连

  • 后果:Pod 重建 IP 变化,备库连不上主库,同步中断
  • 整改:全部使用 Headless Service 的固定 DNS 域名,彻底屏蔽 IP 漂移

⚠️ 坑 3:数据目录不判断非空,重启覆盖数据

  • 后果:Pod 重启后重新初始化,生产数据直接丢失
  • 整改:initContainer 必须先判断数据目录是否存在,已有数据则跳过初始化

⚠️ 坑 4:sys_hba.conf 没开复制权限

  • 后果:备库连接主库报错认证失败,同步建不起来
  • 整改:hba 配置中必须单独加 replication 类型的权限条目

⚠️ 坑 5:主备调度到同一节点,高可用失效

  • 后果:节点宕机,主备一起挂,集群形同虚设
  • 整改:强制配置节点级反亲和性,主备必须分散在不同节点

⚠️ 坑 6:wal_keep_segments 设太小,备库延迟就断流

  • 后果:备库稍微延迟,主库就把 WAL 删了,同步中断,必须重做备库
  • 整改:生产环境至少设 200 段以上,配合复制槽使用更稳妥

⚠️ 坑 7:没有复制槽,WAL 随意删除

  • 后果:备库维护期间,主库 WAL 被覆盖,恢复后同步直接失败
  • 整改:生产必须用复制槽,主库会保留备库未接收的 WAL

⚠️ 坑 8:时间不同步,流复制异常

  • 后果:主备时间差太大,日志同步错乱、延迟异常
  • 整改:所有节点统一配置 NTP 时间同步,容器继承节点时间

⚠️ 坑 9:备库开启写入,数据不一致

  • 后果:备库写入数据,主备数据不一致,集群彻底错乱
  • 整改:物理备库默认只读,禁止在备库执行写操作

⚠️ 坑 10:切换后不更新标签,流量还打旧主库

  • 后果:主备切换了,但 Service 没切,业务流量还打宕机的旧主库
  • 整改:切换流程必须包含标签更新步骤,或用 Sidecar 自动同步

八、日常运维常用命令速查

集群状态检查

复制代码
# 查看Pod状态与节点分布
kubectl get pods -n kingbase -o wide

# 查看PVC状态
kubectl get pvc -n kingbase

# 查看主库复制状态
kubectl exec -it kingbase-0 -n kingbase -- ksql -U system -c "SELECT * FROM sys_stat_replication;"

# 查看备库恢复状态
kubectl exec -it kingbase-1 -n kingbase -- ksql -U system -c "SELECT sys_is_in_recovery();"

常用运维操作

复制代码
# 主库手动切换WAL
kubectl exec -it kingbase-0 -n kingbase -- ksql -U system -c "SELECT sys_switch_wal();"

# 备库查看延迟
kubectl exec -it kingbase-1 -n kingbase -- ksql -U system -c "SELECT pg_size_pretty(pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn())) as delay;"

# 查看数据库日志
kubectl logs -f kingbase-0 -n kingbase

配置更新

复制代码
# 修改ConfigMap后滚动重启集群
kubectl rollout restart statefulset kingbase -n kingbase

# 查看滚动更新进度
kubectl rollout status statefulset kingbase -n kingbase

总结

人大金仓容器化主从高可用,核心不是堆复杂组件,而是把基础做扎实:用 StatefulSet 保障稳定身份、用 Headless Service 解决 IP 漂移、用原生流复制实现数据同步,再配合标签机制实现流量切换。这套方案没有黑盒,所有能力都是 K8s 和金仓原生提供的,排障简单、稳定性高,完全满足政务生产要求。

信创云原生不是为了炫技,而是为了更稳定、更易运维的交付。把底层架构选对了,后续的运维、扩容、故障处理都会事半功倍。

政务选金仓,金融选达梦;MySQL 迁移选金仓,Oracle 迁移选达梦。

📚 专栏推荐:专注 SpringBoot3 + 人大金仓 + 达梦信创实战,持续输出生产级部署、性能调优、安全合规、避坑指南干货,关注不迷路。

相关推荐
潘正翔3 小时前
k8s基础_kubeadm搭建k8s集群
linux·运维·docker·云原生·容器·kubernetes
Echo flower3 小时前
Docker 容器中 Puppeteer 僵尸进程排查与修复
运维·docker·容器·puppeteer
潘正翔3 小时前
k8s进阶_Harbor镜像仓库
git·云原生·容器·kubernetes·gitee·github
江湖有缘4 小时前
Docker实战 | 使用Docker部署Donetick任务与家务管理应用
运维·docker·容器
键盘鼓手苏苏5 小时前
AI 音频生成流水线:异步任务要有进度和取消
云原生·kubernetes·k8
雨辰AI5 小时前
K8s 达梦数据守护集群部署|容器化高可用生产落地指南
云原生·容器·kubernetes
张忠琳6 小时前
【NPU】Ascend Docker Runtime v26.0.1 之一 runtime/main.go — 超深度分析
云原生·容器·架构·kubernetes·npu·docker-runtime
画中有画6 小时前
云原生可观测性体系设计及其应用
云原生
刹那芳华19927 小时前
基于 Docker 的 LLaMA-Factory 全流程部署指南
docker·容器·llama