熟悉又陌生的 k8s 字段:finalizers

简介: metadata 中熟悉又陌生的字段 finalizers

前言

经常操作 Kubernetes 集群的同学肯定对 finalizers 字段不陌生,每当删除 namespace 或 pod 等一些 Kubernetes 资源时,有时资源状态会卡在 Terminating,很长时间无法删除,甚至有时增加 --force flag 之后还是无法正常删除。这时就需要 edit 该资源,将 finalizers 字段设置为 [],之后 Kubernetes 资源就正常删除了。

这是一个比较常见的操作,但是当有人问 finalizers 字段的作用是什么的时候,我是懵逼的,我甚至不知道这个熟悉又陌生的单词怎么读!那么这篇文章就来探索一下 finalizers 这个字段到底是做什么的,在实践中应该怎么应用这个字段。(另外,这个单词读作 ['faɪnəlaɪzər]

Finalizers

Finalizers 字段属于 Kubernetes GC 垃圾收集器,是一种删除拦截机制,能够让控制器实现异步的删除前(Pre-delete)回调。其存在于任何一个资源对象的 Meta 中,在 k8s 源码中声明为 []string,该 Slice 的内容为需要执行的拦截器名称。

对带有 Finalizer 的对象的第一个删除请求会为其 metadata.deletionTimestamp 设置一个值,但不会真的删除对象。一旦此值被设置,finalizers 列表中的值就只能被移除。

metadata.deletionTimestamp 字段被设置时,负责监测该对象的各个控制器会通过轮询对该对象的更新请求来执行它们所要处理的所有 Finalizer。 当所有 Finalizer 都被执行过,资源被删除。

metadata.deletionGracePeriodSeconds 的取值控制对更新的轮询周期。

每个控制器要负责将其 Finalizer 从列表中去除。

每执行完一个就从 finalizers 中移除一个,直到 finalizers 为空,之后其宿主资源才会被真正的删除。

// DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This
// field is set by the server when a graceful deletion is requested by the user, and is not
// directly settable by a client. The resource is expected to be deleted (no longer visible
// from resource lists, and not reachable by name) after the time in this field, once the
// finalizers list is empty. As long as the finalizers list contains items, deletion is blocked.
// Once the deletionTimestamp is set, this value may not be unset or be set further into the
// future, although it may be shortened or the resource may be deleted prior to this time.
// For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react
// by sending a graceful termination signal to the containers in the pod. After that 30 seconds,
// the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup,
// remove the pod from the API. In the presence of network partitions, this object may still
// exist after this timestamp, until an administrator or automated process can determine the
// resource is fully terminated.
// If not set, graceful deletion of the object has not been requested.
//
// Populated by the system when a graceful deletion is requested.
// Read-only.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
// +optional
DeletionTimestamp *Time `json:"deletionTimestamp,omitempty" protobuf:"bytes,9,opt,name=deletionTimestamp"`

// Number of seconds allowed for this object to gracefully terminate before
// it will be removed from the system. Only set when deletionTimestamp is also set.
// May only be shortened.
// Read-only.
// +optional
DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" protobuf:"varint,10,opt,name=deletionGracePeriodSeconds"`

// Must be empty before the object is deleted from the registry. Each entry
// is an identifier for the responsible component that will remove the entry
// from the list. If the deletionTimestamp of the object is non-nil, entries
// in this list can only be removed.
// Finalizers may be processed and removed in any order.  Order is NOT enforced
// because it introduces significant risk of stuck finalizers.
// finalizers is a shared field, any actor with permission can reorder it.
// If the finalizer list is processed in order, then this can lead to a situation
// in which the component responsible for the first finalizer in the list is
// waiting for a signal (field value, external system, or other) produced by a
// component responsible for a finalizer later in the list, resulting in a deadlock.
// Without enforced ordering finalizers are free to order amongst themselves and
// are not vulnerable to ordering changes in the list.
// +optional
// +patchStrategy=merge
Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`

在 Operator 中的应用

知道了 Finalizers 是什么,那么当然也要知道怎么用 Finalizers 了。在实际开发 Operator 时,删除前(Pre-delete)回调是一个比较常见的功能,用于处理一些在资源删除前需要处理的逻辑,如:关联资源释放、释放资源通知、相关数据清理,甚至是阻止资源删除。一般 Finalizers 的处理也是会 Reconcile 中实现的,下面就使用 chaosblade-operator 中的源码,简单介绍一些 Finalizers 的使用方式。

首先要了解的是 ChaosBlade-Operator 的工作原理:每个实验都会以 CR 的形式部署到 k8s 集群中,之后由 chaosblade-operator 来操作以 DaemonSet 形式部署 chaosblade-tool 对具体资源进行混沌实验。停止实验只需删除对应 CR 即可,在删除 CR 时,首先会执行一遍实验恢复逻辑,之后才会将 CR 删除。但如果恢复实验失败,则会将 CR 的 Phase 设置为 Destroying,而在 Reconcile 中观测到 Phase 状态为 Destroying 或者 metadata.deletionTimestamp 不为空时,就会不会移除 finalizers 中的拦截器名称,阻止该 CR 被删除。

这样设计的目的是为了在实验恢复失败后,让用户去主动查看实验恢复失败原因,如果是一些意外原因导致的实验恢复失败,及时去处理。在确认原因后,可使用 CLI 工具增加 --force-remove 进去强制删除,项目维护者在 Issue#368 中也就这个设计给出了解答。

// pkg/controller/chaosblade/controller.go 部分源码
...
const chaosbladeFinalizer = "finalizer.chaosblade.io"
...
func (r *ReconcileChaosBlade) Reconcile(request reconcile.Request) (reconcile.Result, error) {
    reqLogger := logrus.WithField("Request.Name", request.Name)
    forget := reconcile.Result{}
    // Fetch the RC instance
    cb := &v1alpha1.ChaosBlade{}
    err := r.client.Get(context.TODO(), request.NamespacedName, cb)
    if err != nil {
      return forget, nil
    }
    if len(cb.Spec.Experiments) == 0 {
      return forget, nil
    }

    // Destroyed->delete
    // Remove the Finalizer if the CR object status is destroyed to delete it
    if cb.Status.Phase == v1alpha1.ClusterPhaseDestroyed {
      cb.SetFinalizers(remove(cb.GetFinalizers(), chaosbladeFinalizer))
      err := r.client.Update(context.TODO(), cb)
      if err != nil {
        reqLogger.WithError(err).Errorln("remove chaosblade finalizer failed at destroyed phase")
      }
      return forget, nil
    }
    if cb.Status.Phase == v1alpha1.ClusterPhaseDestroying || cb.GetDeletionTimestamp() != nil {
      err := r.finalizeChaosBlade(reqLogger, cb)
      if err != nil {
        reqLogger.WithError(err).Errorln("finalize chaosblade failed at destroying phase")
      }
      return forget, nil
    }
    ...
    return forget, nil
}

如果 Phase 状态为 Destroyed,则从 Finalizers 中移除 finalizer.chaosblade.io,之后正常删除 CR。

结语

在实际工作中,像 Finalizers 这样的东西太多了,很多平时挂在嘴边的东西,深究起来我们可能对其并不了解,甚至原本的理解就是错误的。在今后的文章中,除了各种实践干货,笔者还会将更多的精力投注到基本原理、底层实现、源码剖析中,更聚焦于技术本身,在不重复造轮子的基础上,学习和了解更多产品背后的代码设计和实现原理。最后在分享一句弗兰西斯·培根的话:

“人生如同道路。最近的捷径通常是最坏的路。”

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
11月前
|
存储 Kubernetes Linux
【探索 Kubernetes|作业管理篇 系列 8】探究 Pod 的 API 对象属性级别与重要字段用法
【探索 Kubernetes|作业管理篇 系列 8】探究 Pod 的 API 对象属性级别与重要字段用法
71 1
|
存储 Kubernetes 安全
Kubernetes 中的对象是如何删除的:Finalizers 字段介绍
Kubernetes 中的对象删除并不像表面上看起来那么简单,删除对象涉及一系列过程,例如对象的级联和非级联删除,在删除之前检查以确定是否可以安全删除对象等等。这些都是通过称为 `Finalizers`(终结器)的 API 对象实现的。
1042 0
Kubernetes 中的对象是如何删除的:Finalizers 字段介绍
|
Kubernetes 容器 Perl
kubernetes:字段选择器(field-selector)标签选择器(labels-selector)和筛选 Kubernetes 资源
kubernetes:字段选择器(field-selector)标签选择器(labels-selector)和筛选 Kubernetes 资源
1182 0
kubernetes:字段选择器(field-selector)标签选择器(labels-selector)和筛选 Kubernetes 资源
|
Kubernetes 安全 Linux
熟悉又陌生的 k8s 字段:SecurityContext
pod 和 containers 中熟悉又陌生的字段 SecurityContext
4533 0
|
21小时前
|
存储 Kubernetes 监控
Kubernetes 集群的持续性能优化实践
【5月更文挑战第26天】 在动态且复杂的微服务架构中,确保 Kubernetes 集群的高性能和稳定性是至关重要的。本文将探讨一系列实用的策略和工具,用于监控、分析和优化 Kubernetes 集群的性能。通过深入理解资源分配、调度策略以及网络和存储配置的影响,我们能够揭示提升集群效率的关键步骤。文章将结合真实案例,展示如何通过细致的调优过程,实现服务的持续性能提升。
|
1天前
|
存储 Kubernetes 调度
Kubernetes 集群的持续性能优化策略
【5月更文挑战第25天】 随着容器化技术的普及,越来越多的企业采用 Kubernetes 作为其服务部署和运维的标准平台。然而,随着集群规模的增长和应用复杂性的上升,性能问题逐渐浮现,成为系统管理员关注的焦点。本文将探讨在 Kubernetes 环境中进行持续性能优化的实践方法,旨在为读者提供一系列实用的调优技巧,帮助其提升集群的稳定性与效率。通过深入分析资源分配、调度优化、网络效率以及存储管理等方面的调优手段,我们将展示如何构建一个高效、可扩展的 Kubernetes 集群。
|
2天前
|
Prometheus 监控 Kubernetes
Kubernetes 集群的监控与日志管理实践
【5月更文挑战第25天】在现代微服务架构中,容器编排工具如Kubernetes已成为部署、管理和扩展应用程序的关键。随着其广泛应用,对集群的监控和日志管理的需求也日益增长。本文将探讨如何利用Prometheus和Fluentd等开源工具实现对Kubernetes集群的有效监控和日志收集,旨在为运维工程师提供一套可行的解决方案,以保障集群的稳定性和提高故障排查效率。
|
2天前
|
运维 监控 Kubernetes
Kubernetes 集群的监控与维护最佳实践
【5月更文挑战第25天】 在现代微服务架构中,容器编排平台如Kubernetes已成为不可或缺的组成部分。随着其广泛应用,对集群进行有效的监控和维护变得至关重要。本文将探讨针对Kubernetes集群监控的最佳工具选择、常见问题的诊断方法以及预防性维护措施。通过深入分析Prometheus和Grafana在性能监控中的应用,以及介绍如何使用ELK栈进行日志管理,文章旨在为运维专家提供一系列实用的策略和步骤,以确保集群的健康和优化性能。
|
3天前
|
Prometheus 运维 Kubernetes
Kubernetes 集群的监控与日志管理最佳实践
【5月更文挑战第23天】 在容器化和微服务架构日益普及的当下,Kubernetes 已成为众多企业的首选平台。随之而来的是对集群性能、资源利用和运行状况的持续监控需求,以及日志管理的重要性。本文将探讨在 Kubernetes 环境中实现有效监控和日志管理的策略,涵盖关键组件的选择、配置优化及故障排查流程,旨在为运维工程师提供一套综合解决方案,确保集群的稳定性和高可用性。
|
4天前
|
存储 运维 Kubernetes
Kubernetes 集群的持续性能优化策略
【5月更文挑战第23天】 在动态且日益复杂的云计算环境中,维护 Kubernetes 集群的性能是一个不断进化的挑战。本文深入探讨了一系列实用的技术手段和最佳实践,旨在帮助运维专家持续优化其 Kubernetes 环境。通过分析关键组件、资源调度策略、网络效率以及存储优化等方面,我们提出了一套综合性能提升方案。这些策略不仅有助于提高系统的响应能力和资源利用率,还能确保长期的稳定运行。