生产级落地:达梦 DM9 K8s StatefulSet 高可用部署|集群自动发现 + 故障自愈全流程
摘要
金融信创场景下达梦是数据库首选,但传统物理机部署扩容慢、运维重,容器化又常踩单节点单点故障、集群组网依赖固定 IP、故障切换人工介入慢、性能跟不上生产要求等坑。本文基于一线金融项目落地经验,从零拆解达梦 DM9 在 K8s 环境的生产级高可用方案,覆盖 StatefulSet 持久化部署、基于 Headless Service 的集群自动发现、守护进程 + 监视器双层故障自愈、生产级性能调优、自动化备份体系,附全套可直接复制的 Dockerfile、YAML 配置与 15 个生产级避坑点、完整故障排查手册。
政务选金仓,金融选达梦;MySQL 迁移选金仓,Oracle 迁移选达梦。本文所有配置均在 K8s 1.26 集群 + 达梦 DM9 企业版实测验证,复制即用。
目录
一、架构选型:达梦容器化为什么必须走 StatefulSet + 主备架构 二、前置环境与生产级镜像制作 三、基础资源全量配置(命名空间 / ConfigMap/Secret) 四、单节点生产级部署:StatefulSet + 持久化存储 五、主备集群自动发现:零手动配置组网全实现 六、自动监视器部署与故障自愈全流程 七、生产级性能调优(K8s 层 + 数据库层) 八、自动化备份方案:CronJob + dmrman 全量 + 增量备份 九、生产级避坑 15 条(踩过的坑直接帮你避) 十、常见故障排查手册 十一、常用运维命令与状态验证 总结
一、架构选型:达梦容器化为什么必须走 StatefulSet + 主备架构
📌 核心结论:达梦属于强有状态数据库,生产级容器化必须采用「StatefulSet + Headless Service + 本地块存储 + 原生主备集群」架构,绝对禁止用 Deployment 单节点裸跑。
很多团队初次容器化达梦时图省事用 Deployment,最终都会遇到数据丢失、IP 漂移后主备断连、节点重启后集群脑裂等问题,本质是选错了工作负载类型。
1.1 Deployment vs StatefulSet 数据库场景对比
| 能力维度 | Deployment | StatefulSet | 达梦为什么必须选后者 |
|---|---|---|---|
| Pod 身份 | 随机后缀,重启即变 | 固定序号(dmdb-0、dmdb-1),重建身份不变 | 主备角色绑定、配置文件固定依赖节点身份 |
| 网络标识 | 无固定内网地址 | 配合 Headless Service 生成永久 DNS 域名 | 主备同步、集群组网需要稳定的节点地址 |
| 存储绑定 | 多副本共享卷,无独立绑定 | 每个 Pod 对应独立 PVC,重建自动挂载原卷 | 主备数据独立,故障恢复不能错乱数据卷 |
| 部署顺序 | 并发创建销毁 | 有序部署(0→1)、逆序销毁 | 必须先启主库再启备库,避免集群状态异常 |
1.2 达梦高可用方案选型建议
| 方案模式 | 实现方式 | 适用场景 | 推荐指数 |
|---|---|---|---|
| 单节点持久化 | 单 Pod + PVC | 开发测试、非核心边缘系统 | ⭐⭐⭐ |
| 一主一备 Data Guard | 双 Pod + 原生守护进程 + 手动切换 | 中小规模生产、非核心交易系统 | ⭐⭐⭐⭐ |
| 一主一备 + 自动监视器 | 双 Pod + dmwatcher + dmmonitor 自动切换 | 金融核心业务、高可用要求高的场景 | ⭐⭐⭐⭐⭐ |
| DSC 共享存储集群 | 多实例 + 共享存储 | 超高并发、Oracle RAC 迁移场景 | ⭐⭐⭐(K8s 落地复杂度高) |
💡 落地建议:90% 以上金融信创项目优先选择「一主一备 + 自动监视器」方案,基于达梦原生 Data Guard 能力,成熟稳定、排障简单、改造成本可控;DSC 方案对共享存储性能要求极高,K8s 环境落地复杂度高,非必要不优先选用。
1.3 整体高可用架构总览
业务应用(配置连接服务名自动重连)
↓
dmdb-svc(ClusterIP)
↓
┌─────────────┐ 流复制同步 ┌─────────────┐
│ dmdb-0 │ ───────────────→ │ dmdb-1 │
│ 主库实例 │ │ 备库实例 │
│ dmwatcher │ │ dmwatcher │
└─────────────┘ └─────────────┘
↓ ↓
pvc-dmdb-0 pvc-dmdb-1
(独立数据卷) (独立数据卷)
↘ ↙
dmdb-monitor(独立监视器Pod,自动裁决切换)
1.4 为什么现阶段优先原生方案而非 Operator
很多团队一开始就想自研或引入 Operator,但实际落地中,达梦官方 Operator 成熟度有限,自研 Operator 运维成本高、排障难度大。对于绝大多数项目,原生方案具备三个核心优势:
- 排障简单:所有问题都可以用达梦原生运维思路排查,没有额外黑盒
- 稳定性高:依赖的都是 K8s 和达梦最成熟的核心能力
- 改造成本低:从物理机迁移到容器,运维逻辑基本不变,团队学习成本低
当数据库实例规模超过 30 套、需要统一管控平台时,再逐步演进到 Operator 方案更合理。
二、前置环境与生产级镜像制作
2.1 集群基础要求
- K8s 版本:1.24+(本文基于 1.26 实测)
- 容器运行时:containerd 1.6+ / Docker 20.10+
- 存储:至少 1 套生产级块存储 StorageClass,支持 ReadWriteOnce
- 节点:至少 2 个工作节点,单节点预留 4C8G 以上资源
- 网络:节点间网络延迟 < 3ms,保证主备同步效率
2.2 生产级镜像制作(附完整 Dockerfile)
采用多阶段构建,减小镜像体积,剔除安装包、调试工具等冗余文件,同时保留所有运维工具。
# 构建阶段:静默安装达梦数据库
FROM centos:7 AS builder
WORKDIR /opt/dm_install
# 拷贝达梦安装包(需提前放入构建目录,替换为实际安装包名)
COPY dm9_enterprise_xxx.iso /opt/dm_install/
# 挂载镜像并执行静默安装
RUN mkdir -p /mnt/dmiso && mount -o loop /opt/dm_install/dm9_enterprise_xxx.iso /mnt/dmiso && \
echo "
KEY 0
INSTALL_TYPE 1
LANG 0
INSTALL_PATH /opt/dmdbms
" > /tmp/install_rsp && \
/mnt/dmiso/DMInstall.bin -q /tmp/install_rsp && \
umount /mnt/dmiso && rm -rf /opt/dm_install /tmp/install_rsp
# 运行阶段:最小化运行镜像
FROM centos:7
LABEL maintainer="信创运维团队"
# 安装系统依赖
RUN yum install -y glibc libgcc libstdc++ numactl-libs net-tools && \
yum clean all && rm -rf /var/cache/yum
# 创建达梦运行用户与用户组(UID/GID 固定为5236,与官方一致)
RUN groupadd -g 5236 dinstall && \
useradd -u 5236 -g dinstall -m -d /home/dmdba -s /bin/bash dmdba
# 从构建阶段拷贝完整达梦安装目录
COPY --from=builder /opt/dmdbms /opt/dmdbms
# 配置环境变量
ENV DM_HOME=/opt/dmdbms \
PATH=$PATH:/opt/dmdbms/bin \
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/dmdbms/bin \
TZ=Asia/Shanghai
# 创建数据、日志、归档、备份目录
RUN mkdir -p /dm/data /dm/log /dm/arch /dm/backup /dm/template && \
chown -R dmdba:dinstall /dm /opt/dmdbms
# 切换为非root用户运行
USER dmdba
WORKDIR /opt/dmdbms/bin
# 拷贝启动脚本与健康检查脚本
COPY --chown=dmdba:dinstall entrypoint.sh /opt/dmdbms/bin/entrypoint.sh
COPY --chown=dmdba:dinstall health_check.sh /opt/dmdbms/bin/health_check.sh
RUN chmod +x /opt/dmdbms/bin/entrypoint.sh /opt/dmdbms/bin/health_check.sh
EXPOSE 5236 5536
ENTRYPOINT ["/opt/dmdbms/bin/entrypoint.sh"]
💡 镜像优化要点:
- 多阶段构建,镜像体积从 2G + 压缩到 800M 左右
- 全程使用非 root 用户运行,符合生产安全规范
- 统一时区为东八区,避免业务时间错乱
- 保留 disql、dmrman、dmwatcher 等所有运维工具,不裁剪功能
2.3 授权文件适配说明
⚠️ 生产环境授权必须提前适配容器场景,三类授权适配方式不同:
- 硬件绑定型授权:绑定 MAC/CPU 的授权无法直接在容器漂移场景使用,需固定 Pod 调度节点 + hostNetwork 模式部署
- 软授权 / 容器适配授权:向达梦厂商申请容器场景专用授权,通过 Secret 挂载,支持 Pod 跨节点漂移
- 试用授权:严禁用于生产,到期后数据库会自动关停,造成业务中断
三、基础资源全量配置(可直接复制)
3.1 命名空间创建
# dmdb-base.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dmdb
labels:
app: dmdb
3.2 核心配置 ConfigMap
存放数据库参数、守护进程、监视器模板配置,避免硬编码进镜像。
# dmdb-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: dmdb-config
namespace: dmdb
data:
dm.ini.template: |
[INSTANCE]
INSTANCE_NAME = {INST_NAME}
PORT_NUM = 5236
[MEMORY]
MEMORY_TARGET = 2048
BUFFER = 1024
MAX_BUFFER = 2048
[LOG]
LOG_PATH = /dm/log
LOG_FILE_SIZE = 256
LOG_SPACE_LIMIT = 4096
[ARCHIVE]
ARCH_INI = 1
ARCH_DEST = /dm/arch
ARCH_FILE_SIZE = 256
ARCH_SPACE_LIMIT = 10240
[CHECKPOINT]
CHECKPOINT_INTERVAL = 300
CHECKPOINT_COMPLETED_TARGET = 0.9
dmwatcher.ini.template: |
[WATCHER]
WATCHER_TYPE = GLOBAL
WATCHER_INTERVAL = 3
SWITCH_INTERVAL = 10
INST_ERROR_TIME = 10
RECOVER_TIME = 60
AUTO_RECOVER = 1
[INST0]
INST_NAME = dmdb0
INST_HOST = {MASTER_HOST}
INST_PORT = 5236
WATCHER_PORT = 5536
[INST1]
INST_NAME = dmdb1
INST_HOST = {SLAVE_HOST}
INST_PORT = 5236
WATCHER_PORT = 5536
dmmonitor.ini.template: |
[MONITOR]
MONITOR_TYPE = GLOBAL
MONITOR_INTERVAL = 2
MONITOR_LOG_PATH = /dm/log
[DG1]
WATCHER_NUM = 2
WATCHER_HOST0 = {MASTER_HOST}
WATCHER_PORT0 = 5536
WATCHER_HOST1 = {SLAVE_HOST}
WATCHER_PORT1 = 5536
3.3 敏感信息 Secret
密码、授权文件统一通过 Secret 管理,禁止明文写在配置中。
# dmdb-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: dmdb-secret
namespace: dmdb
type: Opaque
data:
sysdba_password: U1lTREJBMTIzNDU2 # base64编码后的SYSDBA密码,替换为实际值
---
apiVersion: v1
kind: Secret
metadata:
name: dmdb-license
namespace: dmdb
type: Opaque
data:
dm.lic: 你的授权文件base64编码内容
3.4 RBAC 权限配置(角色标签同步用)
用于 Sidecar 自动更新 Pod 角色标签,实现流量自动切换。
# dmdb-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: dmdb-role-sync
namespace: dmdb
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: dmdb-role-sync
namespace: dmdb
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dmdb-role-sync
namespace: dmdb
subjects:
- kind: ServiceAccount
name: dmdb-role-sync
namespace: dmdb
roleRef:
kind: Role
name: dmdb-role-sync
apiGroup: rbac.authorization.k8s.io
四、单节点生产级部署:StatefulSet + 持久化存储
先跑通单节点持久化部署,验证存储、网络、镜像无问题,再演进到主备集群,降低调试成本。
4.1 Service 配置
同时配置 Headless Service(集群内部发现用)和业务访问 Service。
# dmdb-service.yaml
apiVersion: v1
kind: Service
metadata:
name: dmdb-headless
namespace: dmdb
labels:
app: dmdb
spec:
type: ClusterIP
clusterIP: None # Headless Service核心配置
selector:
app: dmdb
ports:
- port: 5236
name: db
- port: 5536
name: watcher
---
# 业务统一访问入口,自动绑定主库角色
apiVersion: v1
kind: Service
metadata:
name: dmdb-svc
namespace: dmdb
labels:
app: dmdb
spec:
type: ClusterIP
selector:
app: dmdb
role: master # 绑定主库角色标签,切换后自动漂移
ports:
- port: 5236
targetPort: 5236
name: db
4.2 StatefulSet 完整部署配置
# dmdb-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: dmdb
namespace: dmdb
spec:
serviceName: "dmdb-headless"
replicas: 1
selector:
matchLabels:
app: dmdb
template:
metadata:
labels:
app: dmdb
role: master
spec:
serviceAccountName: dmdb-role-sync
terminationGracePeriodSeconds: 180 # 数据库优雅关停时长
# 安全上下文:非root运行,自动修正PVC权限
securityContext:
runAsUser: 5236
runAsGroup: 5236
fsGroup: 5236
runAsNonRoot: true
# 调度策略:主备强制分散节点,优先SSD节点
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- dmdb
topologyKey: kubernetes.io/hostname
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssd
initContainers:
# 初始化:目录权限校验与配置生成
- name: init-config
image: your-registry/dm9:v9.1.2.100
command: ["/bin/sh", "-c"]
args:
- |
POD_ORDINAL=$(hostname | awk -F'-' '{print $NF}')
INST_NAME="dmdb${POD_ORDINAL}"
# 生成实例专属dm.ini
sed -e "s/{INST_NAME}/$INST_NAME/g" /dm/template/dm.ini.template > /dm/data/dm.ini
# 初始化实例(仅首次启动执行)
if [ ! -f /dm/data/SYSTEM.DBF ]; then
dminit PATH=/dm/data DB_NAME=dmdb INSTANCE_NAME=$INST_NAME SYSDBA_PWD=$DM_SYSDBA_PWD
fi
env:
- name: DM_SYSDBA_PWD
valueFrom:
secretKeyRef:
name: dmdb-secret
key: sysdba_password
volumeMounts:
- name: dmdb-data
mountPath: /dm/data
- name: dmdb-config
mountPath: /dm/template
containers:
- name: dmdb
image: your-registry/dm9:v9.1.2.100
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5236
name: db
env:
- name: DM_SYSDBA_PWD
valueFrom:
secretKeyRef:
name: dmdb-secret
key: sysdba_password
volumeMounts:
- name: dmdb-data
mountPath: /dm/data
- name: dmdb-log
mountPath: /dm/log
- name: dmdb-arch
mountPath: /dm/arch
- name: license
mountPath: /opt/dmdbms/bin/dm.lic
subPath: dm.lic
- name: shm
mountPath: /dev/shm
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "8"
memory: "16Gi"
# 优雅关停钩子:正常关闭数据库
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "dm_ctl stop -D /dm/data -m immediate"]
# 存活检测
livenessProbe:
exec:
command: ["sh", "-c", "disql SYSDBA/$DM_SYSDBA_PWD@localhost:5236 -c 'select 1;' || exit 1"]
initialDelaySeconds: 90
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
# 就绪检测
readinessProbe:
exec:
command: ["sh", "-c", "disql SYSDBA/$DM_SYSDBA_PWD@localhost:5236 -c 'select status$ from v\\$instance;' || exit 1"]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 3
volumes:
- name: dmdb-config
configMap:
name: dmdb-config
- name: license
secret:
secretName: dmdb-license
- name: shm
emptyDir:
medium: Memory
sizeLimit: 2Gi
# 持久化卷模板,每个Pod自动创建独立PVC
volumeClaimTemplates:
- metadata:
name: dmdb-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-ssd" # 替换为你的存储类
resources:
requests:
storage: 100Gi
- metadata:
name: dmdb-log
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-ssd"
resources:
requests:
storage: 50Gi
- metadata:
name: dmdb-arch
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-ssd"
resources:
requests:
storage: 100Gi
4.3 部署与基础验证
# 依次部署基础资源
kubectl apply -f dmdb-base.yaml
kubectl apply -f dmdb-config.yaml
kubectl apply -f dmdb-secret.yaml
kubectl apply -f dmdb-rbac.yaml
kubectl apply -f dmdb-service.yaml
kubectl apply -f dmdb-statefulset.yaml
# 验证Pod与PVC状态
kubectl get pods -n dmdb -o wide
kubectl get pvc -n dmdb
✅ 验证标准:Pod 状态 Running,PVC 全部 Bound,能通过 dmdb-svc 正常连接数据库。
五、主备集群自动发现:零手动配置组网全实现
💡 核心思路:全程不写死任何 IP,完全依赖 StatefulSet 的固定 DNS 域名实现节点自动发现,Pod 重建后自动重连集群,无需人工修改配置。
5.1 自动发现原理
StatefulSet + Headless Service 会为每个 Pod 生成永久固定的 DNS 域名,格式为:
pod名称.service名称.namespace.svc.cluster.local
对应部署就是:
- 0 号节点:
dmdb-0.dmdb-headless.dmdb.svc.cluster.local - 1 号节点:
dmdb-1.dmdb-headless.dmdb.svc.cluster.local
无论 Pod 如何重建、IP 如何漂移,域名永远不变。启动时通过 Pod 序号自动判断角色,拼接对端域名写入配置文件,实现零手动组网。
5.2 主备自动初始化逻辑
升级 initContainer 脚本,自动判断角色并搭建主备集群:
- 读取 Pod 主机名,提取序号(0/1)
- 0 号节点:初始化为主库实例,启动数据库
- 1 号节点:循环等待主库就绪,通过 dmrman 热备份搭建备库
- 自动替换守护进程配置模板中的主备域名,生成最终配置
将 StatefulSet 的 initContainer 脚本替换为以下内容:
POD_ORDINAL=$(hostname | awk -F'-' '{print $NF}')
MASTER_HOST="dmdb-0.dmdb-headless.dmdb.svc.cluster.local"
SLAVE_HOST="dmdb-1.dmdb-headless.dmdb.svc.cluster.local"
INST_NAME="dmdb${POD_ORDINAL}"
# 生成实例专属dm.ini
sed -e "s/{INST_NAME}/$INST_NAME/g" /dm/template/dm.ini.template > /dm/data/dm.ini
if [ "$POD_ORDINAL" = "0" ]; then
# 0号节点:主库初始化
if [ ! -f /dm/data/SYSTEM.DBF ]; then
dminit PATH=/dm/data DB_NAME=dmdb INSTANCE_NAME=$INST_NAME SYSDBA_PWD=$DM_SYSDBA_PWD
# 开启归档模式
dm_ctl start -D /dm/data -m mount
disql SYSDBA/$DM_SYSDBA_PWD@localhost:5236 -c "alter database archivelog;"
dm_ctl stop -D /dm/data
fi
echo "role=master" > /dm/data/role.conf
else
# 1号节点:备库初始化,等待主库就绪后备份还原
until disql SYSDBA/$DM_SYSDBA_PWD@$MASTER_HOST:5236 -c "select 1;" > /dev/null 2>&1; do
echo "等待主库就绪..."
sleep 5
done
if [ ! -f /dm/data/SYSTEM.DBF ]; then
# 主库热备份
dmrman CTLSTMT="BACKUP DATABASE 'SYSDBA/$DM_SYSDBA_PWD@$MASTER_HOST:5236' FULL BACKUPSET '/dm/arch/master_full_bak';"
# 还原备库
dmrman CTLSTMT="RESTORE DATABASE '/dm/data/dm.ini' FROM BACKUPSET '/dm/arch/master_full_bak';"
dmrman CTLSTMT="RECOVER DATABASE '/dm/data/dm.ini' FROM BACKUPSET '/dm/arch/master_full_bak';"
dmrman CTLSTMT="RECOVER DATABASE '/dm/data/dm.ini' UPDATE DB_MAGIC;"
fi
echo "role=slave" > /dm/data/role.conf
fi
# 自动生成守护进程配置
sed -e "s/{MASTER_HOST}/$MASTER_HOST/g" -e "s/{SLAVE_HOST}/$SLAVE_HOST/g" \
/dm/template/dmwatcher.ini.template > /dm/data/dmwatcher.ini
5.3 新增守护进程 Sidecar
在 StatefulSet 的 containers 中追加守护进程容器,独立运行 dmwatcher:
- name: dmwatcher
image: your-registry/dm9:v9.1.2.100
command: ["/bin/sh", "-c"]
args:
- |
# 等待数据库就绪后启动守护进程
until disql SYSDBA/$DM_SYSDBA_PWD@localhost:5236 -c "select 1;" > /dev/null 2>&1; do
sleep 5
done
dmwatcher /dm/data/dmwatcher.ini
env:
- name: DM_SYSDBA_PWD
valueFrom:
secretKeyRef:
name: dmdb-secret
key: sysdba_password
volumeMounts:
- name: dmdb-data
mountPath: /dm/data
- name: dmdb-log
mountPath: /dm/log
resources:
requests:
cpu: "0.5"
memory: "512Mi"
limits:
cpu: "2"
memory: "2Gi"
5.4 集群部署与验证
- 将 StatefulSet 副本数调整为
replicas: 2 - 重新应用配置:
kubectl apply -f dmdb-statefulset.yaml - 验证主备同步状态
# 进入主库查看主备同步状态
kubectl exec -it dmdb-0 -n dmdb -- disql SYSDBA/SYSDBA123456 -c "select * from v\\$dataguard_status;"
✅ 正常结果:备库状态为 VALID,同步延迟为 0,无异常告警。物理备库默认处于 MOUNT 状态,开启实时查询功能后可转为 OPEN 只读状态。
六、自动监视器部署与故障自愈全流程
生产级高可用的核心是故障自愈,本方案实现两层自愈机制,覆盖进程故障、节点故障、主库宕机全场景。
6.1 自动监视器独立部署
监视器作为集群裁决节点,必须独立部署,不能和数据库实例同节点,避免单点故障。
# dmdb-monitor.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dmdb-monitor
namespace: dmdb
spec:
replicas: 1
selector:
matchLabels:
app: dmdb-monitor
template:
metadata:
labels:
app: dmdb-monitor
spec:
securityContext:
runAsUser: 5236
runAsGroup: 5236
runAsNonRoot: true
# 尽量调度到非数据库节点
nodeSelector:
node-role: database-backup
containers:
- name: dmmonitor
image: your-registry/dm9:v9.1.2.100
command: ["/bin/sh", "-c"]
args:
- |
# 生成监视器配置
MASTER_HOST="dmdb-0.dmdb-headless.dmdb.svc.cluster.local"
SLAVE_HOST="dmdb-1.dmdb-headless.dmdb.svc.cluster.local"
sed -e "s/{MASTER_HOST}/$MASTER_HOST/g" -e "s/{SLAVE_HOST}/$SLAVE_HOST/g" \
/dm/template/dmmonitor.ini.template > /dm/log/dmmonitor.ini
# 启动监视器
dmmonitor /dm/log/dmmonitor.ini
volumeMounts:
- name: monitor-config
mountPath: /dm/template
- name: monitor-log
mountPath: /dm/log
resources:
requests:
cpu: "0.5"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
volumes:
- name: monitor-config
configMap:
name: dmdb-config
- name: monitor-log
emptyDir: {}
6.2 流量自动切换:角色标签同步 Sidecar
在每个数据库 Pod 中加入轻量 Sidecar,定期检测本地数据库的主备角色,自动更新 Pod 的role标签,实现 Service 流量自动切换,无需人工干预。
在 StatefulSet 的 containers 中追加:
- name: role-sync-sidecar
image: alpine:3.18
command: ["/bin/sh", "-c"]
args:
- |
# 安装kubectl
apk add --no-cache curl
curl -LO "https://dl.k8s.io/release/v1.26.0/bin/linux/amd64/kubectl"
chmod +x kubectl && mv kubectl /usr/local/bin/
# 循环检测角色并同步标签
while true; do
# 查询数据库角色:0=主库,1=备库
ROLE=$(disql SYSDBA/$DM_SYSDBA_PWD@localhost:5236 -c "select mode$ from v\\$instance;" | grep -E "^[01]" | tr -d ' ')
if [ "$ROLE" = "0" ]; then
ROLE_LABEL="master"
else
ROLE_LABEL="slave"
fi
# 更新Pod标签
kubectl label pod $HOSTNAME role=$ROLE_LABEL --overwrite -n $POD_NAMESPACE
sleep 10
done
env:
- name: DM_SYSDBA_PWD
valueFrom:
secretKeyRef:
name: dmdb-secret
key: sysdba_password
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
resources:
requests:
cpu: "0.1"
memory: "128Mi"
limits:
cpu: "0.5"
memory: "256Mi"
💡 备选方案:业务端配置达梦连接服务名,内置主备两个地址,客户端自动识别当前主库,切换时自动重连,无需依赖 K8s 标签。两种方案可叠加使用,双重保障流量不中断。
6.3 完整故障自愈时序
主库节点宕机
↓
K8s健康检测失败 + 守护进程检测异常
↓
监视器确认故障,达到故障阈值
↓
触发主备切换:备库升为主库
↓
角色Sidecar更新Pod标签,Service流量自动切到新主库
↓
业务连接自动重连(秒级闪断)
↓
K8s重建故障节点Pod,挂载原数据卷
↓
新Pod自动同步数据,作为新备库重新加入集群
↓
集群恢复一主一备状态
6.4 第一层自愈:K8s 原生能力
- 进程崩溃:健康检测失败后,K8s 自动重启容器,数据保留,重启后自动恢复角色
- 节点故障:StatefulSet 自动将故障节点上的 Pod 调度到健康节点重建,挂载原有 PVC 数据卷
- 自愈效果:备库故障完全不影响业务;主库故障时触发数据库层面切换,K8s 负责重建故障实例
七、生产级性能调优(K8s 层 + 数据库层)
容器化数据库性能损耗是很多团队的顾虑,做好以下调优,容器化性能可接近物理机 95% 以上。
7.1 K8s 层面性能调优
1. CPU 绑定与独占
开启 Kubelet 的 CPU 管理器 static 策略,为数据库 Pod 分配独占 CPU 核心,避免上下文切换损耗。
- 节点开启 CPU 管理器:kubelet 参数
--cpu-manager-policy=static - Pod 配置 Guaranteed QoS 等级:requests 与 limits 的 CPU、内存完全一致,自动进入 static 分配池
resources:
requests:
cpu: "8"
memory: "16Gi"
limits:
cpu: "8"
memory: "16Gi"
2. 大页内存配置
达梦数据库大量使用共享内存,开启大页可显著降低 TLB 缺失,提升内存访问性能。
- 节点提前配置大页,建议大页总容量为数据库共享内存的 1.2 倍
- Pod 中挂载大页,禁用透明大页(THP),避免 IO 抖动
# volumeMounts 中追加
- name: hugepage
mountPath: /dev/hugepages
# volumes 中追加
- name: hugepage
emptyDir:
medium: HugePages
# limits 中追加大页限额
limits:
hugepages-2Mi: 2Gi
memory: 16Gi
3. IO 性能优化
- 存储类使用块存储,禁用 atime,减少元数据写入
- 本地 SSD 存储优先使用 Local PV,性能远超分布式存储
- 避免使用 overlay2 存储层存放数据,所有数据必须走 PVC 挂载
4. 网络优化
- 主备节点部署在同一机架,降低网络延迟
- 开启 SR-IOV 网络虚拟化,降低网络 IO 损耗
- 数据库流量走独立网卡,与业务流量隔离
7.2 达梦数据库层面调优
针对容器化场景调整核心参数,最大化利用容器资源。
| 参数 | 推荐值 | 说明 |
|---|---|---|
| MEMORY_TARGET | 容器内存上限的 50%~60% | 总内存池大小,预留足够内存给系统和连接进程 |
| BUFFER | 容器内存上限的 30%~40% | 数据缓冲区,OLTP 场景可适当调大 |
| MAX_CONNECTIONS | 业务峰值 * 1.2 | 连接数不宜过大,避免内存溢出 |
| CHECKPOINT_INTERVAL | 300 | 检查点间隔,延长可减少磁盘 IO,需平衡恢复时间 |
| LOG_FILE_SIZE | 512M | 重做日志文件大小,OLTP 场景建议 256M~1G |
| UNDO_RETENTION | 300 | 回滚段保留时间,避免大查询快照过旧 |
💡 调优原则:容器内存必须预留至少 30% 给系统进程、连接进程、文件系统缓存,不能把所有内存都分给数据库,否则容易触发 OOM Kill。
八、自动化备份方案:CronJob + dmrman 全量 + 增量备份
容器化不等于不需要备份,生产环境必须建立完整的备份体系,这里实现「每周全量 + 每日增量」的自动备份策略,备份文件存入独立持久化存储,支持定期清理。
8.1 备份策略设计
- 全量备份:每周日凌晨 2 点执行,保留 30 天
- 增量备份:周一至周六凌晨 2 点执行,保留 7 天
- 备份验证:每次备份完成后自动校验备份集有效性
- 备份存储:独立 PVC 存储,与数据库数据盘分离
8.2 完整备份脚本与 CronJob 配置
# dmdb-backup.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dmdb-backup-pvc
namespace: dmdb
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-ssd"
resources:
requests:
storage: 500Gi
---
# 全量备份CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: dmdb-full-backup
namespace: dmdb
spec:
schedule: "0 2 * * 0" # 每周日凌晨2点
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
securityContext:
runAsUser: 5236
runAsGroup: 5236
containers:
- name: backup
image: your-registry/dm9:v9.1.2.100
command: ["/bin/sh", "-c"]
args:
- |
BACKUP_DATE=$(date +%Y%m%d)
BACKUP_PATH=/dm/backup/full_$BACKUP_DATE
# 执行压缩全量备份
dmrman CTLSTMT="BACKUP DATABASE 'SYSDBA/$DM_SYSDBA_PWD@dmdb-svc:5236' FULL BACKUPSET '$BACKUP_PATH' COMPRESSED;"
# 校验备份集有效性
dmrman CTLSTMT="CHECK BACKUPSET '$BACKUP_PATH';"
# 清理30天前的全量备份
find /dm/backup -name "full_*" -type d -mtime +30 -exec rm -rf {} \;
env:
- name: DM_SYSDBA_PWD
valueFrom:
secretKeyRef:
name: dmdb-secret
key: sysdba_password
volumeMounts:
- name: backup-storage
mountPath: /dm/backup
resources:
requests:
cpu: "2"
memory: "4Gi"
volumes:
- name: backup-storage
persistentVolumeClaim:
claimName: dmdb-backup-pvc
---
# 增量备份CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: dmdb-incr-backup
namespace: dmdb
spec:
schedule: "0 2 * * 1-6" # 周一至周六凌晨2点
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
securityContext:
runAsUser: 5236
runAsGroup: 5236
containers:
- name: backup
image: your-registry/dm9:v9.1.2.100
command: ["/bin/sh", "-c"]
args:
- |
BACKUP_DATE=$(date +%Y%m%d)
BACKUP_PATH=/dm/backup/incr_$BACKUP_DATE
# 执行压缩增量备份
dmrman CTLSTMT="BACKUP DATABASE 'SYSDBA/$DM_SYSDBA_PWD@dmdb-svc:5236' INCREMENT BACKUPSET '$BACKUP_PATH' COMPRESSED;"
# 校验备份集有效性
dmrman CTLSTMT="CHECK BACKUPSET '$BACKUP_PATH';"
# 清理7天前的增量备份
find /dm/backup -name "incr_*" -type d -mtime +7 -exec rm -rf {} \;
env:
- name: DM_SYSDBA_PWD
valueFrom:
secretKeyRef:
name: dmdb-secret
key: sysdba_password
volumeMounts:
- name: backup-storage
mountPath: /dm/backup
resources:
requests:
cpu: "1"
memory: "2Gi"
volumes:
- name: backup-storage
persistentVolumeClaim:
claimName: dmdb-backup-pvc
💡 进阶建议:核心业务系统建议额外将备份文件同步到对象存储或异地存储,应对机房级故障。
九、生产级避坑 15 条(踩过的坑直接帮你避)
⚠️ 坑 1:共享内存不足导致实例启动失败 达梦 MEMORY_TARGET 依赖系统共享内存,容器默认 /dev/shm 只有 64M,必须通过 emptyDir 挂载扩容,否则启动直接报共享内存不足错误。
⚠️ 坑 2:硬件授权无法在容器使用 绑定宿主机 MAC/CPU 的授权文件,容器重建后硬件信息变化会导致授权失效。生产环境必须申请容器适配版授权,或固定节点 + hostNetwork 部署。
⚠️ 坑 3:优雅关停时间不足,数据文件损坏 达梦大事务回滚、检查点刷盘耗时较长,默认 30 秒关停时间会被强制 kill,导致数据文件损坏。必须设置terminationGracePeriodSeconds ≥ 180,并配置 preStop 执行正常关闭命令。
⚠️ 坑 4:数据目录权限不匹配 容器内运行达梦的用户 UID 与 PVC 挂载目录权限不匹配,会导致实例无法读写数据。通过 securityContext 的 fsGroup 自动修正,或在 initContainer 中执行 chown。
⚠️ 坑 5:主备实例名相同,守护进程启动失败 达梦 Data Guard 要求集群内所有实例名唯一,主备必须使用不同的 INSTANCE_NAME(如 dmdb0、dmdb1),否则守护进程无法识别节点,集群搭建失败。
⚠️ 坑 6:守护进程与数据库启停顺序混乱 必须先启动数据库实例,再启动守护进程,否则守护进程会误判实例故障,触发异常切换甚至脑裂。Sidecar 模式下要加启动延迟或就绪检测。
⚠️ 坑 7:主备 Pod 调度到同一节点,高可用失效 未配置 Pod 反亲和性时,K8s 可能将主备两个 Pod 调度到同一个工作节点,节点宕机后主备一起挂,高可用完全失效。必须强制配置节点级反亲和性。
⚠️ 坑 8:用共享存储跑 OLTP 数据库 ReadWriteMany 共享存储 IO 延迟高、锁冲突多,完全不适合达梦这类 OLTP 数据库。生产必须用 ReadWriteOnce 块存储,优先本地 SSD。
⚠️ 坑 9:日志归档未持久化 只持久化数据目录,日志和归档放在容器可写层,故障后日志丢失无法排障,且归档打满会导致容器磁盘爆满。
⚠️ 坑 10:主备切换后备份任务失效 备份任务固定连接 0 号节点,主备切换后 0 号变成备库,备份有效性下降。必须通过 Service 连接主库,自动跟随主库漂移。
⚠️ 坑 11:未配置大页,内存性能损耗严重 容器化场景默认不开启大页,达梦共享内存访问性能下降 10%~30%,高并发场景尤为明显。生产环境必须开启大页并禁用透明大页。
⚠️ 坑 12:时区不一致,业务数据时间错乱 容器默认 UTC 时区,达梦实例继承系统时间,会导致业务时间差 8 小时。必须挂载宿主机 localtime 或设置 TZ 环境变量为Asia/Shanghai。
⚠️ 坑 13:容器以 root 用户运行,存在安全风险 默认很多镜像用 root 运行数据库,存在权限溢出风险。生产环境必须使用非 root 用户运行,配置 securityContext 限制权限。
⚠️ 坑 14:迷信自愈能力,忽略备份 自愈只能解决硬件、进程级故障,无法应对误删表、逻辑错误、存储层故障。必须定期执行逻辑备份 + 存储快照,双重保障数据安全。
⚠️ 坑 15:未配置网络策略,数据库端口暴露全集群 默认 K8s 集群内所有 Pod 都可以访问数据库端口,存在安全隐患。生产环境必须配置 NetworkPolicy,只允许业务命名空间访问数据库端口。
十、常见故障排查手册
10.1 实例启动失败类
现象:Pod 启动后很快 CrashBackOff
排查步骤:
- 查看 Pod 日志:
kubectl logs -f dmdb-0 -n dmdb - 常见原因与解决方案:
- 共享内存不足:增大 /dev/shm 的 sizeLimit,或调小 MEMORY_TARGET
- 权限不足:检查 PVC 目录权限,确认 fsGroup 生效
- 授权失效:检查授权文件是否正确,是否为容器适配版
- 端口被占用:检查节点是否有端口冲突
现象:实例启动卡在 mount 状态
排查步骤:
- 查看数据库告警日志
- 检查是否有未完成的恢复操作
- 确认归档日志是否完整,是否存在归档缺失
10.2 主备同步异常类
现象:备库同步中断,归档日志堆积
排查步骤:
- 主库执行查询:
select * from v$dataguard_status; - 检查主备网络连通性:在备库 Pod 中 telnet 主库 5236 端口
- 检查归档日志是否被误删
- 解决方案:
- 网络问题:修复网络后自动追平
- 归档缺失:重新全量备份搭建备库
- 延迟过大:优化网络,调大同步缓冲区
现象:守护进程报实例名不匹配
排查步骤:
- 检查 dm.ini 中 INSTANCE_NAME 与 dmwatcher.ini 中是否一致
- 确认主备实例名唯一,无重复
- 检查 Host 配置是否正确,DNS 解析是否正常
10.3 性能问题类
现象:数据库 IO 延迟高
排查步骤:
- 查看存储层 IOPS、延迟指标
- 检查是否有大量小 IO、随机写入
- 优化方案:
- 调大 BUFFER 缓存,减少磁盘 IO
- 调整检查点参数,平滑写入
- 更换更高性能的存储介质
现象:数据库频繁 OOM Kill
排查步骤:
- 查看 K8s 事件,确认是否为内存超限
- 检查实际内存占用,是否连接数过多
- 优化方案:
- 调小 MEMORY_TARGET,预留更多系统内存
- 限制最大连接数,启用连接池
- 增大容器内存 limits
10.4 授权与许可类
现象:数据库运行一段时间后自动关停
排查步骤:
- 查看数据库日志,是否有授权到期提示
- 确认授权类型是否支持容器漂移
- 解决方案:更换正式商用授权,申请容器场景专用授权
十一、常用运维命令与状态验证
11.1 集群资源检查
# 查看StatefulSet与Pod状态
kubectl get statefulset -n dmdb
kubectl get pods -n dmdb -o wide
# 查看PVC绑定情况
kubectl get pvc -n dmdb
# 查看Service
kubectl get svc -n dmdb
# 查看监视器状态
kubectl get pods -n dmdb -l app=dmdb-monitor
11.2 数据库核心状态查询
# 查看实例基本信息
kubectl exec -it dmdb-0 -n dmdb -- disql SYSDBA/SYSDBA123456 -c "select name,status$,mode$ from v\\$instance;"
# 查看主备同步状态
kubectl exec -it dmdb-0 -n dmdb -- disql SYSDBA/SYSDBA123456 -c "select * from v\\$dataguard_status;"
# 查看归档状态
kubectl exec -it dmdb-0 -n dmdb -- disql SYSDBA/SYSDBA123456 -c "select * from v\\$arch_status;"
11.3 运维操作命令
# 滚动更新配置
kubectl rollout restart statefulset dmdb -n dmdb
# 查看滚动更新进度
kubectl rollout status statefulset dmdb -n dmdb
# 查看数据库日志
kubectl logs -f dmdb-0 -n dmdb -c dmdb
# 查看守护进程日志
kubectl logs -f dmdb-0 -n dmdb -c dmwatcher
总结
达梦 DM9 容器化高可用落地,核心是抓住三个本质:
- 选对工作负载:用 StatefulSet + Headless Service 解决有状态应用的固定身份与自动发现问题,这是一切的基础
- 用原生能力:基于达梦原生 Data Guard 主备架构做高可用,不重复造轮子,稳定性有保障
- 全体系保障:从调度策略、性能调优、故障自愈到备份体系,全方位覆盖生产级需求
这套方案已在多个金融信创项目落地验证,完全满足生产级高可用要求,改造成本可控,运维难度低。
政务选金仓,金融选达梦;MySQL 迁移选金仓,Oracle 迁移选达梦。
📚 专栏推荐:专注 SpringBoot3 + 人大金仓 + 达梦信创实战,持续输出生产级落地方案、避坑指南、性能调优干货,关注不迷路。
觉得文章有用的话,欢迎点赞、收藏、关注三连,后续更新更多达梦性能调优、Oracle 迁移实战的硬核内容。
更多推荐
所有评论(0)