摘要
政务信创容器化改造,人大金仓主从高可用是绕不开的硬骨头。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 实现零人工干预自动搭建:
- Pod 启动时,initContainer 先执行,读取主机名提取序号
- 0 号节点(主库):若数据目录为空,则初始化实例、配置流复制参数、创建复制用户与复制槽
- 1 号节点(备库):循环等待主库就绪,通过 sys_basebackup 拉取全量数据,自动生成备库配置
- 数据目录非空时,直接跳过初始化,用原有数据启动,不覆盖现有数据
- 主容器启动数据库实例,自动建立流复制连接
三、基础资源全量配置(可直接复制)
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 方案一:手动切换(政务生产首选,稳定可控)
适用于计划性切换、主库故障后的人工切换,全程可控、风险最低。
切换步骤
-
确认备库数据同步状态,确保所有日志都已重放完成
-
停止旧主库 (如果还能操作),避免脑裂
kubectl exec -it kingbase-0 -n kingbase -- sys_ctl stop -D /opt/kingbase/data -
提升备库为新主库
kubectl exec -it kingbase-1 -n kingbase -- sys_ctl promote -D /opt/kingbase/data -
更新 Pod 角色标签 ,让业务流量切到新主库
# 移除旧主库master标签 kubectl label pod kingbase-0 role- -n kingbase # 给新主库打上master标签 kubectl label pod kingbase-1 role=master -n kingbase -
业务验证读写正常,切换完成
6.2 方案二:半自动切换(Sidecar 自动同步标签)
在每个数据库 Pod 中加一个轻量 Sidecar,定期检测本地角色,自动更新 Pod 的role标签,实现流量自动漂移。
核心逻辑:
- 每秒检测本地数据库角色,
sys_is_in_recovery()返回 false 则为主库 - 角色变化时自动更新 Pod 标签
- Service 通过 selector 自动绑定新主库,业务无需改动
优势:切换后流量自动切走,无需人工改配置,业务感知只有秒级闪断。
6.3 旧主库重新加入集群
主库故障修复后,需要重新作为备库加入集群:
- 清空旧主库的数据目录(或保留备份)
- 在旧主库节点重新执行 sys_basebackup,从新主库拉取全量数据
- 启动旧主库作为新备库,自动建立流复制连接
- 验证同步状态正常,集群恢复一主一备
七、生产级避坑 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 + 人大金仓 + 达梦信创实战,持续输出生产级部署、性能调优、安全合规、避坑指南干货,关注不迷路。