K8s GC设计原则
Warning:设计文档的对应的 k8s 版本为1.7
Q: What is GC of Kuernetes ?
GC 是 Garbage Collector 的简称。从功能层面上来说,它和编程语言当中的「GC」 基本上是一样的。它清理 Kubernetes 中「符合特定条件」的 Resource Object。(在 k8s 中,你可以认为万物皆资源,很多逻辑的操作对象都是 Resource Object。)
Q: What are dependent mechanisms to clear needless resource objects?
Kubernetes 在不同的 Resource Objects 中维护一定的「从属关系」。内置的 Resource Objects 一般会默认在一个 Resource Object 和它的创建者之间建立一个「从属关系」。
当然,你也可以利用 ObjectMeta.OwnerReferences 自由的去给两个 Resource Object 建立关系,前提是被建立关系的两个对象必须在一个 Namespace 下。
1 // OwnerReference contains enough information to let you identify an owning 2 // object. Currently, an owning object must be in the same namespace, so there 3 // is no namespace field. 4 type OwnerReference struct { 5 // API version of the referent. 6 APIVersion string `json:"apiVersion" protobuf:"bytes,5,opt,name=apiVersion"` 7 // Kind of the referent. 8 // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds 9 Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"` 10 // Name of the referent. 11 // More info: http://kubernetes.io/docs/user-guide/identifiers#names 12 Name string `json:"name" protobuf:"bytes,3,opt,name=name"` 13 // UID of the referent. 14 // More info: http://kubernetes.io/docs/user-guide/identifiers#uids 15 UID types.UID `json:"uid" protobuf:"bytes,4,opt,name=uid,casttype=k8s.io/apimachinery/pkg/types.UID"` 16 // If true, this reference points to the managing controller. 17 // +optional 18 Controller *bool `json:"controller,omitempty" protobuf:"varint,6,opt,name=controller"` 19 // If true, AND if the owner has the "foregroundDeletion" finalizer, then 20 // the owner cannot be deleted from the key-value store until this 21 // reference is removed. 22 // Defaults to false. 23 // To set this field, a user needs "delete" permission of the owner, 24 // otherwise 422 (Unprocessable Entity) will be returned. 25 // +optional 26 BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"` 27 }
OwnerReference
一般存在于某一个 Resource Object 信息中的 metadata 部分。
OwnerReference 中的字段可以唯一的确定 k8s 中的一个 Resource Object。两个 Object 可以通过这种方式建立一个 owner-dependent 的关系。
K8s 实现了一种「Cascading deletion」(级联删除)的机制,它利用已经建立的「从属关系」进行资源对象的清理工作。例如,当一个 dependent 资源的 owner 已经被删除或者不存在的时候,从某种角度就可以判定,这个 dependent 的对象已经是异常(无人管辖)的了,需要进行清理。而 「cascading deletion」则是被 k8s 中的一个 controller 组件实现的: Garbage Collector
所以,k8s 是通过 Garbage Collector 和 ownerReference 一起配合实现了「垃圾回收」的功能。
Q: What is the relationship like ?(owner-dependent)
我们可以通过一个实际的例子来了解这个「从属关系」:
1 apiVersion: extensions/v1beta1 2 kind: ReplicaSet 3 metadata: 4 annotations: 5 deployment.kubernetes.io/desired-replicas: "2" 6 deployment.kubernetes.io/max-replicas: "3" 7 deployment.kubernetes.io/revision: "1" 8 creationTimestamp: 2018-09-07T07:11:52Z 9 generation: 1 10 labels: 11 app: coffee 12 pod-template-hash: "3866135192" 13 name: coffee-7dbb5795f6 14 namespace: default 15 ownerReferences: 16 - apiVersion: apps/v1 17 blockOwnerDeletion: true 18 controller: true 19 kind: Deployment 20 name: coffee 21 uid: 4b807ee6-b26d-11e8-b891-fa163eebca40 22 resourceVersion: "476159" 23 selfLink: /apis/extensions/v1beta1/namespaces/default/replicasets/coffee-7dbb5795f6 24 uid: 4b81e76c-b26d-11e8-b891-fa163eebca40 25 spec: 26 replicas: 2 27 ....
上面截取了一个 ReplicaSet Object 中的 metadata 的部分信息。
我们可以注意到,它的 ownerReferences 字段标识了一个 Deployment Object。我们都清楚的是,ReplicaSet 会创建一系列的 Pod。通过 spec.replicas:2 可以知道,他会创建两个pod。
1 root@xr-service-mesh-lab:~/istio-1.0.2# kubectl get pods | grep coffee 2 coffee-7dbb5795f6-6crxz 1/1 Running 0 9d 3 coffee-7dbb5795f6-hv7tr 1/1 Running 0 5d 4 root@xr-service-mesh-lab:~/istio-1.0.2#
让我们来观察其中一个 Pod:
1 apiVersion: v1 2 kind: Pod 3 metadata: 4 annotations: 5 cni.projectcalico.org/podIP: 192.168.0.14/32 6 creationTimestamp: 2018-09-07T07:11:52Z 7 generateName: coffee-7dbb5795f6- 8 labels: 9 app: coffee 10 pod-template-hash: "3866135192" 11 name: coffee-7dbb5795f6-6crxz 12 namespace: default 13 ownerReferences: 14 - apiVersion: apps/v1 15 blockOwnerDeletion: true 16 controller: true 17 kind: ReplicaSet 18 name: coffee-7dbb5795f6 19 uid: 4b81e76c-b26d-11e8-b891-fa163eebca40 20 resourceVersion: "76727" 21 selfLink: /api/v1/namespaces/default/pods/coffee-7dbb5795f6-6crxz 22 uid: 4b863e4d-b26d-11e8-b891-fa163eebca40
我们可以看出,pod 中的 ownerReferences 所标识的 Object 正式我们上面看到过的 ReplicaSet。最后让我们来检查一下 ReplicaSet 所对应的 Deployment 的情况:
1 apiVersion: extensions/v1beta1 2 kind: Deployment 3 metadata: 4 annotations: 5 deployment.kubernetes.io/revision: "1" 6 creationTimestamp: 2018-09-07T07:11:52Z 7 generation: 1 8 labels: 9 app: coffee 10 name: coffee 11 namespace: default 12 resourceVersion: "476161" 13 selfLink: /apis/extensions/v1beta1/namespaces/default/deployments/coffee 14 uid: 4b807ee6-b26d-11e8-b891-fa163eebca40
对比一下 ReplicaSet Object 中 ownerReference 标识的 Object 可知,这个 Deployment 是 ReplicaSet 的 owner。至此,我们通过观察三个 Object 中的 ownerReference 的信息,可以建立起如下的「从属关系」:
- Deployment(owner)—> ReplicaSet (dependent)
- ReplicaSet (owner) —> Pod (dependent)
Q: What is the working mechanism of Garbage Collector?
一个 Garbage Collector 通常由三部分实现:
- Scanner: 它负责收集目前系统中已存在的 Resource,并且周期性的将这些资源对象放入一个队列中,等待处理(检测是否要对某一个Resource Object 进行 GC 操作)
- Garbage Processor: Garbage Processor 由两部分组成
- Dirty Queue: Scanner 会将周期性扫描到的 Resource Object 放入这个队列中等待处理
- Worker:worker 负责从这个队列中取出元素进行处理
- 检查 Object 的 metaData 部分,查看 ownerReference 字段是否为空
- 如果为空,则本次处理结束
- 如果不为空,检测 ownerReference 字段内标识的 Owner Resource Object是否存在
- 存在:则本次处理结束
- 不存在:删除这个 Object
其实,在有了 Scanner 和 Garbage Processor 之后,Garbage Collector 就已经能够实现「垃圾回收」的功能了。但是有一个明显的问题:Scanner 的扫描频率设置多少好呢?太长了,k8s 内部就会积累过多的「废弃资源」;太短了,尤其是在集群内部资源对象较多的时候,频繁的拉取信息对 API-Server 也是一个不小的压力。
k8s 作为一个分布式的服务编排系统,其内部执行任何一项逻辑或者行为,都依赖一种机制:「事件驱动」。说的简单点,k8s 中一些看起来「自动」的行为,其实都是由一些神秘的「力量」在驱动着。而这个「力量」就是我们所说的「Event」。任意一个 Resource Object 发生变动的时候(新建,更新,删除),都会触发一个 k8s 的事件(Event),这个事件在 k8s 的内部是公开的,也就是说,我们可以在任意一个地方监听这些事件。
总的来说,无论是「事件的监听机制」还是「周期性访问 API-Server 批量获取 Resource Object 信息」,其目的都是为了能够掌握 Resource Object 的最新信息。两者是各有优势的:
- 批量拉取:一次性拉取所有的 Resource Object,全面
- 监听 Resource 的 Event:实时性强, 且对 API—SERVER 不会造成太大的压力
综上所述,在实现 Garbage Collector 的过程中,k8s 向其添加了一个「增强型」的组件:Propagator
- Propagator: Propagator 由三个部分构成
- EventQueue:负责存储 k8s 中资源对象的事件(Eg:ADD,UPDATE,DELETE)
- DAG(有向无环图):负责存储 k8s 中所有资源对象的「owner-dependent」 关系
- Worker:从 EventQueue 中,取出资源对象的事件,根据事件的类型会采取以下两种操作
- ADD/UPDATE: 将该事件对应的资源对象加入 DAG,且如果该对象有 owner 且 owner 不在 DAG 中,将它同时加入 Garbage Processor 的 Dirty Queue 中
- DELETE:将该事件对应的资源对象从 DAG 中删除,并且将其「管辖」的对象(只向下寻找一级,如删除 Deployment,那么只操作 ReplicaSet )加入 Garbage Processor 的 Dirty Queue 中
在有了 Propagator 的加入之后,我们完全可以仅在 GC 开始运行的时候,让 Scanner 扫描一下系统中所有的 Object,然后将这些信息传递给 Propagator 和 Dirty Queue。只要 DAG 一建立起来之后,那么 Scanner 其实就没有再工作的必要了。「事件驱动」的机制提供了一种增量的方式让 GC 来监控 k8s 集群内部的资源对象变化情况。
Q: How can I delete the owner and reserve dependents ?
没错,需求就是这么奇怪,k8s 还兼容一种情况:删除 owner,留下 dependents。剩余的 dependents 被称为是「orphan」
你想怎么实现?
如果暂时先不看设计文档中关于这部分的内容,根据之前对 k8s GC 的了解,让你来实现这个功能,你会怎么做呢?这里给出一下笔者的想法:
首先,我们先来根据上面对于 GC 的了解,给出一幅大致架构图:
在上图中,我用三种颜色分别标记了三条较为重要的处理过程:
- 红色:worker 从 dirtyQueue 中取出资源对象,检查其是否带有 owner ,如果没带,则不处理。否则检测其 owner是否存在,存在,则处理下一个资源对象,不存在,删除这个 object。
- 绿色: scanner 从 api-server 中扫描存在于 k8s 集群中的资源对象并加入至 dirtyQueue
- 粉色:propagator.worker 从 eventQueue 中取出相应的事件并且获得对应的资源对象,根据事件的类型以及相应资源对象所属 owner 对象的情况来进行判定,是否要进行两个操作:
- 从 DAG 中删除相应节点(多为响应 DELETE 事件的逻辑)
- 将有级联关系但是 owner 不存在的对象送入 diryQueue 中
其中红色是「数据处理」过程,而绿色和粉色是「数据收集」的过程。在「数据处理」的过程中(即我们上面分析过的 GC 的 Worker 的工作过程),worker 做的较为重要的工作有两步:
- 检查资源对象信息的「ownerReference」字段,判断其是否处在一个级联关系中
- 若资源对象有所属 owner 且不存在,则删除这个对象
此时,回头看下我们的需求:「owner 删除,dependents 留下」。如果想在「数据处理」这条链路上做些修改达到我们目的的话,唯一可行的办法就是:在删除了 dependents 对应的 owner 对象之后,同时删除 dependents 信息中 「ownerReference」字段和对应的值。这样一来,在检测资源对象是否应该被删除的过程就会因为其没有「ownerReference」字段而放过它,最终实现了 dependents 对象的“孤立”。
k8s 是怎么实现的?
如果你了解 gRPC-intercepter 的工作机制,那么会加快你理解下面的内容
k8s 在系统内部实现了一种类似「删除拦截器链」的机制:即在删除某个资源对象的「删除链路」上,执行一个或多个「拦截逻辑」。并且这种「拦截逻辑」可以自主实现,然后像插件一样注入到这个删除链路上。这种机制在 k8s 当中统称为: Finalizers 。
Finalizers 的声明非常简单,就是一个 []string 。这个 Slice 的内部填充的是要执行拦截器的名称。它存在于任何一个资源对象的 Meta 信息中: apimachinery/types.go at master · kubernetes/apimachinery · GitHub。
Finalizers
中的拦截器在其宿主资源对象触发删除操作之后顺序执行(资源对象的deletionTimestamp不为 nil),每执行完一个,就会从 Finalizers 中移除一个,直到 Finalizers 为空的 Slice,其宿主资源对象才可以被真正的删除。
于「删除 owner 但是不删除 dependents」 的需求,k8s 则是实现了一个:orphan finalizer。一般情况下,正常利用 GC 级连删除一个资源对象是不会涉及到 orphan finalizer 的。它执行的是我们之前提到的 GC 的工作逻辑。如果你想启用这个特性,就需要在删除资源对象的时候,根据 K8s 版本的不同,将名为 DeleteOption.OrphanDependents 的参数赋值为 True(1.7版本以前)
或者将 DeleteOption.PropagationPolicy 参数赋值为 metav1.DeletePropagationOrphan :apimachinery/types.go at 9dc1de72c0f3996657ffc88895f89f3844d8cf01 · kubernetes/apimachinery · GitHub。
通过这个参数的注释也可以看出:如果设置好之后,将会在它的 Finalizers 中加入 orphan finalizer。而加入 orphan finalizer 这部分的逻辑是在 api-server 的 package 中:apiserver/store.go at master · kubernetes/apiserver · GitHub
加入了 orphan finalizer 之后,在 GC 的 worker 从 dirtyQueue 中取出 owner 资源对象进行处理的时候,就会执行它的逻辑:删除 dependents 的 OwnerReference 部分: kubernetes/garbagecollector.go at 0972ce1accf859b73abb5a68c0adf4174245d4bf · kubernetes/kubernetes · GitHub。最终,在「保留 dependents」 的逻辑完成之后,orphan finalizer 也会从相应资源对象的 Finalizers 中删除。
一个隐含的 Race 问题
对于 Controller 来说,它会周期性的通过 Selector 来寻找它所创建的资源。
如果在筛选到了符合自己 label 的 资源,但是发现它的 Meta.OwnerReference 字段中没有自己相关的信息的时候,就会执行一个Adoption 的操作,也就是将和自己有关的 OwnerReference 信息注入到这个 Pod 的 Meta 部分。这种逻辑虽然看起来是比较「保险」,但是实际上它和 orphan finalizer 的逻辑是有冲突的。前者是对 dependents 增加 OwnerReference 信息, 后者则是删除它。两个逻辑在执行的时候,如果不保证「互斥」的话,很可能就会出现一个很严重的竞争问题:指定了 orphan finalizer 的 对象,其 dependents 最终也会被删除。
借鉴操作系统对于「竞争」问题的处理方式,对 OwnerReference操作的的逻辑(即临界区),应该被「互斥」机制保护起来。而在 k8s 中,实现这种互斥保护机制的方式也很简单:Controller 在想执行 Adoption 操作之前,会检查一下当前资源对象的meta.DeletionTimestamp。如果这个字段的值为非 nil,那么就证明这个资源对象已经在被删除中了。所以就不会再继续执行 Adoption 操作。
但是仔细想一下,这种「互斥」保护机制的实现方式,看起来是借助了一个「锁变量」(meta.DeletionTimestamp)的帮助。不过,我们并不需要担心这个字段的「竞争」问题,因为能修改它的操作,只有「删除」操作,而删除操作是肯定会发生在 orphan finalizer 执行之前的。也就是说,当 orphan finalizer 执行的时候,这个值早就被设置进去了。 kubernetes/replica_set.go at 7f23a743e8c23ac6489340bbb34fa6f1d392db9d · kubernetes/kubernetes · GitHub
Q: How Kubernetes defines delete operation of resource object?
K8s 在对资源对象「删除」操作的定义上,思考了一个较为重要的问题:「删除」操作真正完成的标志是什么?(达到什么样的条件才可以通知用户「删除」操作成功)。这个问题出现的源头是在用户侧,当用户在使用 k8s 提供的资源对象的「删除」操作时,有个问题会影响到他们:
- 「删除」操作成功多久后才可以在同一个 ns 下创建同名资源对象?
如果你了解过构建一个 k8s 集群所需要的服务组件,就可以很清楚的知道:k8s 中的资源对象的信息都是存于一个key-value 的数据库当中的(etcd),且是以名字来做索引的。
通过命令行 kubectl get xxx 查询的资源对象的信息都来自于那。而且,当 kubelet 组件删除掉其所在节点上的一些资源的时候,会调用 API-Server 提供的接口删除掉key-value 数据库中相应的记录。所以,在 k8s 中,给「删除」操作下了这样一个定义:
在没有 orphanFinalizer 参与的前提下,直到被删除对象及其「管辖」对象的信息在 key-value 数据库中都被清除,才认为该对象真正的被 GC 回收。即达到了返回给用户「删除成功」的标准。
本质上来说,上述所表示的删除操作是「同步」的。因为有「级联关系」(owner-dependent) 关系的存在,删除一个资源对象往往影响的不是他自己,还有他的 dependents。只有将因它出现的所有资源都删除,才可以认为这个对象被删除了。
若想指定这种同步的删除模式,需要在两个不同的位置设置两个参数:
- dependents 对象 meta 信息中 OwnerReference.BlockOwnerDeletion
- 在发送删除对象请求时,设置 DeleteOptions.PropagationPolicy
OwnerReference.BlockOwnerDeletion
参数大多数情况下在相应的 dependents 对象创建的时候就设置进去了。
如果想在 dependents 对象创建之后更新这个参数的值,可能需要使用 admission controller (1.7及以上版本)提供的一些权限相关的功能。
DeleteOptions.PropagationPolicy
一共有3个候选值:
1 // DeletionPropagation decides if a deletion will propagate to the dependents of 2 // the object, and how the garbage collector will handle the propagation. 3 type DeletionPropagation string 4 5 const ( 6 // Orphans the dependents. 7 DeletePropagationOrphan DeletionPropagation = "Orphan" 8 // Deletes the object from the key-value store, the garbage collector will 9 // delete the dependents in the background. 10 DeletePropagationBackground DeletionPropagation = "Background" 11 // The object exists in the key-value store until the garbage collector 12 // deletes all the dependents whose ownerReference.blockOwnerDeletion=true 13 // from the key-value store. API sever will put the "foregroundDeletion" 14 // finalizer on the object, and sets its deletionTimestamp. This policy is 15 // cascading, i.e., the dependents will be deleted with Foreground. 16 DeletePropagationForeground DeletionPropagation = "Foreground" 17 )
再结合 OwnerReference.BlockOwnerDeletion 参数的注释
1 // If true, AND if the owner has the "foregroundDeletion" finalizer, then 2 // the owner cannot be deleted from the key-value store until this 3 // reference is removed. 4 // Defaults to false. 5 // To set this field, a user needs "delete" permission of the owner, 6 // otherwise 422 (Unprocessable Entity) will be returned. 7 // +optional 8 BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"`
我们可以了解到。同步删除的开启方式如下:
- DeleteOptions.PropagationPolicy = DeletePropagationForeground
- OwnerReference. BlockOwnerDeletion = True
开启之后,在删除身份为 owner 的资源对象的时候,就会先将 denpendents 对象中 OwnerReference.BlockOwnerDeletion 为 true 的资源对象先删除,然后再删除 owner 身份的对象。这里的「删除」就指的是我们前面说过的「真正的删除」:从 k8s 存储资源对象信息的 key-value 数据库中删除所有与其相关的信息。需要注意的是, OwnerReference.BlockOwnerDeletion 为 false 的dependent 对象不会阻碍 owner 对象的删除操作。
Foreground 是 k8s 提供的两种级联删除方式其中之一,另外一种为 Background。通过上面相关的注释可以看到 Foreground 级联删除也是通过 Finalizer 来实现的,查看 Finalizer 相关的定义可知,标准的 Finalizer,一个是 orphan 的,另一个就是Foreground的:
1 // These are internal finalizer values for Kubernetes-like APIs, must be qualified name unless defined here 2 const ( 3 FinalizerOrphanDependents string = "orphan" 4 FinalizerDeleteDependents string = "foregroundDeletion" 5 )
API-Server 的 Delete 函数,在接受到删除请求的时候,会检查 DeleteOptions.PropagationPolicy 参数,若其值为 DeletePropagationForeground , API-Server 随即会对该资源对象进行 Update 操作:
- 插入 FinalizerDeleteDependents Finalizer
- 设置 ObjectMeta.DeletionTimestamp 为当前值
然后,在 GC 处理 owner 对象的 Update 事件的逻辑中,还会给 owner 对象打上一个「正在删除 dependents」 对象的标签。之后,我们会将 owner 对象管辖的 dependent 对象和他自己都加入到 dirtyQueue。dirtyQueue 的 worker 在处理 owner 对象的时候,会检查 owner 对象 「正在删除 dependents」的标签是否存在,如果仍有 dependent 对象没有被删掉,owner 会被轮询处理。而 dependent 对象将会被正常删除。当 dependent 对象相应的删除事件被 Propagator 感知到后,会将其从 DAG 和其 owner 的 dependents 信息中删除。几个循环之后,dependents 机会被删光,而 owner 对象中的 finalizer 和自身也会随之被删掉。
Background 模式的级联删除不会因 dependent 对象而影响 owner 对象的删除操作。当我们发送给 API-Server 删除一个 owner 身份的对象的请求之后,这个资源对象会立即被删除。它「管辖」的 dependent 对象会以「静默」的方式删除。
Q: What problems GC handles in k8s?
通过对 k8s GC 设计文档的阅读,可以大致的概括一下:GC 主要是按照用户的需求来清理系统中「异常」的资源,用户可以自定义「清理方式」和「清理策略」。不难发现,在 GC 中,到底是保留一个资源还是删除一个资源都参照了资源之间的「从属关系」。资源的「从属关系」可以大致分为几个形态:
- 无从属关系:这部分资源基本不会被 GC 做处理
- 有从属关系
- 不符合用户预期:删除异常资源
- 符合用户预期:解绑异常资源之间的级联关系
在有从属关系的资源之间,即使被探测到关系异常,也并不代表一定要将他们都清除。如果有 Orphan Finalizer 的存在,可能某种「异常」正是用户想要的。所以,这就回到了我们一开始所说到的「清理策略」问题。GC 有一定的默认的清理策略,但是用户可以通过加入 Finalizer 的形式来修改「清理策略」,从而保持一个「符合用户期望」的资源之间的从属关系。
同时,用户可以还可以通过参数来制定特定的「清理方式」,如 Foreground 或者 Background。总体上来说,GC 的行为会受到如下几个因素的影响:
- 默认:
- 依据:资源之间默认的从属关系
- 行为:删除级联关系异常的资源
- 方式:Foreground 或者 Background
- 可定制
- 依据:用户定义的从属关系(通过 Finalizer)
- 行为:删除级联关系异常的资源
- 方式:Foreground 或者 Background
目前看来,GC 主要是解决了「资源清理」 的问题。那么再抽象一点来看的话,GC 解决的是「资源管理」这个大问题中的一个关于「清理」的小问题。既然说到「资源管理」,那么肯定就不止「清理」一个问题需要处理:
其中,对于资源的创建部分,除了正常的新建操作之外,controller 还有定期执行一个「Adoption」 的操作,用来维护其创建的资源之间那些「本应该建立但是却断开的从属关系」。而对于更新操作来说,controller_manager 需要处理用户对于资源的扩缩容请求,如将 deployment.replicaset.replicacount 减少或者增大,相应资源对应的 controller 需要对可见资源的数量进行调整。至于「资源超卖」的问题,一定会涉及到 scheduler。因为物理资源是固定的,「超卖」本质上来说就是按照实时的需求,动态的调整服务所在的 Node,以便恰好满足服务对于资源的需求。
如果不把资源管理问题讨论的范围局限在 k8s 中的话,那么「审计」和「复用」同样也是「资源管理」问题中不得不考虑的两个点。前者可以增加整个系统资源的「可控性」,后者则可以最大限度的提升资源的利用率,从而降低成本。其实「复用」和「超卖」的目的是一样的,都是想最大限度的利用物理资源。不过这两个功能笔者暂时还没有去查看它们是否在 k8s 已经实现。
总结
之所以去了解 k8s 的 GC,是因为在将 k8s 集群从1.7版本升级至1.9版本的过程中,因为我错误的设置了资源之间的从属关系,导致该资源被 GC 给回收掉了。问题在1.7版本没有出现的原因是那时 k8s 还没有支持对自定义资源(CRD)的级联删除。通过对 GC 设计理念的了解,我们可以初步的感受到 k8s 对于「资源管理」这个问题域中「资源清理」这个小问题的解决思路。以此为起点,我们可以顺藤摸瓜,去观察 k8s 对于「资源管理」问题域中的其他难题是如何处理的。后续,我也将会根据设计文档中的思路,在代码级别上去了解 GC 的实现细节,从而贡献出更加详细的 blog。