K8s Controller Operator 开发
K8s Controller/Operator 开发
一句话:K8s 的灵魂是 声明式 + 调谐(Reconcile)——你描述”期望状态”,Controller 不断对比”实际状态”并修正。Go 是写 Controller 的母语(client-go / controller-runtime)。本文是你
K8s 特性详解系列在”怎么造 K8s 扩展”层面的补全。
前提:
Go 基础速查已讲模块与基础。本文讲控制循环的内部机制。
client-go 架构(底层机制)
graph LR
API[K8s API Server] --> REF[Reflector]
REF --> DELTA[DeltaFIFO 队列]
DELTA --> INFORMER[Informer]
INFORMER --> INDEX[(本地 Indexer 缓存)]
INFORMER --> HANDLER[Event Handler]
HANDLER --> WORKQ[Workqueue]
WORKQ --> RECON[Reconcile 逻辑]
RECON --> API2[调 API 修正状态]
- Reflector:watch API Server,把事件写进 DeltaFIFO
- Informer:消费队列、更新本地缓存(Indexer)、触发回调
- Indexer:本地缓存,避免每次读 API(降负载)
- Workqueue:去重 + 重试,保证事件最终被处理
- Reconcile:你写的业务逻辑——对比期望 vs 实际,下发修正
关键认知:Informer 的本地缓存让 Controller 几乎不直连 API Server 读状态,只有”修正动作”才写 API。这是 K8s 能 scale 到上千节点的原因。
controller-runtime:别再手写 Informer
直接 client-go 写很啰嗦,生产用 controller-runtime(kubebuilder 底层):
func (r *MyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var obj MyCRD
if err := r.Get(ctx, req.NamespacedName, &obj); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 1. 对比期望 vs 实际
if !obj.Spec.Ready {
// 2. 修正:创建依赖资源 / 调外部系统
if err := r.createDependency(ctx, &obj); err != nil {
return ctrl.Result{RequeueAfter: 5 * time.Second}, err // 退避重试
}
}
// 3. 更新状态
return ctrl.Result{}, r.Status().Update(ctx, &obj)
}
Reconcile 的设计哲学:
- 幂等:同一输入多次调用结果一致(因为会反复触发)
- 不保存状态:状态在 K8s 对象里,不在内存(重启可恢复)
- 出错就 Requeue:返回 error 或
RequeueAfter自动重试
一个最小 Operator 骨架
mgr, _ := ctrl.NewManager(cfg, ctrl.Options{Scheme: scheme})
_ = ctrl.NewControllerManagedBy(mgr).
For(&myv1.MyCRD{}). // watch 哪种资源
Owns(&appsv1.Deployment{}). // 它创建的子资源
Complete(&MyReconciler{Client: mgr.GetClient(), Scheme: mgr.GetScheme()})
_ = mgr.Start(ctrl.SetupSignalHandler())
CRD 设计原则
| 原则 | 说明 |
|---|---|
| Spec/Status 分离 | Spec = 期望(用户写),Status = 实际(Controller 写) |
| Status 要有条件 | conditions[] 表达进度(Progressing/Ready/Failed) |
| 用 Finalizer | 删除前做清理(如释放外部资源),避免孤儿资源 |
| 版本化 | v1alpha1 → v1beta1 → v1,用 conversion webhook |
| 不要塞太多 | 一个 CRD 管一类事,避免”上帝对象” |
与你的 vault 强相关
K8s 特性详解(25 篇):你已精通 K8s 对象模型、准入控制、调度——本文是”怎么扩展它”Cilium 架构与数据面组件:Cilium 本身就是一个超复杂 Operator(cilium-operator 调谐 CiliumNode/CiliumEndpoint)CI-CD:Operator 用 kubebuilder 生成,CI 里跑make test+ 镜像构建GPU 集群运维:自定义 GPU 调度 Operator 的思路同源
常见坑
| 坑 | 现象 | 解法 |
|---|---|---|
| Reconcile 不幂等 | 反复创建重复资源 | 先 Get 判断存在再创建 |
| 缓存与直连不一致 | 读到的状态过期 | 用 r.Get(走缓存)还是 APIReader(直连)要分清 |
| 事件风暴 | 一个变更触发无限 Reconcile | 比较 Spec 变化才下发;用 Owns 正确设 owner reference |
| Finalizer 泄漏 | 资源删不掉(Terminating) | Finalizer 清理逻辑必须成功才移除 |
关联知识
- Go 基础速查 — 模块/语法基础
- K8s 网络架构总览 /
K8s 特性详解— 对象模型与调度基础 - Cilium 架构与数据面组件 — 真实 Operator 范例
- CI-CD 最佳实践与安全 — Operator 的 CI 流程
- Go 并发模型深入 — Reconcile 并发安全
参考资源
- client-go 示例:kubernetes/client-go/blob/master/examples
- controller-runtime 文档
- Kubebuilder 官方 Book
学习时间
| 内容 | 日期 | 状态 |
|---|---|---|
| K8s Controller/Operator | 2026-07-24 | 完成:client-go 架构、controller-runtime、骨架、CRD 设计、坑 |
状态
- client-go 架构
- controller-runtime Reconcile
- Operator 骨架
- CRD 设计原则
- 常见坑