原文链接:https://zdyxry.github.io/2019/09/13/Kubernetes-%E5%AE%9E%E6%88%98-Operator-Finalizers/

Finalizers

Finalizers 允许 Operator 控制器实现异步的 pre-delete hook。比如你给 API 类型中的每个对象都创建了对应的外部资源,你希望在 k8s 删除对应资源时同时删除关联的外部资源,那么可以通过 Finalizers 来实现。

Finalizers 是由字符串组成的列表,当 Finalizers 字段存在时,相关资源不允许被强制删除。存在 Finalizers 字段的的资源对象接收的第一个删除请求设置 metadata.deletionTimestamp 字段的值, 但不删除具体资源,在该字段设置后, finalizer 列表中的对象只能被删除,不能做其他操作。

当 metadata.deletionTimestamp 字段非空时,controller watch 对象并执行对应 finalizers 的动作,当所有动作执行完后,需要清空 finalizers ,之后 k8s 会删除真正想要删除的资源。

Operator finalizers 使用

介绍了 Finalizers 概念,那么我们来看看在 Operator 中如何使用,在 Operator Controller 中,最重要的逻辑就是 Reconcile 方法,finalizers 也是在 Reconcile 中实现的。要注意的是,设置了 Finalizers 会导致 k8s 的 delete 动作转为设置 metadata.deletionTimestamp 字段,如果你通过 kubectl get 命令看到资源存在这个字段,则表示资源正在删除(deleting)。

有以下几点需要理解:

  1. 如果资源对象未被删除且未设置 finalizers,则添加 finalizer并更新 k8s 资源对象;
  2. 如果正在删除资源对象并且 finalizers 仍然存在于 finalizers 列表中,则执行 pre-delete hook并删除 finalizers ,更新资源对象;
  3. 由于以上两点,需要确保 pre-delete hook是幂等的。

kuberbuilder 示例

func (r *CronJobReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
ctx := context.Background()
log := r.Log.WithValues("cronjob", req.NamespacedName) var cronJob batch.CronJob
if err := r.Get(ctx, req.NamespacedName, &cronJob); err != nil {
log.Error(err, "unable to fetch CronJob")
return ctrl.Result{}, ignoreNotFound(err)
} // 声明 finalizer 字段,类型为字符串
myFinalizerName := "storage.finalizers.tutorial.kubebuilder.io" // 通过检查 DeletionTimestamp 字段是否为0 判断资源是否被删除
if cronJob.ObjectMeta.DeletionTimestamp.IsZero() {
// 如果为0 ,则资源未被删除,我们需要检测是否存在 finalizer,如果不存在,则添加,并更新到资源对象中
if !containsString(cronJob.ObjectMeta.Finalizers, myFinalizerName) {
cronJob.ObjectMeta.Finalizers = append(cronJob.ObjectMeta.Finalizers, myFinalizerName)
if err := r.Update(context.Background(), cronJob); err != nil {
return ctrl.Result{}, err
}
}
} else {
// 如果不为 0 ,则对象处于删除中
if containsString(cronJob.ObjectMeta.Finalizers, myFinalizerName) {
// 如果存在 finalizer 且与上述声明的 finalizer 匹配,那么执行对应 hook 逻辑
if err := r.deleteExternalResources(cronJob); err != nil {
// 如果删除失败,则直接返回对应 err,controller 会自动执行重试逻辑
return ctrl.Result{}, err
} // 如果对应 hook 执行成功,那么清空 finalizers, k8s 删除对应资源
cronJob.ObjectMeta.Finalizers = removeString(cronJob.ObjectMeta.Finalizers, myFinalizerName)
if err := r.Update(context.Background(), cronJob); err != nil {
return ctrl.Result{}, err
}
} return ctrl.Result{}, err
}
} func (r *Reconciler) deleteExternalResources(cronJob *batch.CronJob) error {
//
// 删除 crobJob关联的外部资源逻辑
//
// 需要确保实现是幂等的
} func containsString(slice []string, s string) bool {
for _, item := range slice {
if item == s {
return true
}
}
return false
} func removeString(slice []string, s string) (result []string) {
for _, item := range slice {
if item == s {
continue
}
result = append(result, item)
}
return
}

cluster-api-provider-vsphere 实现

看完了示例,我们来招一个具体项目看看,cluster-api-provider-vsphere 是 cluster-api 相关项目,用于提供 vsphere 相关资源创建的 Operator,采用 kubebuilder 来实现的。

vspheremachine_controller.go 中实现了 Reconcile 方法:

// Reconcile ensures the back-end state reflects the Kubernetes resource state intent.
func (r *VSphereMachineReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, reterr error) {
...
// Always close the context when exiting this function so we can persist any VSphereMachine changes.
defer func() {
if err := machineContext.Patch(); err != nil && reterr == nil {
reterr = err
}
}() // Handle deleted machines
if !vsphereMachine.ObjectMeta.DeletionTimestamp.IsZero() {
return r.reconcileDelete(machineContext)
} // Handle non-deleted machines
return r.reconcileNormal(machineContext)
}

在 Reconcile 中检测了 DeletionTimestamp 是否为0 ,如果不为0 ,则表示资源处于正在删除中,那么来看下 reconcileDelete 实现:

func (r *VSphereMachineReconciler) reconcileDelete(ctx *context.MachineContext) (reconcile.Result, error) {
ctx.Logger.Info("Handling deleted VSphereMachine") var vmService services.VirtualMachineService = &govmomi.VMService{} // 执行删除虚拟机逻辑
vm, err := vmService.DestroyVM(ctx)
if err != nil {
// 如果删除失败,则直接返回错误,controller 会自动重试
return reconcile.Result{}, errors.Wrapf(err, "failed to destroy VM")
} // 重新调度删除虚拟机逻辑,直到虚拟机状态处于 notfound 状态
if vm.State != infrav1.VirtualMachineStateNotFound {
ctx.Logger.V().Info("requeuing operation until vm state is reconciled", "expected-vm-state", infrav1.VirtualMachineStateNotFound, "actual-vm-state", vm.State)
return reconcile.Result{RequeueAfter: config.DefaultRequeue}, nil
} // pre-delete hook执行成功,也就是上面的删除虚拟机逻辑执行成功,则清空 Finalizers
ctx.VSphereMachine.Finalizers = clusterutilv1.Filter(ctx.VSphereMachine.Finalizers, infrav1.MachineFinalizer) return reconcile.Result{}, nil
}

可以看到整体逻辑与示例的使用是一致的,主要通过这种方式来达到 pre-delete hook 的效果。

k8s-initializer-finalizer-practice

在搜索相关资料的时候,看到有人在 SO 上问了如何使用的问题,其中有个回答中附上了一个练习项目,项目很小,很适合了解 Finalizers 概念。

相关逻辑如下:

}else{
customdeployment:=obj.(*crdv1alpha1.CustomDeployment).DeepCopy()
fmt.Println("Event..............................")
if customdeployment.DeletionTimestamp != nil{
// check if it has finalizer
if customdeployment.GetFinalizers()!=nil{
finalizers:=customdeployment.GetFinalizers() // check if first finalizer match with deletepod.crd.emruz.com
if finalizers[]=="deletepods.crd.emruz.com"{
//
_,err:=myutil.PatchCustomDeployment(c.clientset,customdeployment, func(deployment *crdv1alpha1.CustomDeployment) *crdv1alpha1.CustomDeployment {
// delete pods under this deployment
err:=myutil.DeletePods(c.kubeclient,c.podLabel)
if err!=nil{
fmt.Println("Failed to remove all pods. Reason: ",err)
return nil
}
// pods sucessfully removed. remove the finalizer
customdeployment.ObjectMeta=myutil.RemoveFinalizer(customdeployment.ObjectMeta)
return customdeployment
})
if err!=nil{
return err
}
}
}

Kubernetes 实战-Operator Finalizers 实现的更多相关文章

  1. kubernetes实战(二十六):kubeadm 安装 高可用 k8s v1.16.x dashboard 2.x

    1.基本配置 基本配置.内核升级.基本服务安装参考https://www.cnblogs.com/dukuan/p/10278637.html,或者参考<再也不踩坑的Kubernetes实战指南 ...

  2. kubernetes实战(二十八):Kubernetes一键式资源管理平台Ratel安装及使用

    1. Ratel是什么? Ratel是一个Kubernetes资源平台,基于管理Kubernetes的资源开发,可以管理Kubernetes的Deployment.DaemonSet.Stateful ...

  3. Kubernetes实战总结 - 自定义Prometheus

    一.概述 首先Prometheus整体监控结构略微复杂,一个个部署并不简单.另外监控Kubernetes就需要访问内部数据,必定需要进行认证.鉴权.准入控制, 那么这一整套下来将变得难上加难,而且还需 ...

  4. Kubernetes实战总结 - 阿里云ECS自建K8S集群

    一.概述 详情参考阿里云说明:https://help.aliyun.com/document_detail/98886.html?spm=a2c4g.11186623.6.1078.323b1c9b ...

  5. kubernetes实战篇之helm填坑与基本命令

    系列目录 其实前面安装部分我们已经分享一些互联网上其它网友分享的一些坑,本篇介绍helm的基本使用以及在使用过程中碰到的一些坑. 客户端版本和服务端版本不一致问题 有些朋友可能在使用helm init ...

  6. 新书推荐《再也不踩坑的Kubernetes实战指南》

      <再也不踩坑的Kubernetes实战指南>终于出版啦.目前可以在京东.天猫购买,京东自营和当当网预计一个星期左右上架. 本书贴合生产环境经验,解决在初次使用或者是构建集群中的痛点,帮 ...

  7. kubernetes实战(二十七):CentOS 8 二进制 高可用 安装 k8s 1.16.x

    1. 基本说明 本文章将演示CentOS 8二进制方式安装高可用k8s 1.16.x,相对于其他版本,二进制安装方式并无太大区别.CentOS 8相对于CentOS 7操作更加方便,比如一些服务的关闭 ...

  8. kubernetes实战(二十九):Kubernetes RBAC实现不同用户在不同Namespace的不同权限

    1.基本说明 在生产环境使用k8s以后,大部分应用都实现了高可用,不仅降低了维护成本,也简化了很多应用的部署成本,但是同时也带来了诸多问题.比如开发可能需要查看自己的应用状态.连接信息.日志.执行命令 ...

  9. kubernetes实战(三十):CentOS 8 二进制 高可用 安装 k8s 1.17.x

    1. 基本说明 本文章将演示CentOS 8二进制方式安装高可用k8s 1.17.x,相对于其他版本,二进制安装方式并无太大区别. 2. 基本环境配置 主机信息 192.168.1.19 k8s-ma ...

随机推荐

  1. 第37课 智能指针分析(指针特征操作符( -> 、 *)重载)

    1. 永恒的话题:内存泄漏 (1)动态申请堆空间,用完后不归还 (2)C++语言中没有垃圾回收的机制 (3)指针无法控制所指堆空间的生命周期------------指针是变量,可以指向内存堆空间,但是 ...

  2. ES数据库重建索引——Reindex(数据迁移)

    应用背景: 1.当你的数据量过大,而你的索引最初创建的分片数量不足,导致数据入库较慢的情况,此时需要扩大分片的数量,此时可以尝试使用Reindex. 2.当数据的mapping需要修改,但是大量的数据 ...

  3. API返回延迟,FPM重启后恢复之后又重现 问题解决方案

    背景 最近在提供后台API时,提供了一个简单逻辑的接口 部署在测试环境,自测没问题,提交测试 突然有一天,接口响应延迟严重,几乎每次都是3-4秒返回 这对于一个接口来说,肯定是有问题的 于是便有了以下 ...

  4. 面试(JS篇)

    1.js基本类型 Boolean,Number,String,Null,Undefined,Symbol. 2.null非对象,但是typeof null 返回的是Object,原因遗留下来的一个bu ...

  5. Python 字典(Dictionary) len()方法

    Python 字典(Dictionary) len()方法 描述 Python 字典(Dictionary) len() 函数计算字典元素个数,即键的总数.高佣联盟 www.cgewang.com 语 ...

  6. Blob分析之Board_Segmentation_Uncalib

    * ************************************************************************************************** ...

  7. 5.15 省选模拟赛 容斥 生成函数 dp

    LINK:5.15 T2 个人感觉生成函数更无脑 容斥也好推的样子. 容易想到每次放数和数字的集合无关 所以得到一个dp f[i][j]表示前i个数字 逆序对为j的方案数. 容易得到转移 使用前缀和优 ...

  8. [C#] (原创)一步一步教你自定义控件——01,TrackBar

    一.前言 技术没有先进落后之分,只有合不合适. WinForm有着非常多的优点,在使用WinForm久了之后,难免会觉得WinForm自带的某些控件外观上有些许朴素.或者功能上有些不如意,自然而然便想 ...

  9. 每日一道 LeetCode (5):最长公共前缀

    前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee: https://gitee.com ...

  10. vue中一些常见的面试题

    前言 一位正在学习前端的菜鸟,虽菜,但还未放弃. 内容 1,说一下vue中的指令 答: ①,v-html:主要用来渲染html节点,其作用与原生的innerHtml基本一致 ②,v-text:主要用来 ...