Kubernetes控制面扩展Operator选型与开发 - 第2节

核心概念解析:自定义资源(CRD)与自定义控制器

Operator 模式的本质是 领域知识编码。Kubernetes 提供了两个核心扩展原语:自定义资源定义(CRD) 和 自定义控制器。CRD 负责声明“世界应该是什么样”,控制器负责让“世界变成那样”。

2.1 自定义资源定义(CRD)

CRD 是 Kubernetes API 的插件机制。你通过定义 CRD,向集群注册一种新的资源类型(如 RedisCluster、PostgreSQL)。一旦注册成功,用户就可以像使用 Pod、Deployment 一样,通过 kubectl 或 API 创建、查询、更新、删除这种自定义资源实例。

维度 内置资源(如 Deployment) 自定义资源(CRD)
定义方式 Kubernetes 源码内建 YAML 文件声明,动态注册
API 路径 /apis/apps/v1 /apis/<group>/<version>
校验逻辑 内置 admission webhook OpenAPI v3 schema + 可选 webhook
存储后端 etcd(原生格式) etcd(JSON 或 Protobuf)
控制器 kube-controller-manager 用户自定义控制器
设计建议:CRD 的 spec 字段应只包含用户关心的业务意图,不要包含运行时状态。运行时状态应放在 status 子资源中,由控制器负责更新。

2.2 自定义控制器

控制器是一个无限循环的进程,它通过 List-Watch 机制监听资源的变化,并将当前状态(Actual State)调整为期望状态(Desired State)。自定义控制器专门监听你定义的 CRD 实例,并执行对应的业务逻辑。

核心工作流:
  1. Watch:通过 API Server 监听 CRD 实例的创建、更新、删除事件。
  2. Reconcile:收到事件后,读取当前集群状态,对比 CRD 中声明的期望状态。
  3. Act:调用 Kubernetes API 或外部系统接口,执行调谐动作(如创建 Service、调整副本数)。
  4. Update Status:将操作结果写回 CRD 的 status 字段。
CRD + 自定义控制器交互流程 用户 / kubectl API Server etcd 自定义控制器 (Reconcile Loop) 创建/更新 CR 持久化 Watch / Informer 创建/更新资源 更新 Status

Reconcile 循环机制

Reconcile(调谐)是控制器的核心逻辑单元。它不是一个简单的事件处理函数,而是一个 基于状态对比的闭环。每次调谐都从零开始读取当前状态,而不是依赖事件携带的增量信息。

2.3 调谐循环的输入与输出

元素 说明 典型内容
输入 触发调谐的请求对象 命名空间 + 名称(如 default/my-redis)
期望状态 从 CRD 的 spec 中读取 副本数=3,版本=7.0,持久化=true
当前状态 从集群中实时查询 当前 Pod 数=2,镜像版本=6.2
输出 调谐后的集群状态变化 创建 1 个 Pod,更新镜像版本
状态回写 更新 CRD 的 status 字段 ReadyReplicas=3,CurrentVersion=7.0
常见误区:不要假设调谐只执行一次。Kubernetes 控制器框架(如 controller-runtime)会保证调谐最终成功,但可能重试多次。你的调谐逻辑必须是 幂等 的——重复执行不会产生副作用。

2.4 调谐触发条件

调谐循环不会一直运行,它由以下事件触发:

  • 资源事件:CRD 实例被创建、更新、删除。
  • 关联资源事件:控制器 Watch 的次级资源(如 Pod、Service)发生变化。
  • 定时重调谐:为防止遗漏,控制器会定期重新调谐所有资源(默认 10 小时)。
  • 外部事件:通过自定义 Event 或 Request 手动触发。
Reconcile 循环内部状态机 触发事件 Reconcile 入口 读取 Spec 读取当前状态 对比 & 决策 期望 vs 实际 执行调谐动作 创建/更新/删除 更新 Status 写回 etcd 调谐结果反馈

声明式 API 与最终一致性

2.5 声明式 API 的核心思想

声明式 API 要求用户只描述 目标状态,而不描述 如何到达。这与命令式 API(如“运行这个脚本”、“执行那个命令”)有本质区别。

特性 命令式 API 声明式 API(Kubernetes 方式)
用户操作 执行步骤:创建 A,等待 A 就绪,再创建 B 提交 YAML:声明 A 和 B 的最终关系
错误处理 用户需自行处理中间失败 控制器自动重试,直到达到目标
可审计性 操作日志分散 所有状态变更记录在 etcd 中
幂等性 重复执行可能出错 天然幂等,重复提交相同 YAML 无副作用

2.6 最终一致性模型

Kubernetes 不保证 强一致性(即每次读取都是最新写入)。它采用 最终一致性 模型:系统会持续调谐,直到当前状态与期望状态完全一致。这意味着:

  • 短暂不一致是正常的:创建 Deployment 后,Pod 不会立即全部就绪。
  • 控制器必须容忍不一致:调谐逻辑应假设当前状态可能滞后于期望状态。
  • 乐观并发控制:多个控制器可能同时操作同一资源,使用 resourceVersion 进行冲突检测。
Operator 开发启示:
  • 不要在 CRD 的 spec 中写入运行时自动生成的值(如随机密码),这会导致每次调谐都产生 diff。
  • 使用 status 字段记录控制器观察到的实际状态,供用户或其他系统查询。
  • 调谐函数应能处理“资源不存在”的情况(如用户删除了 CRD 实例)。

2.7 声明式 API 的典型工作流

  1. 用户提交:kubectl apply -f redis-cluster.yaml
  2. API Server 校验:通过 CRD 的 OpenAPI schema 验证字段合法性。
  3. 持久化:将 CRD 实例写入 etcd。
  4. 控制器感知:Informer 收到创建事件,将请求加入工作队列。
  5. 调谐执行:控制器读取 Spec,创建 StatefulSet、Service、ConfigMap 等子资源。
  6. 状态更新:控制器将子资源的状态汇总,写回 CRD 的 status。
  7. 用户查询:kubectl get rediscluster my-cluster -o yaml 查看最终状态。
最佳实践:在 Operator 中,Reconcile 函数应该只做一件事:确保当前状态匹配期望状态。不要在其中执行长时间运行的业务逻辑(如数据迁移),这些应拆分为独立的 Job 或 Sidecar 容器。