Kubebuilder 控制器使用 Finalizers 实现资源删除前清理钩子:完整实战指南
开发者工具代码生成CLI云原生后端【免费下载链接】kubebuilderKubebuilder - SDK for building Kubernetes APIs using CRDs项目地址https://gitcode.com/gh_mirrors/ku/kubebuilder点击查看免费下载导读在 Kubebuilder 构建的 Kubernetes Operator 中Finalizers 是控制器实现异步预删除钩子pre-delete hooks的标准机制。当你的 API 类型每个对象都对应一个外部资源如存储桶、虚拟机、DNS 记录时如何在对象从 Kubernetes 集群中被删除的同时可靠地清理掉关联的外部资源本指南以 Kubebuilder 官方 CronJob 教程示例为核心讲解 finalizers 的工作原理、RBAC 权限配置、以及在Reconcile方法中注册与触发预删除钩子的完整代码范式读完即可在自己的 Operator 中落地删除前清理外部依赖这一经典能力。Finalizers 是什么控制器侧的预删除钩子Finalizers允许控制器实现异步的预删除钩子。假设你为 API 类型的每个对象创建了一个外部资源例如为每个 CronJob 对象创建一个存储桶并且希望在对象从 Kubernetes 中被删除时一并删除关联的外部资源——此时就应该使用 finalizer 来完成这个任务。它的核心价值在于将对象删除这件事从一次性动作变成了可拦截、可确认、可重试的多阶段流程控制器可以在对象真正从 etcd 中被清除之前执行必要的清理逻辑。核心机制delete 变成设置 deletionTimestamp 的 update理解 finalizers 的关键在于下面这一点一个 finalizer 会使对象上的delete操作变成一次用于设置删除时间戳deletionTimestamp的update操作。对象上出现deletionTimestamp字段即表示该对象正在被删除。反之如果没有 finalizers对象的删除在控制器眼中表现为缓存中找不到该对象的一次 reconcile——此时对象已经没了你也就失去了在它彻底消失前做清理的机会。用一句话概括二者差异有 finalizer删除请求到达 API Server 后对象并未立即消失而是被打上deletionTimestamp并继续存在于集群中等待所有 finalizer 被移除后才真正删除无 finalizer删除请求直接导致对象从 API Server 消失控制器下一次 reconcile 时Get会得到NotFound。设计要点注册与触发的两条路径在Reconcile方法中实现 finalizer 逻辑需要把握以下三条设计要点对象不在删除流程中、且尚未注册该 finalizer此时应当把 finalizer 添加到对象的metadata.finalizers列表中并调用Update将对象写回 Kubernetes完成注册动作对象正在被删除、且该 finalizer 仍存在于 finalizers 列表中此时说明预删除钩子尚未完成需要执行预删除逻辑随后将 finalizer 从列表中移除并Update对象向 API Server 表明清理已完成可以真正删除对象了确保预删除逻辑是幂等的idempotent因为删除流程可能因各种原因重试清理逻辑必须能够安全地被多次调用而不产生副作用。第一步RBAC 权限配置finalizers 子资源要更新对象的 finalizers控制器需要对资源的finalizers子资源拥有update权限。在 Kubebuilder 中通过 RBAC marker 声明在Reconcile方法上再由 controller-gen 生成对应的 ClusterRole 清单。以 CronJob 教程为例RBAC marker 如下见 finalizer_example.go// kubebuilder:rbac:groupsbatch.tutorial.kubebuilder.io,resourcescronjobs,verbsget;list;watch;create;update;patch;delete // kubebuilder:rbac:groupsbatch.tutorial.kubebuilder.io,resourcescronjobs/status,verbsget;update;patch // kubebuilder:rbac:groupsbatch.tutorial.kubebuilder.io,resourcescronjobs/finalizers,verbsupdate其中第三行cronjobs/finalizers,verbsupdate正是为 finalizer 操作授权。关于 RBAC marker 的详细说明可参考 RBAC 相关文档。运行make manifests后controller-gen 会将这些 marker 展开为config/rbac/role.yaml中的规则。可以在 CronJob 教程的 role.yaml 中看到生成的对应片段- apiGroups: - batch.tutorial.kubebuilder.io resources: - cronjobs/finalizers verbs: - update注意对finalizers子资源只需要update权限不需要create/delete。finalizer 的增删本质是对对象metadata.finalizers列表的更新因此update即可覆盖全部操作。第二步在 Reconcile 中实现完整的 finalizer 流程下面这段骨架代码来自 docs/book/src/cronjob-tutorial/testdata/finalizer_example.go它演示了在Reconcile方法中注册并触发预删除钩子的完整写法func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log : r.Log.WithValues(cronjob, req.NamespacedName) cronJob : batchv1.CronJob{} if err : r.Get(ctx, req.NamespacedName, cronJob); err ! nil { log.Error(err, unable to fetch CronJob) // well ignore not-found errors, since they cant be fixed by an immediate // requeue (well need to wait for a new notification), and we can get them // on deleted requests. return ctrl.Result{}, client.IgnoreNotFound(err) } // name of our custom finalizer myFinalizerName : batch.tutorial.kubebuilder.io/finalizer // examine DeletionTimestamp to determine if object is under deletion if cronJob.ObjectMeta.DeletionTimestamp.IsZero() { // The object is not being deleted, so if it does not have our finalizer, // then lets add the finalizer and update the object. This is equivalent // to registering our finalizer. if !controllerutil.ContainsFinalizer(cronJob, myFinalizerName) { controllerutil.AddFinalizer(cronJob, myFinalizerName) if err : r.Update(ctx, cronJob); err ! nil { return ctrl.Result{}, err } } } else { // The object is being deleted if controllerutil.ContainsFinalizer(cronJob, myFinalizerName) { // our finalizer is present, so lets handle any external dependency if err : r.deleteExternalResources(cronJob); err ! nil { // if fail to delete the external dependency here, return with error // so that it can be retried. return ctrl.Result{}, err } // remove our finalizer from the list and update it. controllerutil.RemoveFinalizer(cronJob, myFinalizerName) if err : r.Update(ctx, cronJob); err ! nil { return ctrl.Result{}, err } } // Stop reconciliation as the item is being deleted return ctrl.Result{}, nil } // Your reconcile logic return ctrl.Result{}, nil }逐步拆解这段代码获取对象用r.Get按NamespacedName拉取对象。对于NotFound错误用client.IgnoreNotFound(err)包装返回——对象已不存在时无需立即重试要等下一次变更通知且这种错误常出现在对象刚被删除时。注意当存在 finalizer 时删除流程中的对象仍然可以被Get到这正是我们能够拦截删除的原因。命名 finalizermyFinalizerName : batch.tutorial.kubebuilder.io/finalizer。finalizer 名称应当遵循 DNS 子域风格domain/name并尽量保证唯一。Kubebuilder 官方还建议当项目中存在多个控制器时使用带控制器名的唯一 finalizer例如{controller-name}.example.com/finalizer以避免不同控制器互相误操作参见 multiple-controllers.md。分支一对象未在删除中DeletionTimestamp.IsZero()为真。如果 finalizers 列表中还没有我们的 finalizer就调用controllerutil.AddFinalizer添加然后r.Update写回集群。这一步等价于注册 finalizer。分支二对象正在删除中DeletionTimestamp非零。此时如果我们的 finalizer 仍在列表中说明清理尚未完成先执行r.deleteExternalResources(cronJob)清理外部依赖若清理失败直接返回 error让控制器框架对该请求进行重试finalizer 尚未移除对象不会真正被删除重试是安全的清理成功后调用controllerutil.RemoveFinalizer移除 finalizer再r.Update写回最后return ctrl.Result{}, nil停止本次调和——对象正在被删除无需再执行常规 reconcile 逻辑。常规调和逻辑只有对象不在删除流程中时才走到代码末尾的// Your reconcile logic处。controllerutilcontroller-runtime 提供的 finalizer 辅助函数上述代码中的ContainsFinalizer、AddFinalizer、RemoveFinalizer均来自 controller-runtime 的sigs.k8s.io/controller-runtime/pkg/controller/controllerutil包。它们封装了对ObjectMeta.Finalizers切片的安全读写AddFinalizer(o, finalizer)当 finalizer 不在列表中时追加ContainsFinalizer(o, finalizer)判断 finalizer 是否已注册RemoveFinalizer(o, finalizer)将 finalizer 从列表中移除不存在时静默无操作。Kubebuilder 脚手架生成的控制器默认已引入该包可在 CronJob 教程的控制器实现 中看到这些依赖的实际用法。完整实现参考CronJob 教程控制器在 docs/book/src/cronjob-tutorial/testdata/project/internal/controller/cronjob_controller.go 中可以看到 finalizer 之外的完整控制器实现范式它同样携带cronjobs/finalizers,verbsupdate的 RBAC marker见该文件第 92 行并在调和主流程之外演示了 status conditions、owner reference、字段索引等最佳实践可以作为把 finalizer 融入真实控制器的参考实现。幂等性预删除逻辑的黄金准则原文档特别强调Ensure that the pre-delete logic is idempotent确保预删除逻辑是幂等的。这一点之所以关键是因为删除流程可能因网络错误、API Server 短暂不可用等原因被反复触发只要 finalizer 未移除对象就一直在删除流程中Reconcile会不断被调用如果清理逻辑不幂等例如重复调用外部 API 删除已不存在的存储桶直接报错就会导致 finalizer 永远无法移除对象被卡在删除状态最终造成删除泄漏。因此deleteExternalResources的注释也明确要求删除实现必须是幂等的且可安全地对同一对象多次调用func (r *Reconciler) deleteExternalResources(cronJob *batch.CronJob) error { // // delete any external resources associated with the cronJob // // Ensure that delete implementation is idempotent and safe to invoke // multiple times for same object. }一个务实的实现策略是在调用外部资源删除 API 前先判断资源是否已存在或对资源不存在的情况视为成功保证重试路径通畅。与其他删除/清理机制的边界在使用 finalizers 时建议同步理解它与 Kubernetes 垃圾回收Garbage Collection的关系Owner Reference / 垃圾回收负责级联删除集群内子资源如 Deployment 删除后级联删除其 Pod 与 ReplicaSet详见 getting-started 教程 中的相关说明Finalizers负责拦截删除流程执行集群外依赖的清理存储桶、云资源、外部系统条目等。两者相辅相成owner reference 解决集群内资源的清理finalizer 解决外部依赖的清理最终共同保证对象消失时一切相关资源都被妥善处理。小结在 Kubebuilder 项目中为控制器加入 finalizers 支持只需三步授权在Reconcile方法上声明// kubebuilder:rbac:groupsgroup,resourcesresource/finalizers,verbsupdate运行make manifests生成 RBAC注册DeletionTimestamp.IsZero()为真且未注册时用controllerutil.AddFinalizerr.Update注册触发与完成DeletionTimestamp非零且 finalizer 仍在时执行幂等的清理逻辑、controllerutil.RemoveFinalizerr.Update最后停止调和。掌握这个模式后你的 Operator 就能可靠地在自定义资源删除前完成外部依赖清理避免对象已删、外部资源残留的经典问题。赞分享开发者工具代码生成CLI云原生后端【免费下载链接】kubebuilderKubebuilder - SDK for building Kubernetes APIs using CRDs项目地址https://gitcode.com/gh_mirrors/ku/kubebuilder点击查看免费下载相关推荐Kubebuilder 控制器资源监听指南Primary/Secondary 资源、Owns/Watches 与 Predicates 完整实战Kubebuilder 控制器资源监听指南Primary/Secondary 资源、Owns/Watches 与 Predicates 完整实战 导读 本文围开发者工具代码生成CLI云原生后端Simple Live 完整指南把虎牙、斗鱼、Bilibili、抖音的直播聚合进一个跨平台应用Simple Live 完整指南把虎牙、斗鱼、Bilibili、抖音的直播聚合进一个跨平台应用 Simple Live 是一个跨平台直播聚合 App把虎牙、音视频直播pulumi-aws资源生命周期管理创建/更新/删除钩子实现pulumi aws资源生命周期管理创建/更新/删除钩子实现 在云资源管理中对资源生命周期的精细控制至关重要。无论是在资源创建前进行合规性检查更新时执行数上一篇Qhull计算几何算法终极指南从凸包到Voronoi图的完整教程 下一篇Python Web开发终极指南快速上手Django实战教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

AIRI 部署指南:5 分钟快速搭出会实时语音对话的 AI 伴侣

AIRI 部署指南:5 分钟快速搭出会实时语音对话的 AI 伴侣

AIRI 部署指南:5 分钟快速搭出会实时语音对话的 AI 伴侣 【免费下载链接】airi 💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama…

2026/9/25 2:30:09 阅读更多 →
Ariakit 实战:用 Dialog + Combobox 组合构建 Raycast 风格可搜索命令面板(Command Menu)

Ariakit 实战:用 Dialog + Combobox 组合构建 Raycast 风格可搜索命令面板(Command Menu)

UI组件前端 【免费下载链接】ariakit Toolkit with accessible components, styles, and examples for your next web app 项目地址: https://gitcode.com/gh_mirrors/ar/ariakit 点击查看 免费下载 本文基于 Ariakit 仓库中的官方示例 examples/dialog-combobox-c…

2026/9/25 2:30:08 阅读更多 →
Ruffle Flash 播放器指南:3 步在浏览器里跑起老 Flash 内容

Ruffle Flash 播放器指南:3 步在浏览器里跑起老 Flash 内容

Ruffle Flash 播放器指南:3 步在浏览器里跑起老 Flash 内容 【免费下载链接】ruffle A Flash Player emulator written in Rust 项目地址: https://gitcode.com/GitHub_Trending/ru/ruffle Ruffle 是一个用 Rust 写的 Flash Player 模拟器,它的浏…

2026/9/25 2:30:08 阅读更多 →

最新新闻

洗碗机水泵EMC整改:从电流路径失控到高集成方案落地

洗碗机水泵EMC整改:从电流路径失控到高集成方案落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 3:09:36 阅读更多 →
Eclipse Mosquitto 自建 Debian .deb 包实战:源码打包、参数定制与安装验证

Eclipse Mosquitto 自建 Debian .deb 包实战:源码打包、参数定制与安装验证

物联网消息队列后端网络/通信 【免费下载链接】mosquitto Eclipse Mosquitto - An open source MQTT broker 项目地址: https://gitcode.com/gh_mirrors/mo/mosquitto 点击查看 免费下载 对于需要在新特性(TLS、WebSockets、$CONTROL 等)尚未…

2026/9/25 3:09:36 阅读更多 →
BAML C NuGet 包冒烟测试全解析:精确版本恢复、RID 门禁与多形态部署验证

BAML C NuGet 包冒烟测试全解析:精确版本恢复、RID 门禁与多形态部署验证

编程语言AI Agent编译器CLI人工智能 【免费下载链接】baml The programming language for agents 项目地址: https://gitcode.com/gh_mirrors/ba/baml 点击查看 免费下载 baml-bridge 是 BAML(The programming language for agents)为 C# 生…

2026/9/25 3:09:36 阅读更多 →
Two.Collection 源码解析:two.js 中带事件传播的类数组容器

Two.Collection 源码解析:two.js 中带事件传播的类数组容器

图形学前端 【免费下载链接】two.js A renderer agnostic two-dimensional drawing api for the web 项目地址: https://gitcode.com/gh_mirrors/tw/two.js 点击查看 免费下载 Two.Collection 是 two.js 内置的类 Array 容器,它在原生数组行为之上加入了…

2026/9/25 3:09:36 阅读更多 →
Codex流式中断根因与TaoToken协议适配指南

Codex流式中断根因与TaoToken协议适配指南

1. 项目概述:这不是简单的URL替换,而是Codex服务链路的底层重定向改造Codex不是普通插件,它是把本地编辑器(VS Code、Cursor)变成AI原生开发环境的核心协议桥。当用户看到“stream disconnected”报错时,90…

2026/9/25 3:09:36 阅读更多 →
RTX 4060 8GB显存跑7B模型:llama.cpp调参实战与FlashAttention优化

RTX 4060 8GB显存跑7B模型:llama.cpp调参实战与FlashAttention优化

1. 为什么偏偏是 RTX 4060 跑 7B 模型这件事值得聊手里有张 RTX 4060,8GB 显存,笔记本端还是桌面端其实差别不小,但核心矛盾是一样的:想跑 7B 级别的模型,显存刚好卡在“能跑但跑不快”的尴尬位置。我前后折腾了差不多…

2026/9/25 3:08:36 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →