垃圾收集有什么用

概述

k8s中,在删除deployment的时候,deployment从属的replicaset也会被删除,这背后就是垃圾收集器控制器在起作用。垃圾收集控制器中有资源对象的从属依赖关系,当某个资源对象被删除时,该资源对象的从属对象也会执行相应的删除策略。

从属和依赖

资源对象通过引入 metadata.ownerReferences 建立起了不同对象的依赖关系。一个对象可以依赖多个,只有当所有的owner都不存在,才通过请求API server来删除。

type ObjectMeta struct {
	...
	OwnerReferences []OwnerReference
}

type OwnerReference struct {
	APIVersion string
	Kind string
	Name string
	UID types.UID
}

Kubernetes API 资源对象的删除方式

Foreground删除策略

先删除附属对象,再删除属主对象

采用这种删除策略是,首先你的删除对象会进入处理中的状态,对于处于这个状态的对象,会发生一下事件:

  • API server会将这个对象中的metadata.deletionTimestamp设置上时间作为删除的标记。
  • API server还会将metadata.finalizers字段写入foregroundDeletion。
  • 这个对象会一直保持可见(可通过REST API访问),直到删除过程完成。

最后,待删除对象进入这个状态后,会删除所有该对象的从对象,删除完从对象之后,删除待删除对象。此时,这个对象在API server不可见。

OwnerReference.blockOwnerDeletion=true会阻止待删除对象的删除。此时如果要删除这个对象,那必须先删除带有这个字段的从对象,才能完成删除过程。

Background删除策略

先删除属主对象,再删除附属对象

在这个删除策略之下,API server 会立即删除这个对象,之后会在后台来清理其从对象。这个策略是kubernetes默认采用的策略。

Orphan删除策略

只是简单的删除对象,不删除其从对象,剩下的对象会成为“孤儿”。

通过编写Operator简单验证

验证方式

设计验证方式:通过crd定义一个garbage对象,然后garbage这个cr会创建运行nginx镜像的deployment,通过字段来分别控制是否添加ownreferences和finalizers,来达成验证目的。数据结构定义如下:

type Garbage struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   GarbageSpec   `json:"spec,omitempty"`
	Status GarbageStatus `json:"status,omitempty"`
}

type GarbageSpec struct {
	Nginx        *Nginx       `json:"nginx,omitempty"`
	//是否在deployment上添加ownerReference
	SetOwn       bool         `json:"setOwn,omitempty"`
	SetFinalizer SetFinalizer `json:"setFinalizer,omitempty"`
}

type Nginx struct {
	Replica *int32  `json:"replica,omitempty"`
	Image   *string `json:"image,omitempty"`
}

type SetFinalizer struct {
	//是否在garbage上添加finalizer
	Set  bool    `json:"set,omitempty"`
	//finalizer名称
	Name *string `json:"name,omitempty"`
}

验证OwnerReferences

1.添加ownerReferences字段

apiVersion: example.bebc.com/v1
kind: Garbage
metadata:
  labels:
    app.kubernetes.io/name: garbage
    app.kubernetes.io/instance: garbage-sample
    app.kubernetes.io/part-of: garbage-collection-example
    app.kuberentes.io/managed-by: kustomize
    app.kubernetes.io/created-by: garbage-collection-example
  name: garbage-sample
spec:
  nginx:
    replica: 1
    image: nginx
  setOwn: true

创建出来的deployment会有相应OwnerReferences字段

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: "2023-01-16T16:58:44Z"
  generation: 1
  name: garbage-sample-example
  namespace: default
  ownerReferences:
  - apiVersion: example.bebc.com/v1
    blockOwnerDeletion: true
    controller: true
    kind: Garbage
    name: garbage-sample
    uid: dd8a648b-19df-489c-aaf0-44958cb77a26
  resourceVersion: "1437553"
  uid: 4ff1bd6f-1427-47de-b480-97e640af2446

删除Garbage时,从属的deployment也会自动删除。

 kubectl delete -f example_v1_garbage.yaml 
[root@kind-master ~]# kubectl get deployments.apps garbage-sample-example
Error from server (NotFound): deployments.apps "garbage-sample-example" not found

2.不添加ownerReferences字段

apiVersion: example.bebc.com/v1
kind: Garbage
metadata:
  labels:
    app.kubernetes.io/name: garbage
    app.kubernetes.io/instance: garbage-sample
    app.kubernetes.io/part-of: garbage-collection-example
    app.kuberentes.io/managed-by: kustomize
    app.kubernetes.io/created-by: garbage-collection-example
  name: garbage-sample
spec:
  nginx:
    replica: 1
    image: nginx
  setOwn: false

创建出的deployment并不会添加OwnerReferencs字段

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: "2023-01-16T22:07:59Z"
  generation: 1
  name: garbage-sample-example
  namespace: default
  resourceVersion: "1461980"
  uid: 5b5c9574-6f82-4585-816c-e0885927994d
spec:

因此删除Garbage时,创建出的deployment不会自动清除。

验证Finalizers

添加finalizer字段:

apiVersion: example.bebc.com/v1
kind: Garbage
metadata:
  labels:
    app.kubernetes.io/name: garbage
    app.kubernetes.io/instance: garbage-sample
    app.kubernetes.io/part-of: garbage-collection-example
    app.kuberentes.io/managed-by: kustomize
    app.kubernetes.io/created-by: garbage-collection-example
  name: garbage-sample
spec:
  nginx:
    replica: 2
    image: nginx
  setOwn: true
  setFinalizer:
    set: true
    name: test

operator中控制finalizer的相应逻辑:

if garbage.DeletionTimestamp != nil {
	if r.hasFinalizer(garbage) {
		//删除finalizer依赖资源
		err := r.deleteExternalResources(garbage)
		if err != nil {
			return ctrl.Result{}, fmt.Errorf("delete external resourceerr %v", err)
		}
		//移除相应finalizers中的字段
		r.removeFinalizer(garbage)
		//更新garbage对象
		err = r.Update(ctx, garbage)
		if err != nil {
			return ctrl.Result{}, fmt.Errorf("remove finalizer and update garbage err %v", err)
		}
	}
	return ctrl.Result{}, nil
}
//等待10s
func (r *GarbageReconciler) deleteExternalResources(obj any) error {
	time.Sleep(10 * time.Second)
	return nil
}

删除garbage时,需要等待10秒。

[root@kind-node garbage-collection-example]# kubectl delete -f example_v1_garbage.yaml 
garbage.example.bebc.com "garbage-sample" deleted

项目地址:https://github.com/bebc/garbage-collector-example

总结

k8s通过garbagecollector和在资源对象中设置ownerReferences和finalizers来达到控制资源对象的级联删除目的。
在garbagecollector设计文档中:https://github.com/kubernetes/design-proposals-archive/blob/main/api-machinery/garbage-collection.md
提到了级联删除从客户端移动到了服务端。个人认为,这样做有以下几个好处:

  1. 各个控制器中不用关注删除操作逻辑,只需要关注相应字段就行。
  2. 由于删除逻辑不再分散在各个控制器中,因此对象的删除操作预期是可控的。

参考

https://kubernetes.io/zh-cn/docs/concepts/architecture/garbage-collection/
https://kubernetes.io/blog/2021/05/14/using-finalizers-to-control-deletion/
https://github.com/kubernetes/design-proposals-archive/blob/main/api-machinery/garbage-collection.md
https://xie.infoq.cn/article/6a6157ff3d85e2955ebbac994
https://book.kubebuilder.io/reference/using-finalizers.html
https://zhuanlan.zhihu.com/p/519773841
https://draveness.me/kubernetes-garbage-collector/

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐