3. Operator 核心概念:CRD、CR、Controller 与 Manager 深度解析
在 Operator 开发中,CRD(Custom Resource Definition)、CR(Custom Resource)、Controller 与 Manager 构成了最核心的抽象层。理解这四者的关系与工作原理,是编写稳定、可测试 Operator 的基础。
核心关系速览:CRD 定义“是什么”(资源结构),CR 表达“要什么”(期望状态),Controller 负责“做什么”(调谐逻辑),Manager 管理“怎么活”(生命周期与依赖)。
3.1 CRD 与 CR:资源定义与实例
3.1.1 什么是 CRD?
CRD 是一种 Kubernetes API 扩展机制,允许你定义新的资源类型。它本质上是一个 API 资源的蓝图,描述了该资源的名称、作用域(Namespaced / Cluster)、版本以及 OpenAPI v3 校验 schema。
| 属性 | 说明 | 典型值 |
|---|---|---|
group |
API 组,用于逻辑分组 | example.com |
names.kind |
资源类型名称,首字母大写 | MyApp |
names.plural |
复数名称,用于 URL 路径 | myapps |
scope |
资源作用域 | Namespaced 或 Cluster |
versions[].schema |
OpenAPI v3 结构定义 | 包含 spec 和 status 字段 |
3.1.2 什么是 CR?
CR 是 CRD 的一个具体实例,类似于面向对象编程中“类”与“对象”的关系。用户通过提交 CR 的 YAML 清单,向 Kubernetes 声明一个期望的应用状态。
apiVersion: example.com/v1
kind: MyApp
metadata:
name: myapp-sample
spec:
replicas: 3
image: nginx:1.25
serviceType: ClusterIP
测试要点:在自动化测试中,CRD 的 schema 校验是第一个验证点。测试应覆盖:必填字段缺失、字段类型错误、枚举值越界、额外字段拒绝等场景。CR 的创建/更新/删除操作应作为每个测试用例的起点。
3.1.3 CRD 与 CR 的关系图
3.2 Controller:调谐循环的核心
3.2.1 工作原理
Controller 是一个持续运行的调谐循环(Reconciliation Loop)。它监听 CR 或其他资源的变化,将当前状态调整为期望状态。每次调谐包含四个步骤:
- 观察(Watch):通过 Informer 监听 CR 及关联资源的事件(Add/Update/Delete)。
- 读取(Read):从 API Server 获取当前 CR 实例及集群当前状态。
- 比较(Diff):对比期望状态(CR.spec)与当前状态(Deployment、Service 等)。
- 执行(Act):创建、更新或删除子资源,使集群状态趋近期望状态。
// 伪代码:调谐循环核心逻辑
func (r *MyAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// 1. 获取 CR 实例
myApp := &examplev1.MyApp{}
if err := r.Get(ctx, req.NamespacedName, myApp); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 2. 期望状态:从 CR.spec 中提取
desiredReplicas := myApp.Spec.Replicas
// 3. 当前状态:查询 Deployment
deployment := &appsv1.Deployment{}
// ... 查询逻辑
// 4. 比较并执行调谐
if currentReplicas != desiredReplicas {
// 更新 Deployment
}
return ctrl.Result{}, nil
}
3.2.2 Controller 的稳定性测试要点
| 测试维度 | 测试场景 | 预期行为 |
|---|---|---|
| 事件触发 | 创建/更新/删除 CR | Controller 在合理时间内触发调谐 |
| 幂等性 | 重复调谐同一 CR | 不产生重复资源,状态不漂移 |
| 错误恢复 | API Server 临时不可用 | 重试队列机制生效,不丢失事件 |
| 并发安全 | 多个 CR 同时变更 | 调谐互不干扰,无竞态条件 |
3.3 Manager:Controller 的运行时容器
3.3.1 Manager 的职责
Manager 是 controller-runtime 库提供的顶层抽象,负责管理多个 Controller 的生命周期、共享依赖和健康状态。它解决了以下问题:
- 共享客户端:所有 Controller 共用同一个缓存 Client,减少 API Server 压力。
- Leader 选举:高可用部署时,确保同一时刻只有一个 Manager 实例执行调谐。
- 健康探针:内置 /healthz 和 /readyz 端点,供 Kubernetes 存活/就绪检查。
- 优雅关闭:收到 SIGTERM 信号时,等待正在执行的调谐完成再退出。
// Manager 初始化示例
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: ":8080",
HealthProbeBindAddress: ":8081",
LeaderElection: true,
LeaderElectionID: "myapp-operator-lock",
})
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
// 注册 Controller 到 Manager
if err = (&controllers.MyAppReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller")
os.Exit(1)
}
// 启动 Manager(阻塞)
if err := mgr.Start(ctx); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
3.4 Controller 与 Manager 的协作架构
3.5 四者协作的完整流程
当用户提交一个 CR 后,Operator 内部的工作流程如下:
- API Server 校验:根据 CRD 的 schema 验证 CR 的合法性,存入 etcd。
- Informer 通知:Manager 内部的 Informer 监听到 CR 创建事件,将事件放入工作队列。
- 调谐触发:Controller 的工作线程从队列中取出事件,调用
Reconcile()函数。 - 状态读取:通过共享 Client 读取 CR 及关联资源(Deployment、Service 等)。
- 差异计算:对比期望状态(CR.spec)与当前状态,生成操作列表。
- 资源操作:调用 Kubernetes API 创建/更新/删除子资源。
- 状态更新:将调谐结果写回 CR 的
status字段,供用户或其他组件读取。
稳定性陷阱:如果 Controller 在调谐过程中崩溃,Manager 的优雅关闭机制会等待当前调谐完成(最多等待
GracefulShutdownTimeout)。测试中应模拟 SIGTERM 信号,验证未完成的调谐是否被正确重入或丢弃,避免资源状态不一致。
3.6 自动化测试中的核心验证点
| 组件 | 验证项 | 测试方法 |
|---|---|---|
| CRD | Schema 校验、版本兼容性 | 创建非法 CR 应被 API Server 拒绝;多版本 CRD 应支持转换 |
| CR | 创建/更新/删除生命周期 | 通过 kubectl 或 client-go 操作 CR,验证最终状态 |
| Controller | 调谐幂等性、错误重试、并发安全 | 重复调谐同一 CR;注入 API 错误;并发创建多个 CR |
| Manager | Leader 选举、健康探针、优雅关闭 | 启动两个副本,验证只有一个 Leader;访问 /healthz;发送 SIGTERM |
测试框架建议:使用
envtest(controller-runtime 内置)在本地启动轻量级 API Server 和 etcd,无需真实集群即可测试 CRD 注册、CR 操作和 Controller 调谐逻辑。这是 Operator 单元测试和集成测试的标准实践。