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 实例,并执行对应的业务逻辑。
核心工作流:
- Watch:通过 API Server 监听 CRD 实例的创建、更新、删除事件。
- Reconcile:收到事件后,读取当前集群状态,对比 CRD 中声明的期望状态。
- Act:调用 Kubernetes API 或外部系统接口,执行调谐动作(如创建 Service、调整副本数)。
- Update Status:将操作结果写回 CRD 的
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手动触发。
声明式 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 的典型工作流
- 用户提交:
kubectl apply -f redis-cluster.yaml - API Server 校验:通过 CRD 的 OpenAPI schema 验证字段合法性。
- 持久化:将 CRD 实例写入 etcd。
- 控制器感知:Informer 收到创建事件,将请求加入工作队列。
- 调谐执行:控制器读取 Spec,创建 StatefulSet、Service、ConfigMap 等子资源。
- 状态更新:控制器将子资源的状态汇总,写回 CRD 的
status。 - 用户查询:
kubectl get rediscluster my-cluster -o yaml查看最终状态。
最佳实践:在 Operator 中,
Reconcile 函数应该只做一件事:确保当前状态匹配期望状态。不要在其中执行长时间运行的业务逻辑(如数据迁移),这些应拆分为独立的 Job 或 Sidecar 容器。