惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Secure Thoughts
The Hacker News
The Hacker News
T
Tor Project blog
P
Proofpoint News Feed
A
Arctic Wolf
T
The Exploit Database - CXSecurity.com
Cyberwarzone
Cyberwarzone
NISL@THU
NISL@THU
T
Tenable Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cisco Blogs
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
Cisco Talos Blog
Cisco Talos Blog
T
Threat Research - Cisco Blogs
K
Kaspersky official blog
Know Your Adversary
Know Your Adversary
P
Privacy & Cybersecurity Law Blog
Project Zero
Project Zero
C
Cybersecurity and Infrastructure Security Agency CISA
罗磊的独立博客
T
Tailwind CSS Blog
IT之家
IT之家
U
Unit 42
Hugging Face - Blog
Hugging Face - Blog
博客园 - 三生石上(FineUI控件)
博客园_首页
The Cloudflare Blog
J
Java Code Geeks
博客园 - 叶小钗
Engineering at Meta
Engineering at Meta
博客园 - 【当耐特】
Jina AI
Jina AI
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
S
SegmentFault 最新的问题
宝玉的分享
宝玉的分享
The GitHub Blog
The GitHub Blog
F
Fortinet All Blogs
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
Check Point Blog
Vercel News
Vercel News
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Palo Alto Networks Blog
有赞技术团队
有赞技术团队
S
Schneier on Security
The Register - Security
The Register - Security
Last Week in AI
Last Week in AI

博客园 - 左扬

Kubernetes 编程 / Operator 专题【左扬精讲】—— 深入理解 ManagedFields 字段冲突协调机制 Kubernetes 编程 / Operator 专题【左扬精讲】—— k8s Finalizers 深度解析:对象的生命周期与删除控制 Kubernetes 编程 / Operator 专题【左扬精讲】—— OwnerReference 字段与级联删除机制 Kubernetes 编程 / Operator 专题【左扬精讲】—— 深入学习 Server-Side Apply:managedFields 替代 last-applied-configuration 的演进方向 Kubernetes 编程 / Operator 专题【左扬精讲】—— k8s Annotations 与元数据体系(Operator 专题) Kubernetes 编程 / Operator 专题【左扬精讲】—— RESTMapper:把 Group / Version / Kind / Resource 四元组翻译成 REST 路径的"查字典"大师 Kubernetes 编程 / Operator 专题【左扬精讲】—— Converter 资源版本转换器 Kubernetes 编程 / Operator 专题【左扬精讲】—— Application 业务扩展:从单 Deployment 到多 Workload 的复合 Operator 演进 Kubernetes 编程 / Operator 专题【左扬精讲】—— OwnerReference / Finalizer / 准入控制:k8s 资源生命周期的三大支柱 Kubernetes 编程 / Operator 专题【左扬精讲】—— controller-runtime 框架内幕:从 Manager 到 Reconcile 的全栈拆解 Kubernetes 编程 / Operator 专题【左扬精讲】—— 生产级 Operator 最佳实践:并发安全、资源清理与高可用设计 Kubernetes 编程 / Operator 专题【左扬精讲】—— application-operator Reconcile 循环源码精讲:从 client-go Informer 到 workqueue 的全链路解剖 Kubernetes 编程 / Operator 专题【左扬精讲】—— 从零搭建一个 application-operator 新项目:脚手架、API 设计与基于原生 DeploymentStatus/ServiceStatus 的状态建模 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:生产级 Controller 实践:并发安全、资源清理与高可用设计 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析: Controller 调试与诊断工具:从日志分析到问题定位 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:DynamicClient 操作 CRD:无需代码生成的动态操作 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:控制器与 APIServer 完整交互流程:从 Watch 到缓存同步 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:错误处理与重试机制:WorkQueue 限速器详解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Leader 选举机制:高可用控制器的必备技能 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Controller 开发模式完整实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:SharedInformerFactory 与等待缓存同步 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:从认证配置到 Deployment 操作 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:版本对应、架构组件与组件关系 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Informer 源码深度解析:从底层原理到实战应用 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Reflector 源码深度解析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:ListWatcher 源码深度解析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Indexer 与 ThreadSafeStore 核心原理与源码深度剖析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:DeltaFIFO 核心原理与源码深度剖析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:workqueue 核心原理与实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— runtime.Codec 资源编解码:serializer 与 codec 差异、编解码数据结构、codec 核心调用链路 Kubernetes 编程 / Operator 专题【左扬精讲】—— Scheme 资源注册机制全解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 自定义资源的内部版本与外部版本:从源码看版本定义机制 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 1.36.1 核心 API 数据结构全解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 构建过程 【AIOPS】一文读懂LLM【左扬精讲】:从诞生到普及,解锁大语言模型的核心密码 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Hooks 源码精讲:Hooks 可观测性的无侵入式实现 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Golang 配置解析源码精讲 ——SRE 自定义 Agent 核心技巧 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Golang 并发模型解析 ——SRE 应对高并发采集的调优思路 【AIOPS】AI Agent 专题【左扬精讲】基础架构篇:MCP-VictoriaMetrics Golang 源码整体架构拆解 ——SRE 必懂的核心模块与数据流 OpenTelemetry 开发实战【左扬精讲】—— 云原生可观测体系构建与分布式追踪二次开发 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 7 —— 基于流量预测模型的智能弹性扩缩容 Operator 实战(AIOps 模型训练与智能扩容(下篇)—— 预测式弹性扩缩容 Operator 落地实现) Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 7 —— 基于流量预测模型的智能弹性扩缩容 Operator 实战(AIOps 模型训练与智能扩容(上篇)—— 时序预测模型构建与离线训练) Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 6 —— 基于运维专家知识库的智能故障诊断与排查 Operator 实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 5 —— 基于大语言模型(LLM)的实时日志流智能监测 Operator 实现 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 4 —— 基于 Operator 实现大模型私有化部署与管理 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 3(上篇)—— 面向 AI / 算力调度场景:GPU 竞价实例资源池统一调度管理 Operator 开发 Kubernetes编程 / Operator专题【左扬精讲】—— Operator 开发实战项目 2 —— 面向零售 / 电商潮汐流量难题:多云多集群数据中心级全链路弹性伸缩 DataCenter Scaler Operator 从 0 到 1 全链路开发 Kubernetes编程 / Operator专题【左扬精讲】—— 深入理解Kubebuilder注解:为什么Operator开发离不开这些特殊注释 Kubernetes编程 / Operator专题【左扬精讲】—— Operator 开发实战项目1 —— Applicaion Operator(通用应用生命周期管理 Operator 实战) Pod 镜像拉取失败?kubectl edit pods修改镜像地址的底层原理与实操 (该方法仅为临时应急方案,并非长期解决方案) Kubernetes编程/Operator专题精讲—— 理解控制器模式 —— 控制器模式的核心原理与实现逻辑(从原理到实践) 【AIOPS】AI Agent 专题【左扬精讲】模型微调实战:一站式平台 LLaMA-Factory 【AIOPS】AI Agent 专题【左扬精讲】基于 k8s+vLLM+Ray 分布式部署全指南:架构设计、资源调度与性能优化 【AIOPS】AI Agent专题【左扬精讲】非量化版DeepSeek分布式部署全指南:精度保障、显存规划与Ollama/vLLM选型 【AIOPS】AI Agent 专题【左扬精讲】零开发框架实现 ReAct Agent(Go SRE友好)
Kubernetes 编程 / Operator 专题【左扬精讲】—— controller-runtime、kubebuilder、operator-sdk 三大框架深度对比
左扬 · 2026-06-16 · via 博客园 - 左扬

Kubernetes 编程 / Operator 专题【左扬精讲】—— controller-runtime、kubebuilder、operator-sdk 三大框架深度对比

当我们决定在 k8s 平台上构建一个自定义控制器(Operator)时,面对的第一个问题就是:应该选 controller-runtime、kubebuilder 还是 operator-sdk?这三个名字在社区里经常被混在一起讨论,很多新手容易搞不清楚它们之间的关系——有的说"用 kubebuilder 就行",有的说"operator-sdk 更全",还有人说"controller-runtime 是底层库"。这篇文章的目标,就是把这三者的边界、功能差异、适用场景彻底讲清楚,让你读完就能做出正确的技术选型。

在正式开始之前,需要先澄清一个常见误区:这三个框架都不属于 k8s 核心代码库本身。它们是构建在 k8s 基础库之上的第三方工具链,用来帮助开发者更高效地编写 Operator 控制器。k8s 核心仓库(也就是本文分析的 k8s v1.36.1)中提供的,是它们依赖的底层基础:client-goapimachinerycode-generator 等,理解这一点对理清三者关系至关重要。

Kubernetes Operator controller-runtime kubebuilder operator-sdk Go

★ 学习重点提示  — 建议先通读全文,再重点回顾标注内容

★ 重点掌握(必须)
   • 三层架构模型:controller-runtime(底层运行时)→ kubebuilder(脚手架框架)→ operator-sdk(完整工具链),每层职责边界要清晰
   • 依赖关系:operator-sdk 内部使用 kubebuilder;kubebuilder 内部使用 controller-runtime;controller-runtime 依赖 client-go 和 apimachinery
   • 选型决策树:根据团队需求(纯控制器 vs 完整 OLM 集成 vs 底层定制)选择对应框架

☆ 次重点(了解即可)
   • controller-gen 与 kubebuilder 标记语言的历史渊源(+kubebuilder: 前缀的实际处理者)
   • Go 项目与 Ansible/Helm Operator 的区别


📑 文章目录

  1. 一、What:三个框架到底是什么
  2. 二、Why:为什么需要这些框架
  3. 三、How:三者的实际使用对比
  4. 四、SourceCode:底层实现源码解析
  5. 五、Pitfall:选型与使用中的常见陷阱
  6. 六、FAQ:高频问题解答

一、What:三个框架到底是什么

1.1 先用一句话定义

可以把它们想象成盖房子的三层:controller-runtime 是砖头和水泥(最底层的基础材料),kubebuilder 是脚手架和图纸(帮你快速搭起结构),operator-sdk 是装修队加全套工具箱(除了搭结构,还管搬家、验收、后续维护)。接下来我们逐层拆解。

1.2 controller-runtime:底层运行时库

controller-runtime(项目地址 sigs.k8s.io/controller-runtime)是一套 Go 语言库,提供了构建 k8s 控制器(Controller)所需的核心抽象。它定义了 Reconciler 接口、Builder 模式、Client 抽象、Webhook 支持、Cache(缓存层)、Leader Election(领导者选举)等基础设施。你不会直接"运行"controller-runtime,而是把它作为依赖引入到自己的项目中,用它提供的接口来编写控制器逻辑。

controller-runtime 本身不生成任何代码,它只是运行时基础设施。截至 k8s v1.36.1,controller-runtime 的最新版本为 v0.23.3,它与 client-go v0.34(对应 k8s 1.34)同步发布,每个 minor 版本都与对应的 k8s client-go 版本严格绑定。

1.3 kubebuilder:脚手架框架

kubebuilder(项目地址 sigs.k8s.io/kubebuilder)是一个用于快速搭建 k8s API 项目(CRD + Controller)的脚手架框架。它的核心价值在于提供了一组命令行工具,帮助你从零创建一个符合最佳实践的项目结构。执行 kubebuilder initkubebuilder create api 后,你会得到一个包含 API 定义、Reconciler 实现、RBAC 配置文件、Makefile 的完整项目骨架。

kubebuilder 内部依赖 controller-runtime,同时也依赖 controller-toolssigs.k8s.io/controller-tools),后者提供了 controller-gen 代码生成工具。controller-gen 负责读取代码中的 +kubebuilder: 标记,生成 CRD YAML、DeepCopy 方法、RBAC 清单等产物。

💡 注意
一个常见误区是以为 +kubebuilder: 标记是由 kubebuilder 本身处理的。实际上,这些标记完全由 controller-gen(来自 controller-tools 库)处理,"kubebuilder" 只是历史命名遗留。所以 +kubebuilder: 前缀和 kubebuilder CLI 工具是两回事。

1.4 operator-sdk:完整 Operator 工具链

operator-sdk(项目地址 github.com/operator-framework/operator-sdk)是 Operator Framework 社区提供的完整工具链。相比 kubebuilder,它在相同脚手架能力的基础上,额外提供了:Operator Lifecycle Manager(OLM)集成、Scorecard 测试框架、Operator Score 评分体系、以及 Ansible/Helm 方式编写 Operator 的支持(Go 之外,还支持用 Ansible Playbook 或 Helm Chart 来定义 Operator 行为)。

对于纯 Go 语言的 Operator 项目,operator-sdk 在底层使用 kubebuilder 的脚手架引擎——两者生成的 Go 项目结构几乎完全一致,区别主要在于 CLI 命令和额外的企业级功能。operator-sdk 的核心定位是"企业级 Operator 交付",从开发到部署到运维提供一站式支持。

1.5 三层依赖关系全景图

下面这张图展示了三者与 k8s 核心基础库之间的依赖关系。从上到下,抽象层次逐步降低,依赖关系也依次形成:

 ┌────────────────────────────────────────────────────────────┐ │                    operator-sdk                             │ │         (github.com/operator-framework/operator-sdk)         │ │  ┌─────────────────────────────────────────────────────┐   │ │  │  + OLM 集成  + Scorecard 测试  + Ansible/Helm 支持   │   │ │  │  + operator-sdk 独有 CLI 命令(run bundle 等)      │   │ │  └─────────────────────────────────────────────────────┘   │ └──────────────────────────┬───────────────────────────────────┘                            │ 内部使用(Go 项目脚手架)                            ▼ ┌────────────────────────────────────────────────────────────┐ │                      kubebuilder                            │ │                  (sigs.k8s.io/kubebuilder)                 │ │  ┌─────────────────────────────────────────────────────┐   │ │  │  + kubebuilder init / create api                   │   │ │  │  + 依赖 controller-tools(controller-gen)         │   │ │  │  + 依赖 controller-runtime                         │   │ │  └─────────────────────────────────────────────────────┘   │ └──────────────────────────┬───────────────────────────────────┘                            │ 内部使用                            ▼ ┌────────────────────────────────────────────────────────────┐ │                   controller-runtime                         │ │              (sigs.k8s.io/controller-runtime)               │ │  ┌─────────────────────────────────────────────────────┐   │ │  │  Reconciler 接口  /  Builder 模式                   │   │ │  │  Client 抽象  /  Cache  /  Webhook / Leader Election│   │ │  └─────────────────────────────────────────────────────┘   │ └──────────────────────────┬───────────────────────────────────┘                            │ 依赖            ┌───────────────┼───────────────┐            ▼               ▼               ▼ ┌──────────────────┐ ┌────────────┐ ┌──────────────────────┐ │    client-go     │ │ apimachinery│ │   apiserver library  │ │ (staging/src/   │ │ (staging/  │ │   (staging/src/      │ │  k8s.io/client- │ │ src/k8s.io/│ │    k8s.io/apiserver) │ │  go)             │ │ apimachinery)│ │                     │ └──────────────────┘ └────────────┘ └──────────────────────┘            │               │               │            └───────────────┼───────────────┘                            ▼ ┌────────────────────────────────────────────────────────────┐ │              Kubernetes v1.36.1 (核心仓库)                  │ │         这里的代码是 client-go 等库的真实来源               │ └────────────────────────────────────────────────────────────┘ 

从图中可以清晰地看到:controller-runtime 是整个工具链的底层基础,它直接建立在 client-goapimachinery 之上;kubebuilder 在 controller-runtime 之上封装了脚手架和代码生成能力;operator-sdk 在 kubebuilder 基础上进一步提供了企业级运维工具。这不是三选一的关系,而是三层叠加。


二、Why:为什么需要这些框架

2.1 从零编写控制器的痛点

如果我们完全从零(直接依赖 client-go)编写一个控制器,需要手动处理的事情非常多:自己初始化 SharedInformerFactory、为每种资源写 AddFunc/UpdateFunc/DeleteFunc 事件处理、自己维护 WorkQueue 并处理重试逻辑、自己实现 RateLimitingInterface、自己管理缓存同步(HasSynced)、自己处理优雅退出信号。这些工作每写一个控制器都要重复做一遍,而且稍有不慎就会出现缓存不同步、队列积压等隐蔽 Bug。

2.2 controller-runtime 解决什么问题

controller-runtime 把上述所有重复工作抽象成了高层次的接口和 Builder 模式。你只需要定义一个 Reconciler(接收对象,返回 Reconcile 结果),剩下的 Informer 启动、WorkQueue 管理、Leader Election 等全部由框架代为处理。对于需要精细控制底层行为的团队,controller-runtime 是最佳选择;对于只需要快速写业务逻辑的团队,直接用 kubebuilder 更省事。

2.3 kubebuilder 解决什么问题

kubebuilder 在 controller-runtime 之上又解决了一个关键痛点:CRD 代码和 CRD YAML 的同步维护问题。传统做法是手动写 CRD YAML,很容易出现 YAML 和 Go 类型定义不一致的情况。kubebuilder 的做法是"代码即配置"——你在 Go 类型上用 +kubebuilder: 标记描述期望的 schema,然后 controller-gen 自动生成 CRD YAML,保证两者永远一致。

2.4 operator-sdk 解决什么问题

operator-sdk 则更进一步,解决了"Operator 交付到集群之后怎么管理"的问题。企业场景中,一个 Operator 需要:打包成可分发的格式(Bundle)、通过 Operator Lifecycle Manager 安装和升级、支持 Operator Scorecard 自验证、可能还需要用 Ansible 或 Helm 来编写运维逻辑(而不是 Go)。operator-sdk 把这些能力都集成进来了。

2.5 传统方案 vs 框架方案对比

对比维度直接用 client-go(纯手写)controller-runtimekubebuilderoperator-sdk
抽象层次 最低,直接操作 Informer/WorkQueue 中等,提供 Reconciler 接口 较高,脚手架 + 代码生成 最高,完整工具链
CRD 生成 ❌ 纯手写 YAML ❌ 不提供代码生成 ✅ controller-gen 自动生成 ✅ controller-gen 自动生成
项目脚手架 ❌ 完全自己搭建 ❌ 不提供脚手架 ✅ kubebuilder init / create api ✅ operator-sdk init / create api
Webhook 支持 ❌ 需要手动实现 ✅ 内置支持 ✅ kubebuilder create webhook ✅ 同 kubebuilder
OLM 集成 ❌ 不支持 ❌ 不支持 ❌ 不直接支持 ✅ run bundle / OLM 完整集成
Ansible/Helm 支持 ❌ 不适用 ❌ 不支持 ❌ 仅支持 Go ✅ 支持 Go/Ansible/Helm 三种方式
典型使用场景 k8s 内部控制器(如 DeploymentController) 需要深度定制控制逻辑的团队 大多数业务 Operator 开发 企业级 Operator 分发与运维
最低学习成本 极高(需要深入理解 client-go) 较高(需要理解 Reconciler 模式) 中等(脚手架降低入门门槛) 中低(但涉及 OLM 时学习曲线陡峭)

三、How:三者的实际使用对比

3.1 直接用 client-go 手写控制器(k8s 内部模式)

先看 k8s 内部控制器(如 DeploymentController)使用的模式。这是完全基于 client-go 手写的控制器,也是 kubebuilder/controller-runtime 抽象的原始参考。k8s 源码中的 sample-controller(staging/src/k8s.io/sample-controller/)是学习这个模式的最佳示例。

第一步:定义 CRD 类型,加上代码生成标记。Go 类型定义中用注释里的 +genclient+k8s:deepcopy-gen: 标记告诉代码生成工具需要生成哪些代码:

// staging/src/k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1/types.go (k8s v1.36.1)

Go

// +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type Foo struct {     metav1.TypeMeta   `json:",inline"`     metav1.ObjectMeta `json:"metadata,omitempty"`     Spec   FooSpec   `json:"spec"`     Status FooStatus `json:"status"` }  type FooSpec struct {     DeploymentName string `json:"deploymentName"`     Replicas      *int32 `json:"replicas"` }  type FooStatus struct {     AvailableReplicas int32 `json:"availableReplicas"` }

第二步:main.go 中手动初始化 Client、InformerFactory,并启动 Worker。每一个组件都需要手动连接和管理:

// staging/src/k8s.io/sample-controller/main.go (k8s v1.36.1)

Go

func main() {     ctx := signals.SetupSignalHandler()      cfg, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig)     kubeClient, err := kubernetes.NewForConfig(cfg)     exampleClient, err := clientset.NewForConfig(cfg)      // 手动创建两个 SharedInformerFactory     kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30)     exampleInformerFactory := informers.NewSharedInformerFactory(exampleClient, time.Second*30)      controller := NewController(ctx, kubeClient, exampleClient,         kubeInformerFactory.Apps().V1().Deployments(),         exampleInformerFactory.Samplecontroller().V1alpha1().Foos())      kubeInformerFactory.Start(ctx.Done())     exampleInformerFactory.Start(ctx.Done())      if err = controller.Run(ctx, 2); err != nil {         logger.Error(err, "Error running controller")     } }

第三步:在 Controller 结构体中显式持有 kubeclientset、sampleclientset、listers、workqueue 等,手写 Reconcile 逻辑。这是 k8s 内部控制器的标准模式。

// staging/src/k8s.io/sample-controller/controller.go (k8s v1.36.1)

Go

type Controller struct {     kubeclientset    kubernetes.Interface   // k8s 标准资源客户端     sampleclientset  clientset.Interface   // 自定义资源客户端     deploymentsLister appslisters.DeploymentLister     deploymentsSynced cache.InformerSynced   // 缓存同步标记     foosLister        listers.FooLister     foosSynced        cache.InformerSynced     workqueue         workqueue.TypedRateLimitingInterface[any] }

3.2 kubebuilder 脚手架:快速生成项目

使用 kubebuilder 的开发流程非常简洁,只需要几行命令就能生成完整项目结构:

# kubebuilder 项目初始化命令 (k8s v1.36.1 验证通过)

Bash

# 1. 初始化项目(创建 main.go + Makefile + go.mod) kubebuilder init --domain mydomain.com --repo github.com/myorg/my-operator  # 2. 创建 API(自动生成 CRD 类型 + Reconciler 骨架) kubebuilder create api --group mygroup --version v1 --kind MyResource  # 3. 生成 CRD YAML + DeepCopy + Webhook 代码 make generate make manifests

执行完毕后,项目结构自动生成,包括:API 类型定义(api/v1/myresource_types.go)、Reconciler 实现(internal/controller/myresource_controller.go)、RBAC 配置(config/rbac/)、CRD YAML(config/crd/)。开发者只需要在 Reconciler 的 Reconcile 方法中填写业务逻辑。

3.3 operator-sdk:企业级额外能力

operator-sdk 的 Go 项目初始化与 kubebuilder 基本一致(因为底层用同一个脚手架引擎),但它额外提供了几个企业级命令:

# operator-sdk 企业级命令 (k8s v1.36.1 验证通过)

Bash

# 1. 初始化(与 kubebuilder 相同) operator-sdk init --domain mydomain.com --repo github.com/myorg/my-operator  # 2. 创建 API(同 kubebuilder) operator-sdk create api --group mygroup --version v1 --kind MyResource  # 3. 构建 Bundle(OLM 分发格式) operator-sdk bundle create my-operator-bundle:v1.0.0 --directory ./bundle.Dockerfile  # 4. 通过 OLM 部署到集群 operator-sdk run bundle my-operator-bundle:v1.0.0  # 5. 运行 Scorecard 测试(验证 Operator 质量) operator-sdk scorecard ./bundle

其中 run bundlescorecard 是 operator-sdk 独有的能力,kubebuilder 本身不提供这些。如果你的团队只需要编写业务控制器并直接部署到集群,用 kubebuilder 就够了;如果你的 Operator 需要通过 OperatorHub 分发或接受安全审计,operator-sdk 的这些工具能节省大量人工成本。

3.4 controller-runtime:直接使用底层库

当你需要精细控制时,可以直接引入 controller-runtime,手写 Builder 模式来组装控制器。以下是 controller-runtime 的 Reconciler 接口定义(不含 kubebuilder 脚手架):

// sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go (k8s v1.36.1)

Go

// Reconciler 核心接口:接收一个 Request,返回 Result 或错误 type Reconciler interface {     Reconcile(context.Context, Request) (Result, error) }  // Request 包含被调和对象的命名空间和名称 type Request struct {     types.NamespacedName }  // Result 表示调和结果:是否需要立即重队列,以及下次重队列的延迟 type Result struct {     Requeue      bool     RequeueAfter time.Duration }

相比 client-go 的手写模式,controller-runtime 把 WorkQueue、Leader Election、Client 抽象全部封装了,开发者只需实现 Reconcile 这一个方法。但它不提供脚手架,所有项目结构需要自己搭建——这适合对架构有特殊要求、或在现有项目中集成控制器能力的场景。


四、SourceCode:底层实现源码解析

4.1 client-go WorkQueue:控制器的脉搏

所有控制器的核心都是 WorkQueue(工作队列)。在 k8s v1.36.1 中,client-go 提供了泛型 WorkQueue 接口,是整个控制器模式的数据枢纽。理解它的接口设计,就理解了"事件如何变成工作"的核心机制:

// staging/src/k8s.io/client-go/tools/workqueue/queue.go (k8s v1.36.1)

Go

// 基础队列接口:Add / Get / Done / ShutDown type TypedInterface[T comparable] interface {     Add(item T)     Len() int     Get() (item T, shutdown bool)   // 阻塞获取队列元素     Done(item T)                     // 标记处理完成     ShutDown()     ShutDownWithDrain()     ShuttingDown() bool }  // 限速队列接口:在基础队列上增加了速率限制能力 type TypedRateLimitingInterface[T comparable] interface {     TypedInterface[T]     AddRateLimited(item T)   // 处理失败后,按限速器决策重新入队     Forget(item T)           // 处理成功后,清除该元素的限速追踪     NumRequeues(item T) int  // 返回该元素被重新入队的次数 }

AddRateLimitedForget 是限速队列最核心的两个方法。当 Reconciler 执行出错时,调用 AddRateLimited 将对象重新入队,限速器会按照指数退避策略决定下次重试的时机;处理成功后调用 Forget 清除追踪状态,防止无限重试。这个设计是 k8s 控制器"最终一致性"模型的关键支撑。

4.2 client-go SharedInformer:事件监听机制

SharedInformer 是 k8s 控制器感知集群状态变化的核心机制。它在 client-go 层面维护了一个本地缓存,通过 List/Watch 与 apiserver 保持同步,并将变化事件分发给注册的 Handler。以下是 k8s v1.36.1 中 SharedInformer 的核心接口定义:

// staging/src/k8s.io/client-go/tools/cache/shared_informer.go (k8s v1.36.1)

Go

// SharedInformer 提供最终一致的集群状态链接 // 它维护一个本地缓存(Indexer),通过 Watch 与 apiserver 同步 type SharedInformer interface {     // 添加事件处理器:Add/Update/Delete 三种事件     AddEventHandler(handler ResourceEventHandler)     // 启动 Informer:在独立 goroutine 中运行 Watch 循环     Run(stopCh  

HasSynced 是使用 SharedInformer 时最容易被忽略、但又最容易引发 Bug 的方法。在 Controller 的 Run 方法启动 Worker 之前,必须先等待 HasSynced() 返回 true,否则 Reconciler 可能在看到不完整的缓存状态时就发起了错误操作。这个等待逻辑在 sample-controller 的 controller.go 中体现得很清晰:

// staging/src/k8s.io/sample-controller/controller.go (k8s v1.36.1)

Go

func (c *Controller) Run(ctx context.Context, workers int) error {     // 等待所有 Informer 的本地缓存同步完成     if !cache.WaitForCacheSync(ctx.Done(), c.deploymentsSynced, c.foosSynced) {         return fmt.Errorf("failed to wait for caches to sync")     }      // 启动 workers,每个 worker 从 workqueue 取任务执行     for i := 0; i < workers; i++ {         go wait.Until(c.worker, time.Second, ctx.Done())     }      <-ctx.Done()     return nil }  func (c *Controller) worker() {     for c.processNextWorkItem() {} }  func (c *Controller) processNextWorkItem() bool {     obj, shutdown := c.workqueue.Get()     defer c.workqueue.Done(obj)      if err := c.syncHandler(ctx, obj); err != nil {         // 出错时按限速策略重新入队         c.workqueue.AddRateLimited(obj)     } else {         // 成功后忘记该元素         c.workqueue.Forget(obj)     }     return true }

4.3 code-generator:自动生成 Boilerplate

k8s 源码中内置了 code-generator(staging/src/k8s.io/code-generator/),它是 kubebuilder/controller-tools 内部使用的那套代码生成逻辑的原始来源。code-generator 包含多个子工具:

deepcopy-gen 生成 DeepCopy 方法 生成zz_generated.deepcopy.go

client-gen 生成 typed client Create/Update/Delete/List/Watch/Patch

informer-gen 生成 informer SharedInformerFactory

lister-gen 生成 lister List() + NamespaceLister

kubebuilder 的 make generate 命令实际上就是调用了 controller-gen(controller-tools 封装版)。controller-gen 读取 Go 源码中的 +kubebuilder: 注释标记,根据标记内容生成对应的代码。以 sample-controller 为例,运行 code-generator 后会生成这些文件:

生成工具输出文件生成内容
deepcopy-gen zz_generated.deepcopy.go DeepCopyInto / DeepCopy 方法
client-gen generated/clientset/ FooInterface(Create/Update/Delete/List...)
informer-gen generated/informers/ FooInformer + SharedInformerFactory
lister-gen generated/listers/ FooLister + FooNamespaceLister

4.4 版本对应关系

理解版本对应关系对于选型和排错非常重要。controller-runtime 每个 minor 版本都与对应的 client-go/k8s 版本严格绑定,因为它的核心就是 client-go 的封装。以下是社区维护的版本对照表:

controller-runtime 版本对应的 k8s.io/client-go最低 Go 版本大致的 k8s 版本
v0.22 v0.34 Go 1.24 k8s 1.34
v0.21 v0.33 Go 1.24 k8s 1.33
v0.20 v0.32 Go 1.23 k8s 1.32
v0.19 v0.31 Go 1.22 k8s 1.31

🚀 版本更新  — k8s v1.36.1 引入 / 变更
在 k8s v1.36.1 对应的工具链中,controller-runtime 已升至 v0.23.x 系列,配合 client-go v0.34。值得注意的是,controller-runtime v0.24.0 在 2026 年 4 月废弃了 sigs.k8s.io/controller-runtime/pkg/scheme.Builder,改用 k8s.io/apimachinery/pkg/runtime.NewSchemeBuilder,以减少 API 包对 controller-runtime 的依赖。kubebuilder go/v4 版本(支持 k8s 1.26+)已适配这一变更。


五、Pitfall:选型与使用中的常见陷阱

坑 1:混淆 controller-gen 和 kubebuilder

这是新手最容易踩的坑。很多人在 CRD 类型的 Go 代码里看到 +kubebuilder: 标记,就以为这些标记是由 kubebuilder 命令行工具处理的。实际上:所有 +kubebuilder: 标记都是由 controller-gen(来自 controller-tools 库)处理的,与 kubebuilder CLI 没有直接关系。kubebuilder 只是"恰好也用了这个前缀",而 controller-runtime 更是完全不用这些标记。

关键结论:如果你在调试 CRD 生成问题,应该检查的是 controller-gen 的版本和配置,而不是 kubebuilder 版本。

坑 2:直接导入 controller-runtime 到 API 包

从 controller-runtime v0.24.0 起,社区明确不推荐将 controller-runtime 导入到 API 包(即定义 CRD 类型的 pkg/apis/ 包)中。这是因为 controller-runtime 作为运行时依赖,版本较重,引入到 API 包会增加下游消费者的依赖负担。正确的做法是用 k8s.io/apimachinery/pkg/runtime.NewSchemeBuilder 来注册 Scheme,这与 k8s 核心代码的做法完全一致。

坑 3:HasSynced 未完成就启动 Worker

这个问题在直接使用 client-go 编写控制器时非常隐蔽。SharedInformer 启动后,本地缓存需要一定时间从 apiserver 拉取全量数据。如果在 HasSynced() 返回 true 之前就开始处理 WorkQueue 中的对象,Reconciler 可能读取到空缓存,导致"对象不存在"的错误被反复重试。sample-controller 中用 cache.WaitForCacheSync 包装了这个等待逻辑:

# 坑 3 的错误现象

E0515 deployment_controller.go:142] deployment controller cache not synced:   deploymentcache does not exist # 对象被反复 Reconcile,每次都报错 "not found",日志里大量 retry

坑 4:kubebuilder 和 operator-sdk 项目混用 CLI

由于 operator-sdk 的 Go 脚手架底层就是 kubebuilder,很多团队先用 kubebuilder 初始化项目,后来又切换到 operator-sdk 的命令。这样做虽然通常能工作,但容易出现版本不一致的问题:kubebuilder CLI 和 operator-sdk CLI 的版本不同步,导致 make generate 生成的代码格式有差异。建议在项目早期就确定使用哪个 CLI,并统一版本。

坑 5:误以为 OLM 是必须的

operator-sdk 提供的 OLM 集成非常强大,但这不意味着所有 Operator 都必须通过 OLM 部署。如果你只是在自己公司的集群中使用 Operator,直接用 kubectl apply -f 部署 CRD 和 Deployment 完全没问题。OLM 的核心价值在于 Operator 的分发市场(OperatorHub)和集群级别的版本管理——如果你的场景不需要这些,用 kubebuilder 足矣。

💡 注意
OLM 本身需要集群提前安装 Operator Lifecycle Manager 组件。如果你的集群是托管服务(如 GKE、EKS),需要确认是否已经默认安装了 OLM。


六、FAQ:高频问题解答

下面汇总了 20 个关于 controller-runtime、kubebuilder、operator-sdk 的高频问题,涵盖选型决策、原理理解、生产实践三个维度。


Question 1:我想新写一个业务 Operator,应该选 kubebuilder 还是 operator-sdk?

Answer: 如果你不需要 OperatorHub 分发和 OLM 集成,直接选 kubebuilder。它上手最简单,社区活跃度高,文档最完善。Go 项目用 kubebuilder init 和 kubebuilder create api 就能快速出活。operator-sdk 适合企业级场景:需要通过 OperatorHub 分发、需要 Scorecard 评分验证、或团队已经重度依赖 OLM 进行版本管理。


Question 2:controller-runtime 和 client-go 是什么关系?应该用哪个?

Answer: client-go 是 k8s 官方提供的底层客户端库,controller-runtime 是在 client-go 之上封装的控制器构建库。绝大多数 Operator 开发场景下,你应该用 controller-runtime(或通过 kubebuilder 使用它)。只有在以下情况才需要直接用 client-go:需要构建与 k8s 控制平面交互的工具(非控制器)、对 Informer/WorkQueue 有特殊定制需求、或者在构建 controller-runtime 本身的场景。


Question 3:kubebuilder 和 operator-sdk 的项目结构有什么区别?

Answer: 对于纯 Go Operator 项目,kubebuilder 和 operator-sdk 生成的目录结构几乎完全一致——都包含 api/、controllers/、config/ 子目录,都使用相同的 Makefile 目标和 controller-gen 代码生成流程。两者的差异在于 operator-sdk 额外多出了 bundle/(OLM 分发格式)和 Makefile 中的 scorecard 相关目标,以及 operator-sdk 特有的 CLI 命令(run bundle、run packagemanifests 等)。


Question 4:controller-gen 和 kubebuilder 是什么关系?

Answer: controller-gen 是代码生成工具,它读取 Go 代码中的 +kubebuilder: 标记,生成 CRD YAML、DeepCopy 方法、RBAC 配置等产物。controller-gen 来自 controller-tools 库(sigs.k8s.io/controller-tools),kubebuilder 内部依赖它。kubebuilder CLI 本身不处理这些标记,+kubebuilder: 前缀中的 "kubebuilder" 只是历史命名。所以当你遇到"CRD 生成的 schema 不对"时,应该检查的是 controller-gen 版本,而不是 kubebuilder 版本。


Question 5:为什么不直接用 k8s 内部的 code-generator,而要用 kubebuilder?

Answer: k8s 源码中的 code-generator 是给 k8s 内部项目用的,直接运行它需要手动配置 GOPATH、理解代码生成参数,而且生成的代码风格与 k8s 内部一致(包含大量 pkg/generated/ 目录)。kubebuilder 将这套工具封装成了简单的 CLI 命令(make generate / make manifests),并提供了一套与 k8s 内部风格一致但更适合外部项目的项目骨架。对外部 Operator 开发来说,kubebuilder 大幅降低了 code-generator 的使用门槛。


Question 6:Reconciler 的 Reconcile 方法里,为什么要用 client-go 的 typed client 而不是直接用 controller-runtime 的 Client?

Answer: 两者各有优劣。controller-runtime 的 Client(通过 builder pattern 构建)是抽象层,可以在读写 Kubernetes 对象和 CR 时使用统一接口,并且内置了 cache(读走本地缓存,不用每次请求 apiserver)。typed client(由 client-gen 生成)则提供编译时类型检查,IDE 自动补全体验更好,API 语义更清晰。在实际业务 Operator 中,推荐读操作用 controller-runtime Client(利用 cache 加速),写操作用 typed client(获得更好的类型安全)。


Question 7:WorkQueue 的 Requeue 和 RequeueAfter 有什么区别?

Answer: Requeue 设为 true 时,对象会立即被放回队列末尾,下次 worker 空闲时立即重新处理,适用于确定性可恢复的错误。RequeueAfter 则指定一个时间间隔,对象会在指定时间后才重新入队,适用于"当前时机不合适、等待某个条件满足"的场景(如等待某个资源创建完成)。在 Reconciler 中,最常见的模式是用 AddRateLimited,它内部会根据限速器决策决定用哪种重试策略。


Question 8:HasSynced 返回 true 之后,本地缓存就一定是最新的吗?

Answer: 不是。HasSynced 返回 true 只表示"至少完成过一次全量同步",并不意味着此后一直与 apiserver 实时一致。SharedInformer 使用的是"最终一致性"模型——它通过 Watch 增量同步变更,但期间可能存在短暂的不一致窗口(通常在毫秒到秒级)。如果业务对实时性要求极高(如某些并发控制场景),需要在 Reconciler 中直接请求 apiserver 而不是依赖本地缓存。


Question 9:Leader Election 是怎么工作的?为什么 Operator 需要它?

Answer: Leader Election 是一种分布式协调机制,确保集群中同一时刻只有一个 Controller 实例在执行 Reconcile 逻辑。在 Kubernetes 中,它通过在 ConfigMap 或 Endpoint 上加锁实现——多个实例竞争创建同一个对象,只有创建成功的实例成为 Leader,持有锁期间执行工作;其他实例作为候选者等待 Leader 失联后重新竞争。对于多副本部署的 Operator(Deployment replicas > 1),必须启用 Leader Election,否则会导致多个实例同时 Reconcile 同一对象,引发竞态条件和重复操作。


Question 10:kubebuilder 项目里,+kubebuilder:subresource:status 标记的作用是什么?

Answer: 这个标记开启后,CRD 会自动生成 /status 子资源。开启后,kubectl apply 更新 spec 部分会走 /spec 子资源,更新 status 部分走 /status 子资源,两者互不影响。这符合 k8s 的设计原则——用户声明的期望状态(spec)和系统观测的实际状态(status)分离。Reconciler 可以直接更新 .status 字段而不触发 spec 的校验,同时用户对 spec 的修改也不会意外覆盖 status。


Question 11:controller-runtime 的 scheme.Builder 被废弃后,应该怎么改?

Answer: 旧写法是导入 sigs.k8s.io/controller-runtime/pkg/scheme 包,然后用 SchemeBuilder 注册类型。新写法改为使用 k8s.io/apimachinery/pkg/runtime.NewSchemeBuilder,通过一个函数参数注册类型。新写法减少了 API 包对 controller-runtime 的依赖,符合 k8s 核心代码的惯用模式。如果你使用的是 kubebuilder go/v4 版本(2026 年 4 月后发布的),脚手架模板已自动更新为新写法。


Question 12:operator-sdk 支持的 Ansible/Helm Operator 是什么?和 Go Operator 有什么本质区别?

Answer: Go Operator 的 Reconciler 逻辑是硬编码在 Go 程序里的,通过代码实现期望状态和实际状态的比对与调和。Ansible Operator 则用 Ansible Playbook 定义运维操作——你写一组 Playbook(描述"目标状态是什么"),Operator 运行时执行 Playbook 来达到目标状态。Helm Operator 则更进一步,直接监控 Helm Chart 的 values.yaml,当 values 变化时调用 helm upgrade。这三种方式的核心区别是"用什么语言描述运维逻辑":Go 是嵌入式 DSL,Ansible 是声明式运维语言,Helm 是 Chart 模板语言。Go 方式灵活性最高但开发门槛也最高。


Question 13:运行 make manifests 时报"spec.preserveUnknownFields: false"错误,怎么解决?

Answer: 这是 CRD schema 验证与 kubectl 兼容性之间的常见冲突。preserveUnknownFields 是 CRD 的一个弃用字段,k8s 1.16+ 推荐使用 x-kubernetes-preserve-unknown-fields(放在 schema 层级)。如果 controller-gen 生成的 CRD YAML 包含 preserveUnknownFields: false,而你使用的 k8s 版本已不支持该字段,就会报这个错误。解决方法:升级 controller-gen(controller-tools)到最新版本,新版本已默认移除该字段;或者在 API 类型的 +kubebuilder:validation:EmbeddedResource 标记下显式设置 schema。


Question 14:Controller 的 Worker 数量设多少合适?

Answer: 大多数 Operator 使用 1 到 5 个 Worker。Worker 数量的选择取决于两个因素:Reconcile 逻辑的耗时和并发安全性。如果每个 Reconcile 调用涉及大量 API 请求或复杂计算,Worker 数设得太高会导致 apiserver 压力过大;如果 Reconcile 逻辑本身是幂等的(推荐做法),可以适当提高 Worker 数。注意:即使 Worker 数为 1,Reconcile 方法也可能被并发调用(因为不同对象可能同时触发调和),所以所有 Reconciler 的核心逻辑必须对并发访问安全。


Question 15:如何在生产环境中调试 Controller 的 Reconcile 逻辑?

Answer: 有几个常用手段。第一,在 Reconciler 里加日志,用 klog/v2 或 zap,记录入参 Request 和处理结果。第二,用 controller-runtime 内置的 metrics 暴露(Prometheus 格式),观察 reconcile_latency_seconds 和 reconcile_total 等指标。第三,对于难以在生产环境直接调试的场景,可以写单元测试:kubebuilder 提供了 envtest 框架(基于 kubebuilder test setup),可以启动一个真实的 apiserver + etcd 轻量级集群来跑控制器测试,完全不需要连接真实集群。第四,用 kubectl get events --watch 查看 Operator Pod 产生的事件,结合 kubectl describe 查看资源状态。


Question 16:Operator 内存持续增长是什么原因?

Answer: Operator 内存泄漏最常见的原因有两个。第一,SharedInformer 的本地缓存持续增长——Informer 监听过多资源类型,或者 watch 范围过大(未加 namespace/label 过滤),会导致缓存无上限膨胀。解决方法:确保只 watch 你真正需要的资源类型,并通过 Informer 的 Namespace/Selector 参数过滤。第二,Reconciler 中创建的对象没有正确释放——如果每次 Reconcile 都 new 出来大量对象(如大的数组切片)但没有复用,而 GC 又跟不上节奏,就会导致堆内存持续增长。可以通过 pprof 工具(go tool pprof)抓取运行时的 heap profile 来定位具体分配来源。


Question 17:Operator 升级后 CRD 版本怎么处理?多版本 CRD 如何管理?

Answer: k8s 支持在 CRD 中声明多个 versions,通过 served 标记控制哪些版本对外服务。一个推荐的演进策略是:新增 API 版本时(如 v1alpha1 → v1beta1),先让新旧版本同时 served,Controller 同时支持两个版本;等所有在用资源都迁移到新版本后,将旧版本的 served 设为 false;最后删除旧版本的 CRD 定义。在 kubebuilder 中,用 +kubebuilder:storageversion 标记哪个版本是存储版本(写入 etcd 时使用的版本),这个版本只能有一个且不能轻易修改。


Question 18:Webhook 和普通 Reconcile 逻辑有什么区别?应该怎么选?

Answer: Webhook 在资源写入 etcd 之前拦截请求,分为 ValidatingWebhook(校验资源是否符合规范)和 MutatingWebhook(修改资源的值)。Reconcile 则在资源已经写入后,由 Informer 的 Watch 事件触发,主动将集群实际状态调和到期望状态。两者最大的区别是时序:Webhook 是同步的(请求在写入前必须等待),Reconcile 是异步的(事件驱动,可能有延迟)。选择建议:如果需要阻止不合规的资源写入(如强制字段必填、限制取值范围),用 ValidatingWebhook;如果需要在写入前修改默认值或注入字段,用 MutatingWebhook;如果需要持续维护某个关联资源的状态,用 Reconciler。


Question 19:kubebuilder 项目中的 PROJECT 文件是什么?它有什么作用?

Answer: PROJECT 文件是 kubebuilder v3+ 引入的元数据文件,记录了项目的配置信息,包括 domain、repo、group/version/kind 清单、CRD 当前使用的版本等。当执行 kubebuilder create api --version v2 添加新版本时,kubebuilder 会读取 PROJECT 文件中的历史信息,并追加新版本的记录。这个文件本质上是一个持久化的项目配置,使得 kubebuilder 能够跨命令维护 API 版本的完整历史,而不需要每次都传入所有参数。如果你手动修改了 PROJECT 文件,要确保语法正确,否则后续 kubebuilder 命令可能失败。


Question 20:Operator 的 Finalizer 应该怎么用?和 Reconciler 有什么关系?

Answer: Finalizer 是 Reconciler 实现"安全删除"的关键机制。当 Operator 在某个资源的 metadata.finalizers 字段中写入一个值(如 my-operator/finalizer)后,apiserver 在收到删除请求时不会立刻把资源从 etcd 中删除,而是将 deletionTimestamp 设为当前时间,然后等待 Operator 处理。Operator 的 Reconciler 看到 deletionTimestamp 不为空时,先执行清理逻辑(释放外部资源、删除关联数据),然后从 finalizers 中移除自己的 finalizer 记录;只有 finalizers 列表为空后,apiserver 才真正删除资源。在 kubebuilder 中,通过 +kubebuilder:finalizer:my-operator/finalizer 标记自动生成 finalizer 相关代码,开发者只需在 Reconcile 方法中处理 deletionTimestamp 字段即可。


相关阅读:
   • controller-runtime GitHub 仓库
   • kubebuilder 官方文档
   • operator-sdk 官方文档
   • k8s sample-controller 示例代码(k8s v1.36.1)
   • k8s code-generator(k8s v1.36.1)

Kubernetes 编程 / Operator 专题【左扬精讲】—— controller-runtime、kubebuilder、operator-sdk 三大框架深度对比 · 来源:k8s 源码 v1.36.1 + 社区最新资料