2. K8s Operator 基础回顾:Operator 模式、CRD 与 Controller、Reconcile 循环、Operator SDK 简介
在深入构建灰度批次发布流水线之前,必须重新夯实 Operator 的核心知识体系。Operator 是 Kubernetes 生态中实现“应用感知”自动化运维的关键模式,它将运维专家的领域知识编码为软件,持续地运行在集群中。本节将系统回顾 Operator 模式的本质、核心组件(CRD 与 Controller)、核心逻辑(Reconcile 循环)以及主流开发框架(Operator SDK)。
2.1 Operator 模式:从“资源”到“应用”的运维抽象
Kubernetes 原生提供了 Deployment、Service、ConfigMap 等基础资源来管理无状态应用。然而,对于有状态应用(如数据库、消息队列)或需要复杂运维流程的应用(如灰度发布、备份恢复),原生资源的能力边界不足。Operator 模式的核心思想是:将整个应用的运维知识(部署、扩缩容、升级、故障恢复)编码为一个 Kubernetes 扩展控制器,使其能够像管理 Pod 一样管理整个应用。
Operator 模式由两个核心组件构成:
- 自定义资源定义(CRD): 定义一个新的 Kubernetes API 资源类型,用于描述应用的“期望状态”。例如,定义一个
GreyRelease资源,包含版本号、灰度比例、批次大小等字段。 - 自定义控制器(Controller): 一个持续运行的循环,负责监听
GreyRelease资源的变化,并调用 Kubernetes API 创建、更新或删除底层资源(如 Deployment、Service、Ingress),使集群的实际状态向 CR 中定义的期望状态收敛。
| 对比维度 | 原生 Kubernetes 控制器(如 Deployment Controller) | 自定义 Operator 控制器 |
|---|---|---|
| 管理对象 | 内置资源(Pod、ReplicaSet) | 自定义资源(CR) |
| 领域知识 | 通用调度、副本管理 | 特定应用运维逻辑(如灰度策略、备份流程) |
| 复杂度 | Kubernetes 核心维护 | 由应用开发者或 SRE 团队开发维护 |
| 典型场景 | 无状态应用部署 | 有状态应用、复杂发布策略、自动化运维 |
GreyRelease Operator,它管理的不是单个 Pod,而是一组具有灰度标签的 Pod 版本集合,并动态调整 Service 的流量权重。
2.2 CRD 与 Controller:Operator 的“数据面”与“控制面”
CRD 和 Controller 是 Operator 的一体两面,缺一不可。
2.2.1 自定义资源定义(CRD)
CRD 是 Operator 的“数据面”,它定义了用户如何描述应用的期望状态。一个 CRD 包含:
- API 组与版本: 如
greyrelease.example.com/v1。 - 作用域: 集群级别(Cluster)或命名空间级别(Namespaced)。
- OpenAPI v3 Schema: 定义 CR 的
spec和status字段结构,Kubernetes API Server 会据此进行校验。
# 一个简化的 GreyRelease CRD 示例(YAML 片段)
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: greyreleases.greyrelease.example.com
spec:
group: greyrelease.example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
targetVersion:
type: string
batchSize:
type: integer
minimum: 1
canaryWeight:
type: integer
maximum: 100
status:
type: object
properties:
currentBatch:
type: integer
phase:
type: string
scope: Namespaced
names:
plural: greyreleases
singular: greyrelease
kind: GreyRelease
shortNames:
- gr
2.2.2 自定义控制器(Controller)
Controller 是 Operator 的“控制面”,它通过 Informer 机制监听 CR 及其关联资源的事件,并执行调谐逻辑。Controller 内部通常包含:
- Informer/ListWatcher: 监听 Kubernetes API 资源变化,减少对 API Server 的频繁请求。
- Work Queue: 将事件去重并排队,确保同一资源不会同时被多个协程处理。
- Reconciler: 核心业务逻辑实现,负责比较期望状态与实际状态,并执行操作。
required、enum、minimum/maximum 等约束,将错误配置在 API 层拦截,减轻 Controller 的校验负担。
2.3 Reconcile 循环:Operator 的心脏
Reconcile(调谐)循环是 Controller 的核心执行逻辑。它不是一个简单的“if-then”判断,而是一个基于事件驱动、面向期望状态、持续收敛的闭环过程。
典型的 Reconcile 循环包含以下步骤:
- 接收事件: 从 Work Queue 中取出一个键(Namespace/Name),代表一个发生了变化的 CR 或关联资源。
- 获取期望状态: 从 API Server 读取最新的 CR 实例,解析
spec字段,得到用户期望的最终状态。 - 获取实际状态: 查询集群中由该 CR 管理的所有子资源(如 Deployment、Pod),获取当前的实际运行状态。
- 比较与计算差异: 对比期望状态与实际状态,计算出需要执行的操作列表(如:创建缺失的 Deployment、更新 Service 的 Selector、删除多余的 Pod)。
- 执行操作: 通过 Kubernetes Client 调用 API Server,执行创建、更新或删除操作。
- 更新状态: 更新 CR 的
status字段,将当前进度、版本、错误信息等反馈给用户。 - 重新入队或完成: 如果操作未完成(如等待 Pod 就绪),则重新将 CR 键放入 Work Queue,等待下一次调谐;如果已完成,则不再入队。
2.4 Operator SDK 简介:加速 Operator 开发
手动编写 Informer、Work Queue 和 Reconcile 循环的样板代码非常繁琐且容易出错。Operator SDK 是 Red Hat 主导的开源框架,它封装了底层复杂性,提供了脚手架工具和标准化的开发模式,让开发者可以专注于业务逻辑。
Operator SDK 支持三种主流开发语言:
| 语言 | 底层框架 | 适用场景 | 学习曲线 |
|---|---|---|---|
| Go | controller-runtime | 生产级 Operator,性能要求高,社区生态最丰富 | 中等 |
| Ansible | operator-sdk-ansible | 运维团队,已有大量 Ansible Playbook,快速原型 | 低 |
| Helm | operator-sdk-helm | 将现有 Helm Chart 包装为 Operator,管理复杂应用 | 低 |
使用 Operator SDK(Go 版本)创建 Operator 的标准流程:
- 初始化项目:
operator-sdk init --domain=example.com --repo=github.com/example/greyrelease-operator - 创建 API 与 CRD:
operator-sdk create api --group=greyrelease --version=v1 --kind=GreyRelease --resource=true --controller=true - 实现 Reconcile 逻辑: 编辑
controllers/greyrelease_controller.go文件中的Reconcile方法。 - 生成与部署:
make docker-build docker-push IMG=your-registry/greyrelease-operator:latest和make deploy IMG=your-registry/greyrelease-operator:latest
GreyRelease Operator。我们将重点实现 Reconcile 循环中的灰度批次管理逻辑,包括:根据 batchSize 创建新版本 Pod、动态调整 Service 的 selector 权重、以及监控 Pod 就绪状态以推进下一批次。
Operator SDK 自动生成的 Reconcile 方法骨架如下:
// controllers/greyrelease_controller.go
func (r *GreyReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
// 1. 获取 GreyRelease CR 实例
var greyRelease greyreleasev1.GreyRelease
if err := r.Get(ctx, req.NamespacedName, &greyRelease); err != nil {
// 如果 CR 被删除,则忽略
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 2. 获取当前管理的 Deployment 列表
var deployments appsv1.DeploymentList
if err := r.List(ctx, &deployments, client.MatchingLabels{
"app": greyRelease.Spec.AppName,
}); err != nil {
return ctrl.Result{}, err
}
// 3. 核心调谐逻辑:比较期望版本与当前版本,执行灰度批次操作
// ... (将在后续章节详细实现)
// 4. 更新 CR 状态
greyRelease.Status.Phase = "Progressing"
if err := r.Status().Update(ctx, &greyRelease); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{RequeueAfter: 10 * time.Second}, nil // 10秒后重新调谐
}
ctrl.Result{RequeueAfter: 10 * time.Second} 是 Reconcile 循环中常用的策略,用于实现“轮询”效果。在灰度发布场景中,我们通常需要等待 Pod 就绪或批次间隔,使用 RequeueAfter 比直接返回 Requeue: true 更优雅,可以避免在短时间内频繁调谐。