K8S的资源回收策略

1、ownreference:

一些 Kubernetes 对象是其它一些的 Owner。例如,一个 ReplicaSet 是一组 Pod 的 Owner。具有 Owner 的对象被称为是 Owner 的 Dependent。每个 Dependent 对象具有一个指向其所属对象的 metadata.ownerReferences 字段。当创建一个 ReplicaSet 时,Kubernetes 自动设置 ReplicaSet 中每个 Pod 的 ownerReference 字段值。

apiVersion: v1
kind: Pod
metadata:
  ...
  ownerReferences:
  - apiVersion: extensions/v1beta1
    controller: true
    blockOwnerDeletion: true
    kind: ReplicaSet
    name: my-repset
    uid: d9607e19-f88f-11e6-a518-42010a800195

2、resourceversion和generation

resourceVersion:当前对象的内部版本标识符,每个 Kubernetes 对象都有一个 resourceVersion 字段,代表该资源在下层数据库中 存储的版本。在每次写入时都会更改,并用于乐观并发控制。Kubernetes通过定义资源版本字段实现了乐观并发控制,资源版本(ResourceVersion)字段包含在Kubernetes对象的元数据(Metadata)中。这个字符串格式的字段标识了对象的内部版本号,其取值来自etcd的modifiedindex,且当对象被修改时,该字段将随之被修改。

generation:用于表示当前对象目标状态的代码,在某些对象中,作为影响对象spec的持久写入的一部分,服务器会增加生成。记录修改次数。对 spec 的任何更改都会增加 .metadata.generation 的值。

3、k8s垃圾回收

垃圾收集器在 Kubernetes 中的作用就是删除之前有所有者但是现在所有者已经不存在的对象。在 K8s 中,每个从属对象都具有 唯一数据字段名称 metadata.ownerReferences 用于确定关系。在级联删除(cascading deletion strategy)中,从属对象(dependent object)与所有者对象(owner object)会被一起删除。在级联删除中,又有两种模式: 前台(foreground)后台(background)。当删除对象时,可以指定是否该对象的 Dependent 也自动删除掉。自动删除 Dependent 也称为 级联删除。Kubernetes 中有两种 级联删除 的模式:background 模式和 foreground 模式。

在 background 级联删除 模式下,Kubernetes 会立即删除 Owner 对象,然后垃圾收集器会在后台删除这些 Dependent。

在 foreground 级联删除 模式下,根对象首先进入 “删除中” 状态。在 “删除中” 状态会有如下的情况:

  • 对象仍然可以通过 REST API 可见
  • 会设置对象的 deletionTimestamp 字段
  • 对象的 metadata.finalizers 字段包含了值 “foregroundDeletion”

一旦被设置为 “删除中” 状态,垃圾收集器会删除对象的所有 Dependent。垃圾收集器删除了所有 “Blocking” 的 Dependent(对象的 ownerReference.blockOwnerDeletion=true)之后,它会删除 Owner 对象。注意,在 “foreground 删除” 模式下,Dependent 只有通过 ownerReference.blockOwnerDeletion 才能阻止删除 Owner 对象。如果从对象的object有
“ownerReference.blockOwnerDeletion=true”属性,那么垃圾回收器必需等到此类从对象被删除完成以后,才可以删除根对象。否则垃圾回收器启动对从对象的删除后立即删除根对象。“ownerReference.blockOwnerDeletion=true”会延迟根对象的删除

孤儿(Orphan):这种情况下,对所有者的进行删除只会将其从集群中删除,并使所有对象处于“孤儿”状态。如果对象的 OwnerReferences 元数据中没有任何所有者对象,那么垃圾回收器会删除该对象。

在kubernetes1.9以前,对大部分控制器的删除,默认策略是"Orphan"(注意本段中说的默认值指REST API的默认行为,不是kubectl命令),包含ReplicationController, ReplicaSet, StatefulSet, DaemonSet, and Deployment,也就是在删除根对象时不删除从对象。并且当apiVersion是extensions/v1beta1, apps/v1beta1, and apps/v1beta2时,除非特别指定,默认删除策略仍然是"Orphan"。在kubernetes1.9版本中,所有类型的对象,在app/v1版本的apiVersion中,默认从对象删除。

4、finalizers终结器

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

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

metadata.deletionTimestamp 字段被设置时,负责监测该对象的各个控制器会通过轮询对该对象的更新请求来执行它们所要处理的所有 Finalizer。当所有 Finalizer 都被执行过,资源被删除。每执行完一个就从 finalizers 中移除一个,直到 finalizers 为空,之后其宿主资源才会被真正的删除。

k8s 资源的 metadata 里如果存在 finalizers,那么该资源一般是由某程序创建的,并且在其创建的资源的 metadata 里的 finalizers 加了一个它的标识,这意味着这个资源被删除时需要由创建资源的程序来做删除前的清理,清理完了它需要将标识从该资源的 finalizers 中移除,然后才会最终彻底删除资源。

5、k8s优雅删除策略

k8s 的 Pod 终止流程中还有一个"最多可以容忍的时间",即 grace period (在 pod 的 .spec.terminationGracePeriodSeconds 字段中定义),这个值默认是 30 秒,我们在执行 kubectl delete 的时候也可通过 --grace-period 参数显式指定一个优雅退出时间来覆盖 pod 中的配置。而当 grace period 超出之后,k8s 就只能选择 SIGKILL 强制干掉 Pod 了.

  • 用户删除 Pod

  • Pod 进入 Terminating 状态;

  • 与此同时,k8s 会将 Pod 从对应的 service 上摘除;

  • 与此同时,针对有 preStop hook 的容器,kubelet 会调用每个容器的 preStop hook,假如 preStop hook 的运行时间超出了 grace period,kubelet 会发送 SIGTERM 并再等 2 秒;

  • 与此同时,针对没有 preStop hook 的容器,kubelet 发送 SIGTERM

  • grace period 超出之后,kubelet 发送 SIGKILL 干掉尚未退出的容器

当用户请求删除含有pod的资源对象时(如RC、deployment等),K8S为了让应用程序优雅关闭(即让应用程序完成正在处理的请求后,再关闭软件),K8S提供两种信息通知:

1)、默认:K8S通知node执行docker stop命令,docker会先向容器中PID为1的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认超时时间(30s),会继续发送SIGKILL的系统信号强行kill掉进程。

2)、使用pod生命周期(利用PreStop回调函数),它执行在发送终止信号之前。

默认情况下,所有的删除操作的优雅退出时间都在30秒以内。kubectl delete命令支持–grace-period=的选项,以运行用户来修改默认值。0表示删除立即执行,并且立即从API中删除pod

grace和finalizer的组合删除策略:

  • API Server 检查 Finalizers 并结合是否需要进行 graceful 删除,来决定是否立即删除对象

  • 若对象需要进行 graceful 删除,更新 metadata.DeletionGracePeriodSecond 和 metadata.DeletionTimestamp 字段,不从存储中删除对象

  • 若对象不需要进行 Graceful 删除时

    • metadata.Finalizers 为空,直接删除
    • metadata.Finalizers 不为空,不删除,只更新 metadata.DeletionTimestamp

6、 服务端apply和managedFields

服务器端Apply允许kubectl之外的其他客户端执行Apply操作,并最终完全替代仅存在于kubectl中的复杂客户机端Apply逻辑。如果启用了服务器端Apply特性,PATCH 端点将接受额外的application/apply-patch+yaml 内容类型。服务器端Apply的用户可以将部分指定的对象发送到此端点。应用了的配置应该总是包含应用者关心的每个字段。服务器端应用是一个 beta 版特性,默认启用。 要关闭此特性门控, 你需要在启动 kube-apiserver 时包含参数 --feature-gates ServerSideApply=false。 如果你有多个 kube-apiserver 副本,他们都应该有相同的标记设置。

服务器端Apply使用了一种更声明性的方法,它跟踪用户的字段管理,而不是用户的最后应用状态。这意味着,作为使用服务器端Apply的副作用,关于字段管理者管理对象中的每个字段的信息也变得可用。对于管理字段的用户来说,在服务器端Apply理解中,意味着用户依赖并期望字段的值不会更改。最后断言字段值的用户将被记录为当前字段管理者。这可以通过使用**POSTPUT或non-apply PATCH**更改值,或者在发送到服务器端Apply端点的配置中包含该字段来实现。当使用服务器端应用,尝试着去改变一个被其他人管理的字段, 会导致请求被拒绝。服务器端应用通过跟踪系统的哪个参与者更改了对象的每个字段来工作。它将所有更新分散到对象,并记录所有已更改的字段以及操作的时间。所有这些信息都存储在对象元数据中的managedFields中。由于对象可以有许多字段,所以这个字段可能非常大。当有人应用时,我们可以使用存储在managedFields中的信息来报告相关冲突,并帮助合并算法执行正确的操作。

冲突是一种特定的错误状态, 发生在执行 Apply 改变一个字段,而恰巧该字段被其他用户声明过主权时。 这可以防止一个应用者不小心覆盖掉其他用户设置的值。 在冲突发生的时候,只有 apply 操作失败,而 update 则不会。 使用服务器端Apply实现的合并策略通常提供了更稳定的对象生命周期。服务器端Apply试图根据谁管理字段来合并字段,而不是仅仅根据值来否决字段。这样做的目的是通过减少意外干扰,使多个参与者更新同一个对象更容易、更稳定。当用户发送一个“完整描述的目标”对象到服务器端应用的服务端点, 服务器会将它和活动对象做一次合并,如果两者中有重复定义的值,那就以配置文件的为准。 如果配置文件中的项目集合不是此用户上一次操作项目的超集, 所有缺少的、没有其他应用者管理的项目会被删除。

apiVersion: v1
kind: ConfigMap
metadata:
  name: test-cm
  namespace: default
  labels:
    test-label: test
  managedFields:
  - manager: kubectl
    operation: Apply
    apiVersion: v1
    fields:
      f:metadata:
        f:labels:
          f:test-label: {
    
    }
  - manager: kube-controller-manager
    operation: Update
    apiVersion: v1
    time: '2019-03-30T16:00:00.000Z'
    fields:
      f:data:
        f:key: {
    
    }
data:
  key: new value

猜你喜欢

转载自blog.csdn.net/u014618114/article/details/112909617