Skip to content

About

Kubernetes Operator for declarative MySQL lifecycle, replication convergence, GTID-safe failover, upgrades, maintenance, and observable reconciliation.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

MySQL Operator

一个基于 Kubebuilder 与 controller-runtime 的 MySQL 主从集群 Kubernetes Operator:用 MysqlCluster 一个自定义资源声明集群形态,由控制器完成部署、复制拓扑收敛、GTID 安全故障转移与升级编排。故障转移的每一个阶段都被持久化在集群状态中,任何一步无法被证明安全时,控制器选择停在那里(fail closed),而不是继续推进。

Go Kubernetes License

Overview

  • 声明式集群:MysqlCluster 描述镜像、副本数(1–64)、存储、资源与两个稳定 Service(主库 / 从库),控制器负责把 StatefulSet、Headless Service、ConfigMap、PDB 收敛到期望形态。
  • 复制拓扑以证据收敛:控制器通过 SHOW SLAVE STATUS 等实际观测结果分类每个副本的复制状态,严格校验引导(bootstrap)过程,而不是假设 Pod Ready 等于复制可用。
  • GTID 安全故障转移:主库故障后先围堵(super_read_only fencing),再以围堵时捕获的 GTID 快照与每个实例的 GTID 来源信息(bootstrap GTID set)为参照做选举;找不到可证明安全的候选者时,不发生提升。
  • 升级有序推进:副本先行、主库最后,配合主从切换(handoff)与替换(replacement)两种路径,全部状态以持久化 barrier 表达,重启控制器不丢进度。

当前 v1.0.0 已完成并封板,面向单集群 MySQL 5.7 GTID 主从拓扑。

Architecture

graph TB
    subgraph "控制平面"
        M[mysql-operator<br/>MysqlCluster Reconciler]
    end

    subgraph "数据平面 StatefulSet"
        P0["mysql-0<br/>Primary"]
        P1["mysql-1<br/>Replica"]
        P2["mysql-2<br/>Replica"]
    end

    subgraph "派生资源"
        SS[StatefulSet]
        SVC1[主库 Service]
        SVC2[从库 Service]
        CM[共享 ConfigMap]
        PDB[PodDisruptionBudget]
    end

    CR["MysqlCluster CR"] --> M
    M --> SS
    M --> SVC1
    M --> SVC2
    M --> CM
    M --> PDB
    P0 -->|复制| P1
    P0 -->|复制| P2
    M -->|"pods/exec 只读观测<br/>SET super_read_only 写围堵"| P0
Loading

控制器每次 Reconcile 依次执行:可观测性状态投影 → PDB 收敛 → StatefulSet 初始化或升级运行时 → 复制与 HA 运行时(含持久化的 GTID 快照协程)。每个写资源的阶段独占一次迭代,避免一次 Reconcile 内多次写放大。

Core Capabilities

声明式副本收敛

控制器不依赖"Pod Ready = 复制健康"的假设,而是解析每个副本的实际复制状态(SHOW SLAVE STATUS),将副本分类为未初始化、需要严格引导证明、既建信道需修复等状态,再执行对应的收敛动作,并以 replicaTransition 持久化副本角色的变更过程,收敛完成前不对外宣称就绪。

GTID 安全故障转移(来源感知)

选举不是"GTID 最大者获胜"。控制器为每个实例在加入复制之前记录一份本地 GTID 来源(status.gtidBootstrap:ordinal、PVC UID、server UUID、bootstrap GTID set,全部只追加且不可变)。故障转移时,以围堵后的故障主库 GTID 快照为选举参照,结合候选者 GTID 集合与可信 bootstrap 来源判断"这个候选者的数据是否可以安全成为新主"。无法证明时,持久化"无安全候选者"并隔离可疑实例。

Fail-Closed HA 状态机

HA 状态(Healthy / Suspected / FailoverRequired / FailoverInProgress / Verifying / Degraded)与故障转移阶段(Fencing / CandidateSelected / Promoting / Reconfiguring)都持久化在 status.ha 中。怀疑状态必须跨多次调和、身份匹配后才升级为确认故障;任何阶段因证据不足无法推进时,集群停留在 Degraded 并记录事件,而不是继续执行。

写围堵与候选者接管

确认故障后,控制器通过 SET super_read_only=1 围堵故障主库,验证围堵生效(FenceState: Pending → Verified / Blocked)后才进入选举;接管阶段再次确认没有已发布的主库,然后 STOP SLAVE、SET read_only=0、发布新主角色。发现候选者已被外部发布时,将其隔离而不是在其上继续。围堵是降低旧主继续写入的风险窗口的手段,不声称消灭一切脑裂可能。

StatefulSet 运行时收敛

初始化阶段依次收敛 Headless 基础、共享 ConfigMap、不可变基础校验与 StatefulSet;运行中则做模板语义等价比对(避免无意义滚动)、缩容安全校验与 Pod 健康观测。GTID 来源记录作为初始化 barrier 的一部分,未完成来源记录前复制拓扑不会建立。

升级编排(副本先行,主库最后)

镜像变更走持久化 barrier 序列:Preparing → TemplatePending → TemplateReady → ReplicasVerified → PrimaryReady。每替换一个副本,都要证明其复制收敛后才能继续;主库切换通过 handoff(围堵校验、路由排空、权威复核)完成,替换路径则以 Pod UID 绑定校验新成员身份。升级过程中 PDB 收紧为不可驱逐。

维护安全

PodDisruptionBudget 由集群真实收敛状态推导:只有副本数收敛、镜像收敛、无升级、无副本角色变更且 HA 处于 Healthy 时,才允许 maxUnavailable=1;其余一切未知或不稳定状态都收敛为 maxUnavailable=0(fail closed),并使用 IfHealthyBudget 驱逐策略。

可观测性

  • 状态投影:status.phase、Conditions、HA/升级/复制收敛状态统一投影到 CR 状态,打印列含 Phase / Primary / Ready / Age。
  • Prometheus 指标:mysql_operator_mysqlcluster_* 前缀的 Gauge/Counter,覆盖集群、复制收敛、HA 故障转移与副本角色迁移等维度。
  • 事件与日志:关键转移(HA 状态升级、围堵结果、候选者隔离、升级 barrier)发布 Kubernetes Events;日志只输出安全字段,不落密码等敏感值。

Failover Safety Model

核心原则:每一步都必须先被证明,才能被执行;无法证明安全时,控制器停在那里。

sequenceDiagram
    participant C as Reconciler
    participant ST as status.ha
    participant FP as 故障主库
    participant CD as 候选副本

    Note over C: 观测分类: Suspected → 跨调和身份匹配 → FailoverRequired
    C->>ST: 持久化 FailoverInProgress + Stage=Fencing
    C->>FP: SET super_read_only=1 并验证
    alt 围堵验证通过 (FenceState=Verified)
        C->>FP: 读取围堵后 GTID 快照作为选举参照
        C->>CD: 结合 bootstrap GTID 来源比较候选者
        alt 存在可证明安全的候选者
            C->>ST: Stage=CandidateSelected
            C->>CD: 复核无已发布主库 → STOP SLAVE → SET read_only=0
            C->>ST: Stage=Promoting → Reconfiguring
            C->>CD: 其余副本以 GTID 祖先校验重新接入
            Note over C,CD: 祖先不明的成员被隔离,不接入复制拓扑
            C->>ST: Verifying → Healthy
        else 找不到安全候选者
            C->>ST: 持久化"无安全候选者",集群停留 Degraded
        end
    else 围堵被阻断 (FenceState=Blocked)
        C->>ST: 中止故障转移,等待人工介入
    end
Loading

这条链路上的每个持久化阶段都可以从任意控制器重启后继续;status.ha.failover 中的 fencedPrimaryGTIDSet、failedPrimaryServerUUID 等字段把"依据什么做的决定"也留在了状态里。

Lifecycle

阶段 控制器行为
初始化 收敛 Headless Service / ConfigMap / StatefulSet → 按序启动实例 → 记录 GTID 来源 → 建立复制 → Running
稳态运行 持续观测复制状态与主库身份,PDB 随收敛状态推导,指标与事件持续输出
主库故障 Suspected → FailoverRequired → Fencing → 选举 → 接管 → 副本重配置 → Verifying → Healthy
副本故障 单副本按收敛分类修复或严格重建,不触发全局故障转移
镜像升级 副本先行逐个替换并验证收敛 → handoff 主库切换 → PrimaryReady
缩容 校验缩容安全后再执行,避免丢失仍承载复制的实例

Custom Resource

apiVersion: apps.rubioooooo.github.io/v1
kind: MysqlCluster
metadata:
  name: mysql-demo
spec:
  image: mysql:5.7            # 替换为你环境中的 MySQL 5.7 镜像
  replicas: 3                 # 1-64,默认 3
  masterService: mysql-demo-primary    # 稳定指向主库的 Service 名,创建后不可变
  slaveService: mysql-demo-replicas    # 稳定指向副本的 Service 名,创建后不可变
  credentialsSecretName: mysql-demo-credentials  # 需含 root-password 与 replication-password
  storage:
    storageClassName: standard
    size: 10Gi
  resources:
    requests:
      cpu: 500m
      memory: 1Gi

完整字段定义与 CEL 校验规则见 api/v1/mysqlcluster_types.go;状态中的 ha、upgrade、replicaTransition、gtidBootstrap 是只读的持久化决策记录。

Validation

  • 测试框架:envtest(Kubernetes 1.30.0 API server),覆盖控制器真实调和路径。
  • 覆盖率:internal/controller 语句覆盖率 81.5%(make test 实测,见 cover.out)。
  • 测试内容:HA 状态机转移、围堵与选举、候选者隔离、副本收敛分类、GTID 来源记录、升级 barrier、PDB fail-closed 推导等。
make test          # envtest 单测(含 vet)
make test-e2e      # 端到端测试,需 MYSQL_OPERATOR_E2E=1 且显式指定上下文

运行时验证材料(基线 YAML 与执行证据)见 runtime/baseline/ 与 docs/evidence/。

Repository Structure

├── api/v1/                 # MysqlCluster CRD 类型定义与 CEL 校验
├── internal/controller/    # 全部调和逻辑(HA / 复制 / 升级 / PDB / 可观测性)
├── cmd/                    # manager 入口
├── config/                 # Kustomize 清单(CRD / RBAC / manager / samples)
├── runtime/baseline/       # 运行时基线与验证证据
├── test/                   # e2e 测试与工具
├── docs/                   # 深度技术复盘与工程笔记
└── hack/                   # 开发脚本

Getting Started

安装 CRD 与控制器(镜像需自行构建推送):

make docker-build docker-push IMG=<your-registry>/mysql-operator:tag
make install
make deploy IMG=<your-registry>/mysql-operator:tag

创建集群(样例见 config/samples/apps_v1_mysqlcluster.yaml):

kubectl apply -f config/samples/apps_v1_mysqlcluster.yaml
kubectl get mysqlcluster -w

卸载:

make undeploy
make uninstall

Project Context

本仓库是整体云原生工程体系中的数据层控制平面组件,与应用云原生改造和 Kubernetes/SRE 验证工程分别独立维护:

项目 定位
smart-city-archive-cloud-native 应用侧云原生改造(业务系统上云)
opslab-k8s Kubernetes / SRE 可靠性工程实验场
mysql-operator(本仓库) 有状态数据库控制平面:复制收敛、GTID 故障转移、升级编排

三个项目分别覆盖应用、平台与数据三层,各自独立开发与验证;它们不是一条端到端打通的生产环境链路。

Engineering Notes

深入的设计取舍与阶段复盘:

Scope

  • 当前面向 MySQL 5.7 GTID 主从复制的单主多从拓扑;多主、MySQL 8 特性、备份恢复、跨集群复制均在规划之外。
  • 故障转移依赖 pods/exec 进行只读观测与围堵写入,需要对应的 RBAC 与网络可达性。
  • 部分字段声明后不可变(如 masterService / slaveService、credentialsSecretName),完整约束以 api/v1/mysqlcluster_types.go 中的注释与 CEL 校验规则为准。

Contributing

欢迎 Issue 与 PR。运行 make help 查看全部目标;提交前请确保 make test 通过。

License

Copyright 2024 Rubioooooo.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

About

Kubernetes Operator for declarative MySQL lifecycle, replication convergence, GTID-safe failover, upgrades, maintenance, and observable reconciliation.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages