构想这么一个场景:咱们在 K8s 上创立了一个对象,它依据须要生成正本集和 Pod。在查看时,咱们脱漏了容器某个属性的设置,因而又从新编辑了 Deployment。新的 Deployment 就产生了新的正本集对象和新的 Pod。这里就呈现了一个问题,旧的正本集和 Pop 去哪了?另外,如果间接删除 Deployment,那正本集和 Pod 又会如何?事实就是,在删除 Deployment 后,正本集和 Pod 也会一起被删除,要不然集群早就乱套了。
在这个场景之下,咱们能够深刻思考几个问题:在 K8s 中该如何实现级联删除?有几种级联删除策略?在 K8s 中有没有可能存在孤儿对象(orphan object)?这些问题其实就是典型的垃圾回收(garbage collection,GC)问题。本文将介绍 K8s 中垃圾回收的概念以及实现办法。
什么是垃圾回收?
一般来说,垃圾回收(GC)就是从零碎中删除未应用的对象,并开释调配给它们的计算资源。GC 存在于所有的高级编程语言中,较低级的编程语言通过零碎库实现 GC。
GC 最常见的算法之一是 mark-and-sweep,这个算法会标记将删除的对象,再进行删除,如下图所示:
OwnerRefernce
在面向对象的语言中,一些对象会援用其余对象或者间接由其余对象组成,k8s 也有相似模式,例如正本集治理一组 Pod,而 Deployment 又治理着正本集。
但与面向对象语言不同的是,在 K8s 对象的定义中,没有明确所有者之间的关系,那么零碎要如何确定它们的关系呢?其实,在 K8s 中,每个隶属对象都具备 惟一数据字段名称 metadata.ownerReferences
用于确定关系。
从 Kubernetes v1.8 开始,K8s 对于 ReplicaSet、StatefulSet、DaemonSet、Deployment、Job、CronJob 等创立或治理的对象,会主动为其设置 ownerReferences 的值。如果有须要,咱们还能够手动设置 ownerReferences。
以下内容显示了 core-dns Deployment 上 metadata.ownerReferences
的值。
k get deployment -n kube-system -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES
coredns 2/2 2 2 44d coredns k8s.gcr.io/coredns:1.6.7
k get rs -n kube-system -o json | jq ".items[0].metadata.name, .items[0].metadata.ownerReferences"
"coredns-66bff467f8"
[
{
"apiVersion": "apps/v1",
"blockOwnerDeletion": true,
"controller": true,
"kind": "Deployment",
"name": "coredns",
"uid": "d8f29b78-439c-497e-9a45-7c33bd626a9f"
}
]
k get pods coredns-66bff467f8-rsnmg -n kube-system -o json | jq ".metadata.name, .metadata.ownerReferences"
"coredns-66bff467f8-rsnmg"
[
{
"apiVersion": "apps/v1",
"blockOwnerDeletion": true,
"controller": true,
"kind": "ReplicaSet",
"name": "coredns-66bff467f8",
"uid": "085d5398-1358-43e2-918e-2e03da18c7bd"
}
]
认真察看上述命令的输入,其实它和其余对象 GC 之间是有些许差异的。对象关联参考金字塔是颠倒的:
K8s 的垃圾回收策略
如后面所讲,在 Kubernetes v1.8 之前,依赖对象逻辑删除的实现是在客户端,对于某些资源而言则是在控制器端。有时,客户端会中途失败,导致集群状态凌乱,须要手动清理。起初为了解决这个问题,K8s 社区引入并实现了 Garbage Collector Controller(垃圾回收器),用更好用且更简略的形式实现 GC。在 K8s 中,有两大类 GC:
- 级联(Cascading):在级联删除中,所有者被删除,那集群中的隶属对象也会被删除。
- 孤儿(Orphan):这种状况下,对所有者的进行删除只会将其从集群中删除,并使所有对象处于“孤儿”状态。
级联删除
在级联删除(cascading deletion strategy)中,隶属对象(dependent object)与所有者对象(owner object)会被一起删除。在级联删除中,又有两种模式:前台(foreground)和 后盾(background)。
前台级联删除(Foreground Cascading Deletion):在这种删除策略中,所有者对象的删除将会继续到其所有隶属对象都被删除为止。当所有者被删除时,会进入“正在删除”(deletion in progress)状态,此时:
- 对象依然能够通过 REST API 查问到(可通过 kubectl 或 kuboard 查问到)
- 对象的 deletionTimestamp 字段被设置
- 对象的 metadata.finalizers 蕴含值 foregroundDeletion
一旦对象被设置为“正在删除”状态,垃圾回收器将删除其隶属对象。当垃圾回收器曾经删除了所有的“blocking”隶属对象(ownerReference.blockOwnerDeletion=true 的对象)当前,将删除所有者对象。
后盾级联删除(Background Cascading Deletion):这种删除策略会简略很多,它会立刻删除所有者的对象,并由垃圾回收器在后盾删除其隶属对象。这种形式比前台级联删除快的多,因为不必等待时间来删除隶属对象。
孤儿删除
在 孤儿删除策略(orphan deletion strategy)中,会间接删除所有者对象,并将隶属对象中的 ownerReference 元数据设置为默认值。之后垃圾回收器会确定孤儿对象并将其删除。
垃圾回收器如何工作?
如果对象的 OwnerReferences 元数据中没有任何所有者对象,那么垃圾回收器会删除该对象。垃圾回收器由 Scanner、Garbage Processor 和 Propagator 组成:
Scanner:它会检测 K8s 集群中反对的所有资源,并通过管制循环周期性地检测。它会扫描零碎中的所有资源,并将每个对象增加到 ” 脏队列 ”(dirty queue)中。
Garbage Processor:它由在 ” 脏队列 ” 上工作的 worker 组成。每个 worker 都会从 ” 脏队列 ” 中取出对象,并查看该对象里的 OwnerReference 字段是否为空。如果为空,那就从“脏队列”中取出下一个对象进行解决;如果不为空,它会检测 OwnerReference 字段内的 owner resoure object 是否存在,如果不存在,会申请 API 服务器删除该对象。
Propagator:用于优化垃圾回收器,它蕴含以下三个组件:
- EventQueue:负责存储 k8s 中资源对象的事件
- DAG(有向无环图):负责存储 k8s 中所有资源对象的 owner-dependent 关系
- Worker:从 EventQueue 中取出资源对象的事件,并依据事件的类型会采取操作
在有了 Propagator 的退出之后,咱们齐全能够仅在 GC 开始运行的时候,让 Scanner 扫描零碎中所有的对象,而后将这些信息传递给 Propagator 和“脏队列”。只有 DAG 一建设起来之后,那么 Scanner 其实就没有再工作的必要了。
总体而言,K8s 中 GC 的实现是十分通用且十分无效,心愿这篇文章能够帮忙大家更加理解 K8s 中的 GC。
原文请点击:https://mp.weixin.qq.com/s/zhygwHbdK1h7sViHWN93hw