Finalizer 详细说一下是干啥的,是 Operator 中的机制吗
一、先给核心结论
- Finalizer 不是 Operator 独有的机制,是 Kubernetes APIServer 原生支持的通用机制。
所有资源(Pod、Deployment、CR、ConfigMap……)都可以加 Finalizer,不只自定义 CR。
- Operator 只是大量使用 Finalizer 解决自定义资源删除前的清理难题。
二、Finalizer 是什么?
Finalizer 是资源对象
metadata.finalizers 里的字符串数组,作用:阻止资源被真正删除,给控制器一段窗口期执行 “删除前清理逻辑”
工作规则(APIServer 内置逻辑):
- 用户执行删除操作(
kubectl delete); - APIServer 不会立刻清除数据,先把资源
deletionTimestamp设置为当前时间,资源进入 Terminating 状态; - 只要 finalizers 列表不为空,APIServer 拒绝彻底移除该资源;
- 控制器感知到资源进入 Terminating,执行自定义清理逻辑;
- 清理全部完成后,控制器必须把自己的 finalizer 从数组中移除,更新资源;
- finalizers 数组为空 → APIServer 才会真正删除这条资源。
一句话通俗理解:
Finalizer = 删除拦截器;有 Finalizer,资源删不掉,等控制器做完打扫工作,手动放行。
三、原生资源自带 Finalizer 例子(证明不是 Operator 专属)
- PV/PVC 绑定场景:PV 有
kubernetes.io/pv-protection - Node 节点:
kubernetes.io/node-protection目的:防止资源还在被使用时被直接删除,造成数据丢失。
四、为什么 Operator 格外依赖 Finalizer?
原生 Deployment/StatefulSet 依靠 OwnerReference 垃圾回收:父资源删除,APIServer 自动删子资源。
但 Operator 经常要清理集群外部资源:
对象存储 Bucket、云数据库、负载均衡、外部域名、备份任务……
👉 OwnerReference 只能管理集群内 API 资源,管不了集群外部资源!
场景举例:
你写 MySQL Operator,CR 被删除时:
- 需要先销毁云厂商 RDS 实例
- 需要清理外部备份存储
- 需要释放外网 SLB
这些集群外资源 APIServer 完全不认识,垃圾回收机制无能为力。
流程:
- CR 创建时,Reconcile 检测不存在自定义 finalizer,就追加
finalizers: ["mysql.example.com/finalizer"] - 用户删除 CR → CR 进入 Terminating
- Operator 收到事件,进入清理分支:销毁外部资源
- 外部资源清理成功 → 移除 finalizer
- APIServer 看到 finalizers 为空,彻底删除 CR
⚠️ 致命坑(面试高频)
清理逻辑执行失败、网络报错、代码异常,忘记移除 finalizer → CR 永远卡在 Terminating,删不掉!
五、Finalizer 完整标准代码流程(controller-runtime 标准范式)
-
正常调谐阶段(CR 新建 / 更新)判断:CR 不包含自定义 finalizer && CR 没有 deletionTimestamp→ 添加 finalizer,更新 CR。
-
检测到
deletionTimestamp != nil(资源正在被删除)- 执行外部资源清理逻辑
- 清理成功:移除 finalizer,更新 CR
- 清理失败:返回报错,等待下一轮重试,不要移除 finalizer
-
一旦 finalizer 被移除,后续无法再次拦截删除。
六、Finalizer VS OwnerReference(面试常对比)
表格
| 机制 | 作用范围 | 执行方 | 适用场景 |
|---|---|---|---|
| OwnerReference | 集群内 K8s 资源 | APIServer 垃圾回收控制器 | Pod、Service、ConfigMap 等集群内附属资源自动清理 |
| Finalizer | 集群内资源拦截,可联动外部资源 | 你的 Operator 控制器 | 需要清理外部资源、复杂有序清理流程 |
工程最佳实践:集群内子资源使用 OwnerReference 自动回收;外部资源、复杂业务清理逻辑使用 Finalizer。
七、面试高频追问汇总
追问 1:多个 Finalizer 执行顺序有保证吗?
没有!APIServer 不保证 finalizer 顺序,并发执行风险由开发者自己处理。
一般 Operator 只定义一个自定义 Finalizer,规避顺序问题。
追问 2:我手动删除 finalizer 会发生什么?
APIServer 直接删除 CR,Operator 来不及执行清理 → 外部资源残留,产生僵尸资源。生产严禁手动清 finalizer。
追问 3:Finalizer 会无限重试吗?
会。清理逻辑报错,不删除 finalizer,每次 Reconcile 都会尝试清理,直到成功或者人工干预。
不要手动在 CR 的 YAML 里写 ownerReferences!绝对不要人工写。
ownerReferences 是控制器在创建子资源(Deployment/Service/Pod)的时候,由代码主动打上的,不是写 CR yaml 加在自己身上。
我们分层讲明白:
OwnerReference是干什么的
1. OwnerReference 作用链路
CR = 父资源
Deployment、Service、ConfigMap = 子资源
Operator 在 Reconcile 创建 Deployment 时,代码里给 Deployment 设置:
go
运行
ownerReferences := []metav1.OwnerReference{
*metav1.NewControllerRef(cr, schema.GroupVersionKind{...}),
}
deploy.OwnerReferences = ownerReferences
效果:
当 CR 被删除,K8s 垃圾回收控制器自动清理所有带这个属主引用的 Deployment、Service。
属主引用是打在子资源上,不是打在父 CR 身上!
2. 那 CR 自己能不能加 ownerReferences?
语法上允许,但几乎没人这么用。CR 一般是顶层资源,很少有父资源。
3. 结合你的场景:无状态 CR,管理 Deployment
场景:CR(MyApp)→ 托管 Deployment
- 在代码创建 Deployment 时,给 Deployment 设置 OwnerReference
- 用户删除 CR
- APIServer 发现父 CR 删除,自动回收 Deployment、关联 Pod
✅ 这种场景,不需要 Finalizer!
限制:
这套机制只生效于集群内 K8s 资源。
如果有外部资源(云 SLB、外部数据库),单纯依靠 OwnerReference 不够,依旧需要 Finalizer。
4. 区分两条路线(面试高频)
路线 A:只管理集群内部资源(Deployment/Service/ConfigMap)
- 创建子资源时代码注入 OwnerReference
- 不需要 Finalizer
- 删除 CR:子资源自动回收
路线 B:存在外部资源需要清理
- 子集群资源依旧用 OwnerReference
- 额外增加 Finalizer,拦截 CR 删除,先清理外部资源
- 外部清理完成后移除 finalizer,CR 才被真正删除
5. 澄清你刚才的疑问
“无状态 cr 在编写 yaml 文件时要加上 ownerReferences 字段?”❌ 错误做法:手动编辑 CR yaml 添加 ownerReferences✅ 正确做法:Operator 代码创建 Deployment 的时候,给 Deployment 打上 ownerReferences
6. 附带一个高频面试追问
问:我忘记给 Deployment 设置 OwnerReference,会发生什么?
答:删除 CR 之后,Deployment 和 Pod 保留在集群里,变成 “孤儿资源”,不会自动清理,需要手动删除。
总结就是
- OwnerReference:建立父子关联 → APIServer 自动回收子资源,不需要业务代码写删除逻辑
- Finalizer:拦截 CR 删除事件 → 留给控制器执行自定义清理逻辑(尤其是外部资源)

浙公网安备 33010602011771号