Ref: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/garbage-collection.md#orphaning-the-descendants-with-orphan-finalizer Warning:设计文档的对应的 k8s 版本为1.7

Q: What is GC of Kuernetes ?

A:

GC 是 Garbage Collector 的简称。从功能层面上来说,它和编程语言当中的「GC」 基本上是一样的。它清理 Kubernetes 中「符合特定条件」的 Resource Object。(在 k8s 中,你可以认为万物皆资源,很多逻辑的操作对象都是 Resource Object。)

Q: What are dependent mechanisms to clear needless resource objects ?

A:

Kubernetes 在不同的 Resource Objects 中维护一定的「从属关系」。内置的 Resource Objects 一般会默认在一个 Resource Object 和它的创建者之间建立一个「从属关系」。当然,你也可以利用ObjectMeta.OwnerReferences自由的去给两个 Resource Object 建立关系,前提是被建立关系的两个对象必须在一个 Namespace 下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// OwnerReference contains enough information to let you identify an owning
// object. Currently, an owning object must be in the same namespace, so there
// is no namespace field.
type OwnerReference struct {
	// API version of the referent.
	APIVersion string `json:"apiVersion" protobuf:"bytes,5,opt,name=apiVersion"`
	// Kind of the referent.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds
	Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"`
	// Name of the referent.
	// More info: http://kubernetes.io/docs/user-guide/identifiers#names
	Name string `json:"name" protobuf:"bytes,3,opt,name=name"`
	// UID of the referent.
	// More info: http://kubernetes.io/docs/user-guide/identifiers#uids
	UID types.UID `json:"uid" protobuf:"bytes,4,opt,name=uid,casttype=k8s.io/apimachinery/pkg/types.UID"`
	// If true, this reference points to the managing controller.
	// +optional
	Controller *bool `json:"controller,omitempty" protobuf:"varint,6,opt,name=controller"`
	// If true, AND if the owner has the "foregroundDeletion" finalizer, then
	// the owner cannot be deleted from the key-value store until this
	// reference is removed.
	// Defaults to false.
	// To set this field, a user needs "delete" permission of the owner,
	// otherwise 422 (Unprocessable Entity) will be returned.
	// +optional
	BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"`
}

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 CollectorownerReference 一起配合实现了「垃圾回收」的功能。

Q: What is the relationship like ?(owner-dependent)

A:

我们可以通过一个实际的例子来了解这个「从属关系」:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
  annotations:
    deployment.kubernetes.io/desired-replicas: "2"
    deployment.kubernetes.io/max-replicas: "3"
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: 2018-09-07T07:11:52Z
  generation: 1
  labels:
    app: coffee
    pod-template-hash: "3866135192"
  name: coffee-7dbb5795f6
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: Deployment
    name: coffee
    uid: 4b807ee6-b26d-11e8-b891-fa163eebca40
  resourceVersion: "476159"
  selfLink: /apis/extensions/v1beta1/namespaces/default/replicasets/coffee-7dbb5795f6
  uid: 4b81e76c-b26d-11e8-b891-fa163eebca40
spec:
  replicas: 2
....

上面截取了一个 ReplicaSet Object 中的 metadata 的部分信息。我们可以注意到,它的 ownerReferences字段标识了一个 Deployment Object。我们都清楚的是,ReplicaSet 会创建一系列的 Pod。通过spec.replicas:2可以知道,他会创建两个pod。

1
2
3
4
root@xr-service-mesh-lab:~/istio-1.0.2# kubectl get pods  | grep coffee
coffee-7dbb5795f6-6crxz          1/1       Running   0          9d
coffee-7dbb5795f6-hv7tr          1/1       Running   0          5d
root@xr-service-mesh-lab:~/istio-1.0.2#

让我们来观察其中一个 Pod:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
  annotations:
    cni.projectcalico.org/podIP: 192.168.0.14/32
  creationTimestamp: 2018-09-07T07:11:52Z
  generateName: coffee-7dbb5795f6-
  labels:
    app: coffee
    pod-template-hash: "3866135192"
  name: coffee-7dbb5795f6-6crxz
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: coffee-7dbb5795f6
    uid: 4b81e76c-b26d-11e8-b891-fa163eebca40
  resourceVersion: "76727"
  selfLink: /api/v1/namespaces/default/pods/coffee-7dbb5795f6-6crxz
  uid: 4b863e4d-b26d-11e8-b891-fa163eebca40

我们可以看出,pod 中的ownerReferences所标识的 Object 正式我们上面看到过的 ReplicaSet。最后让我们来检查一下 ReplicaSet 所对应的 Deployment 的情况:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: 2018-09-07T07:11:52Z
  generation: 1
  labels:
    app: coffee
  name: coffee
  namespace: default
  resourceVersion: "476161"
  selfLink: /apis/extensions/v1beta1/namespaces/default/deployments/coffee
  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?

A:

一个 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 的最新信息。两者是各有优势的:

  1. 批量拉取:一次性拉取所有的 Resource Object,全面
  2. 监听 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 ?

A:

╮(╯▽╰)╭没错,需求就是这么奇怪,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 当中统称为: FinalizersFinalizers的声明非常简单,就是一个[]string。这个 Slice 的内部填充的是要执行拦截器的名称。它存在于任何一个资源对象的 Meta 信息中: apimachinery/types.go at master · kubernetes/apimachinery · GitHubFinalizers中的拦截器在其宿主资源对象触发删除操作之后顺序执行(资源对象的deletionTimestamp不为 nil),每执行完一个,就会从Finalizers中移除一个,直到Finalizers为空的 Slice,其宿主资源对象才可以被真正的删除。

对于「删除 owner 但是不删除 dependents」 的需求,k8s 则是实现了一个:orphan finalizer。一般情况下,正常利用 GC 级连删除一个资源对象是不会涉及到 orphan finalizer 的。它执行的是我们之前提到的 GC 的工作逻辑。如果你想启用这个特性,就需要在删除资源对象的时候,根据 K8s 版本的不同,将名为DeleteOption.OrphanDependents的参数赋值为 True(1.7版本以前), 或者将DeleteOption.PropagationPolicy参数赋值为 metav1.DeletePropagationOrphanapimachinery/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?

A:

K8s 在对资源对象「删除」操作的定义上,思考了一个较为重要的问题:「删除」操作真正完成的标志是什么?(达到什么样的条件才可以通知用户「删除」操作成功)。这个问题出现的源头是在用户侧,当用户在使用 k8s 提供的资源对象的「删除」操作时,有个问题会影响到他们:

  1. 「删除」操作成功多久后才可以在同一个 ns 下创建同名资源对象?

如果你了解过构建一个 k8s 集群所需要的服务组件,就可以很清楚的知道:k8s 中的资源对象的信息都是存于一个key-value 的数据库当中的(etcd),且是以名字来做索引的。通过命令行kubectl get xxx查询的资源对象的信息都来自于那。而且,当kubelet组件删除掉其所在节点上的一些资源的时候,会调用 API-Server 提供的接口删除掉key-value 数据库中相应的记录。所以,在 k8s 中,给「删除」操作下了这样一个定义:

在没有 orphanFinalizer 参与的前提下,直到被删除对象及其「管辖」对象的信息在 key-value 数据库中都被清除,才认为该对象真正的被 GC 回收。即达到了返回给用户「删除成功」的标准。

本质上来说,上述所表示的删除操作是「同步」的。因为有「级联关系」(owner-dependent) 关系的存在,删除一个资源对象往往影响的不是他自己,还有他的 dependents。只有将因它出现的所有资源都删除,才可以认为这个对象被删除了。

若想指定这种同步的删除模式,需要在两个不同的位置设置两个参数:

  1. dependents 对象 meta 信息中 OwnerReference.BlockOwnerDeletion
  2. 在发送删除对象请求时,设置 DeleteOptions.PropagationPolicy

OwnerReference.BlockOwnerDeletion参数大多数情况下在相应的 dependents 对象创建的时候就设置进去了。如果想在 dependents 对象创建之后更新这个参数的值,可能需要使用admission controller (1.7及以上版本)提供的一些权限相关的功能。

DeleteOptions.PropagationPolicy一共有3个候选值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// DeletionPropagation decides if a deletion will propagate to the dependents of
// the object, and how the garbage collector will handle the propagation.
type DeletionPropagation string

const (
	// Orphans the dependents.
	DeletePropagationOrphan DeletionPropagation = "Orphan"
	// Deletes the object from the key-value store, the garbage collector will
	// delete the dependents in the background.
	DeletePropagationBackground DeletionPropagation = "Background"
	// The object exists in the key-value store until the garbage collector
	// deletes all the dependents whose ownerReference.blockOwnerDeletion=true
	// from the key-value store.  API sever will put the "foregroundDeletion"
	// finalizer on the object, and sets its deletionTimestamp.  This policy is
	// cascading, i.e., the dependents will be deleted with Foreground.
	DeletePropagationForeground DeletionPropagation = "Foreground"
)

再结合OwnerReference.BlockOwnerDeletion 参数的注释

1
2
3
4
5
6
7
8
	// If true, AND if the owner has the "foregroundDeletion" finalizer, then
	// the owner cannot be deleted from the key-value store until this
	// reference is removed.
	// Defaults to false.
	// To set this field, a user needs "delete" permission of the owner,
	// otherwise 422 (Unprocessable Entity) will be returned.
	// +optional
	BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"`

我们可以了解到。同步删除的开启方式如下:

  1. DeleteOptions.PropagationPolicy = DeletePropagationForeground
  2. 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
2
3
4
5
// These are internal finalizer values for Kubernetes-like APIs, must be qualified name unless defined here
const (
	FinalizerOrphanDependents string = "orphan"
	FinalizerDeleteDependents string = "foregroundDeletion"
)

API-Server 的Delete函数,在接受到删除请求的时候,会检查 DeleteOptions.PropagationPolicy参数,若其值为DeletePropagationForeground, API-Server 随即会对该资源对象进行 Update 操作:

  1. 插入FinalizerDeleteDependents Finalizer
  2. 设置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?

A:

通过对 k8s GC 设计文档的阅读,可以大致的概括一下:GC 主要是按照用户的需求来清理系统中「异常」的资源,用户可以自定义「清理方式」和「清理策略」。不难发现,在 GC 中,到底是保留一个资源还是删除一个资源都参照了资源之间的「从属关系」。资源的「从属关系」可以大致分为几个形态:

  1. 无从属关系:这部分资源基本不会被 GC 做处理
  2. 有从属关系
    1. 不符合用户预期:删除异常资源
    2. 符合用户预期:解绑异常资源之间的级联关系

在有从属关系的资源之间,即使被探测到关系异常,也并不代表一定要将他们都清除。如果有 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。