KES 进了 K8s 之后运维归谁管?

一、先说那个绕不过去的断层

容器化改造做到最后,数据库往往是唯一一块还留在原地的东西。原因不难理解:微服务、网关、消息队列这些组件天生"无状态",Pod 挂了拉一个新的、流量大了多跑几个副本,K8s 处理起来得心应手。数据库不一样------数据要落盘、节点之间分主备、启停有严格的先后顺序。把它塞进 K8s 之后,事情并没有变简单,反而多出一层尴尬:资源是 K8s 在管,业务状态还是人在管。

于是出现了几个非常具体的麻烦:

  • 拉起一套集群要协调的资源太多。 StatefulSet、Service、Headless Service、ConfigMap、Secret、PV/PVC 得逐个配好,少一个引用或写错一个端口,Pod 就是起不来,而报错信息通常并不告诉你错在哪。
  • 两套操作体系来回切。 K8s 一侧用 kubectl,数据库一侧还得 exec 进容器敲命令,同一次运维动作被拆成两半,出问题时难以还原现场。
  • 规模一上去就开始失控。 三套集群靠人盯得住,三十套就只能靠记忆和文档,而文档总是会过期。

问题的根子在于:K8s 擅长回答"实例怎么跑起来",回答不了"集群该怎么管"。主库不可达时谁该被提升、从库的日志位点追到哪里算安全、一次升级按什么顺序滚动------这些是数据库领域的判断,K8s 原生对象里没有对应的位置去表达。

金仓给出的答案是 KES-Operator:把这些判断从人的脑子里搬到代码里,让 KES 集群以 K8s 原生对象的形式存在,运维动作从"执行命令"变成"声明目标"。

二、先把一件事讲清楚:StatefulSet 不等于数据库运维

StatefulSet 解决的是有状态实例的编排问题,它给了三样东西:稳定的网络标识(pod-0、pod-1 不会跳号)、稳定的存储绑定(重建后还是挂原来那块盘)、有序的创建与删除。这三样对数据库来说是必要条件,但远远不够。缺的那些,才是数据库运维真正的着力点:

运维动作 StatefulSet 能做到吗 说明
保证 Pod 有固定名字和存储 这是它的本职
按顺序启动 / 停止 OrderedReady 策略
判断主库是否真的不可用 不能 网络抖动和进程崩溃表现不同,需要结合复制状态判断
决定把哪个从库提升为新主 不能 要先看谁的数据最新
主备切换后把其余从库重新指向新主 不能 涉及复制链路重建
升级前的数据校验与回滚准备 不能 属于数据库专属逻辑
备份任务的调度与保留策略 不能 需要额外的控制器

一句话概括:StatefulSet 让数据库能跑在 K8s 上,Operator 让数据库能被 K8s 管住。前者是基础设施能力,后者是领域知识的封装,两者是叠加关系而不是替代关系。理解了这一点,KES-Operator 的设计思路就顺了------它并不重新发明调度和存储,而是站在 StatefulSet 之上,专门补上数据库那半边的逻辑。

三、拆开看:KES-Operator 的两个零件

KES-Operator 的骨架可以简洁地概括为 CRD + 控制器,分别承担"描述"和"收敛"两件事。

3.1 CRD:把"一套集群"变成一种 K8s 资源

CRD(Custom Resource Definition)的作用是给 K8s 的 API 新增一种资源类型。在 KES-Operator 的场景里,新增的就是"一个 KES 集群"这个概念。

这件事听起来抽象,实际意义很直接:过去集群的样貌散落在部署脚本、参数文件、值班文档和人脑里;现在它是一份可以被提交、被版本管理、被 diff 的 YAML。

yaml 复制代码
apiVersion: kdb.kingbase.com/v1
kind: KingbaseES
metadata:
  name: kes-prod
  namespace: kdb-es
spec:
  version: "V9"
  image: registry.example.com/kingbase/kes-server:V9R1
  imagePullSecrets:
    - name: reg-cred
  mode: primary-standby        # 一主多备形态
  replicas: 3                  # 1 个主节点 + 2 个备节点
  resources:
    requests:
      cpu: "2"
      memory: 4Gi
    limits:
      cpu: "4"
      memory: 8Gi
  storage:
    size: 100Gi
    storageClassName: ceph-rbd
    accessModes: ["ReadWriteOnce"]
  ha:
    failoverTimeout: 30s
    targetRPO: 0               # 期望零数据丢失
  backup:
    enabled: true
    schedule: "0 3 * * *"      # 每天凌晨 3 点
    retentionDays: 14

说明:以上是一份演示用的示意配置,字段名与取值请以你所使用版本随包附带的 CRD 定义为准。最稳妥的验证方式是在提交前先用 kubectl explain 查一遍:

bash 复制代码
# 确认 CRD 已经注册进集群
kubectl get crd | grep -i kingbase
# 逐层查看字段定义,避免凭记忆写 YAML
kubectl explain kingbasees.spec
kubectl explain kingbasees.spec.storage
kubectl explain kingbasees.spec.ha

这里体现的是声明式运维最核心的一点:你描述的是"集群应该长什么样",而不是"你要执行哪些步骤"。前者是目标,后者是实现路径。目标可以反复提交,路径一旦执行过一次就不该再来一次------这个区别在扩缩容和自愈场景里会被放大得非常明显。

3.2 控制器:声明式的关键是"比对",不是"执行"

有了 CRD,集群的期望形态就存在了。真正让它落地的,是控制器里跑着的那个循环,每一步都很朴素:

  1. 读取期望状态------从 KingbaseES 对象里拿到用户声明的副本数、存储大小、版本号;
  2. 采集实际状态------查 K8s 里现存的 Pod、PVC、Service,再向 KES 实例查询主备角色与复制延迟;
  3. 比对并修正差异------只对不一致的部分动手,缺什么补什么;
  4. 回写状态------把观测结果写进对象的状态字段,供人查看;
  5. 回到第一步------等待下一次事件或周期性触发。

关键在第 3 步的措辞:它不记录"我执行过什么",只关心"现在和目标的差距是什么"。这个特性带来两个直接好处。

一是幂等 。同一份 YAML 反复提交,结果一致,不会出现"多跑一遍就多建一套"的事故。二是自愈。因为差距被持续计算,所以无论集群是因为 Pod 被驱逐、节点下线还是被人误删而偏离目标,下一次循环都会自动把它拉回来------不需要有人记得去修。

3.3 与 KES 内核的咬合:高可用不能只靠 K8s

有一个容易被忽略的细节,值得单独拿出来说。K8s 的探针机制判断"这个容器还活着吗",靠的是探活接口。但数据库的高可用要比这精细得多:一个主库的 Pod 还在正常响应探针,不代表它还能正常承载写入;一个从库进程没挂,不代表它的日志位点没有落后。

所以 Operator 不能只盯着 K8s 层面的存活状态,必须向 KES 内核取数------看主节点的 WAL 写入状态、看从节点的同步延迟。当主节点被判定为不可达时,也不能立刻切换,而是要先确认候选新主的数据同步到了什么位置,只有延迟落在允许范围内才发起提升,目标是把 RPO 压到 0。

这一段逻辑无法用 K8s 原生对象表达,也不该硬编码进数据库内核,放在 Operator 里是位置最合适的选择:它既是集群的管理者,也是数据库内部状态的观察者。

四、动手:走一遍完整的声明式生命周期

原理讲完,落到操作上其实只有几步。以下流程基于单集群实验环境整理,生产环境的冗余要求会更高。

4.1 前置条件核对

项目 要求 备注
K8s 版本 v1.24 及以上 生产建议直接上高可用控制面
StorageClass 支持动态供给 NFS、Ceph RBD 或云厂商块存储均可,注意 RWO/RWX 能力
权限 cluster-admin 或等效 RBAC 需要创建 CRD 和 ClusterRole
镜像 已推送到可达的私有仓库 预先配好 imagePullSecrets
客户端 kubectl 版本与集群匹配 版本差过大时部分字段会提示 Unknown

4.2 安装 Operator

bash 复制代码
# 1. 创建独立命名空间,把 Operator 和业务集群放一起便于排查
kubectl create namespace kdb-es
# 2. 先装 CRD,再装 Operator(顺序不能反,否则控制器找不到资源类型)
kubectl apply -f config/crd/bases/
kubectl apply -f deploy/operator.yaml -n kdb-es
# 3. 盯住 Operator 自身是否就绪
kubectl -n kdb-es get pods -l app=kes-operator -w

Operator 自身也是一个跑在 K8s 里的普通工作负载,把它当成一个"24 小时值班的运维机器人"来看待会更容易理解------它挂了,集群就没人负责调和了。所以这一步之后建议顺手加上 PodDisruptionBudget 和资源限额。

4.3 建集群:一次提交,剩下交给循环

bash 复制代码
# 提交前面那份 kes-prod.yaml
kubectl apply -f kes-prod.yaml
# 看集群对象本身的状态字段
kubectl -n kdb-es get kingbasees kes-prod
# 观察底层 Pod 逐个就绪
kubectl -n kdb-es get pods -w
# 顺便确认 Operator 到底替我们建了哪些东西
kubectl -n kdb-es get statefulset,svc,pvc

最后一条命令的输出往往最有说服力:一份十几行的 YAML,换来了 StatefulSet、Headless Service、若干 PVC 和 ConfigMap 一整套资源。这些如果手工编排,是一轮不短的复制粘贴加排错。

4.4 扩容:改一个字段的含义

传统流程下扩容需要:申请资源、改配置、重启实例、验证状态、更新台账。声明式流程下,动作只有一个------把期望状态改掉。

bash 复制代码
# 方式一:一行 patch 直接改副本数
kubectl -n kdb-es patch kingbasees kes-prod \
  --type=merge -p '{"spec":{"replicas":5}}'
# 方式二:kubectl edit,改完保存退出即可
# kubectl -n kdb-es edit kingbasees kes-prod
# 观察 Operator 自动补齐新节点并加入复制链路
kubectl -n kdb-es get pods -w

值得注意的是,扩容过程中不需要人工判断"新节点该从哪里拉数据、IP 是什么、复制怎么建"。这些是主备架构的通用问题,Operator 已经内置了处理逻辑。人只需要为容量决策负责,不再为执行路径负责,这是声明式运维最有价值的一点。

4.5 自愈:删掉一个 Pod 看看会发生什么

验证自愈能力最直接的办法就是搞点破坏(仅限实验环境):

bash 复制代码
# 随机删掉一个从节点
kubectl -n kdb-es delete pod kes-prod-2
# 观察重建过程
kubectl -n kdb-es get pods -w
# 查看集群对象记录的状态条件,确认角色与健康度
kubectl -n kdb-es get kingbasees kes-prod \
  -o jsonpath='{.status.conditions}' | head -c 800

你会看到被删掉的 Pod 被重新拉起来、PVC 重新挂回原来的卷、节点重新以从库身份加入,整个过程没有人工介入。

这里有个容易混淆的地方需要说明:Pod 被重建这件事,StatefulSet 自己也能做到。Operator 在自愈上真正多做的事情,是判断这个节点回来之后该扮演什么角色、需不需要重建复制关系、主备拓扑有没有因此发生变化。前者是"把实例拉起来",后者是"让集群回到健康状态",差别就在这。

4.6 备份与监控:收进同一套体系

备份过去通常是独立于 K8s 之外的一套流程:单独的备份脚本、单独的定时任务、单独的存储路径。KES-Operator 把这部分也做成了可声明的对象。

yaml 复制代码
apiVersion: kdb.kingbase.com/v1
kind: KESBackup
metadata:
  name: kes-prod-20260918
  namespace: kdb-es
spec:
  clusterRef: kes-prod       # 指向要备份的集群
  type: physical             # 物理备份
  retentionDays: 14
bash 复制代码
kubectl -n kdb-es apply -f kes-backup.yaml
kubectl -n kdb-es get kesbackup
kubectl -n kdb-es describe kesbackup kes-prod-20260918

监控侧则通过纳管 KMonitor 组件,把 KES 集群的运行指标纳入 K8s 侧的观测体系。这意味着备份是否成功、集群是否健康,都可以在同一条 kubectl get 的视野里看到,而不用再登录到某台机器上去翻日志目录。

五、体验小结:顺手的地方,以及要留心的地方

用下来的整体感受是:KES-Operator 解决的是一致性问题,而不是某个单点痛点------集群怎么建、状态怎么维持、容量怎么调整、备份怎么做,第一次有了同一个入口。

比较顺手的三点:

  • 统一入口。 所有操作收敛到 kubectl 和 YAML,运维动作可审计、可回放,排查问题时不用在不同体系之间来回跳。
  • 幂等带来的安全感。 配置即真相,脚本越跑越乱的问题被从根上避免了,同一份 YAML 交给任何人执行结果都一样。
  • 天然的 GitOps 适配。 YAML 能进版本库,就意味着集群变更可以走代码评审流程,变更历史与业务代码放在一起,追溯成本大幅降低。

同时也有几处需要留个心眼:

  • 存储是真正的天花板。 Operator 能把 Pod 调起来,但调不出 IOPS。扩缩容的耗时和稳定性,很大程度上取决于底层 StorageClass 的能力,落地前建议先单独压一压存储。
  • CRD 版本演进要提前做预案。 CRD 字段一旦发生不兼容变更,存量对象在升级时会很棘手。建议在集群规模还很小的阶段就把升级路径验证一遍。
  • 有状态升级依然是高风险动作。 滚动更新对无状态服务是常规操作,对数据库则意味着数据风险。上线前务必在预发环境完整演练一次版本滚动。
  • Operator 的权限收敛要跟上。 它需要创建 StatefulSet、PVC 等资源,RBAC 授权范围偏大,生产环境建议按命名空间隔离而不是直接给集群级权限。
  • 备份要和存储策略对齐。 物理备份解决了"有备份",但还需要配套的恢复演练和快照策略,才谈得上"能恢复"。

六、写在最后

围绕数据库上 K8s,一直有种担心是"DBA 是不是要没事干了"。用下来的判断恰恰相反:活没有变少,只是变了性质。

过去 DBA 的价值大量体现在"执行得对、执行得快"------记得住参数、敲得准命令、半夜能爬起来。这一类能力现在被编码进了 Operator。而新出现的位置是:定义集群应该长什么样的人,以及判断这套定义是否合理的人。副本数给几个、故障切换的容忍窗口设多长、备份保留多少天------这些决策没有标准答案,依赖对业务的理解,也依赖对数据库的认知,代码替代不了。

从这个角度看,KES-Operator 这类工具带来的变化,与其说是岗位的收缩,不如说是职责的上移。它把确定性的事情自动化掉了,把原本被琐碎操作占满的注意力,还给了真正需要判断的地方。这大概也是声明式运维最本质的意思:人负责表达意图,系统负责抵达意图。

相关推荐
知识的搬运工旺仔1 小时前
CREATE INDEX CONCURRENTLY:线上建索引不阻塞 DML 的代价与坑
数据库·后端·sql
guo_wen_qiang2 小时前
mysql中有哪些日志
数据库·mysql
达梦数据2 小时前
达梦数据复制软件DMDRS搭建部署示例:源数据库DM8到目标数据库DM8数据迁移
数据库
沪上企服通2 小时前
旧账系统的信创迁移工程:从 MySQL/Oracle 到国产库、国密与电子会计档案闭环
数据库·笔记·数据库架构
m0_715674433 小时前
智能化+基于行标+场景化 政务数据库审计与风险监测全维度解决方案
网络·数据库·安全·网络安全·政务
天天喝旺仔3 小时前
MySQL索引优化实战:B+Tree原理、索引失效与覆盖索引调优
数据库·sql·mysql·性能优化
其实防守也摸鱼3 小时前
DeepSeek Harness 开源贡献手记:从入门到合入主线
服务器·数据库·windows·https·ssl
Omics Pro3 小时前
AI药物研发10原则!欧盟EMA×美国FDA
数据库·人工智能·算法·机器学习·自然语言处理