Kubernetes GarbageCollector

xiaoxiao2021-02-28  81

GarbageCollector controller

1.--enable-garbage-collector参数决定是否启动GCcontroller

2.通过disvoveryclient获取所有可以删除的资源

3.获取默认需要被忽略不删除的资源

4.创建GarbageCollectorGraphBuilder对象

5.调用syncMonitors为每个可以删除并不能忽略的资源创建monitor,即创建一个handler,这个handler为资源的创建更新删除操作创建event在追踪

6.调用gogarbageCollector.Run(workers,ctx.Stop)启动garbagecollector.

7.调用garbageCollector.Sync定期从discoveryservice中同步资源,当新的资源加进来就为其创建新的monitor,当资源被删除了就删除它对应的monitor

garbageCollector.Run

1.gogc.dependencyGraphBuilder.Run(stopCh)启动GraphBuilder,为所有资源增加monitor,根据监听到的event为资源生成含有依赖的Graph,即各资源相互的依赖图,再根据资源含有的Finalizer将资源加入到孤立资源的队列或删除资源的队列中

2.定期执行gc.runAttemptToDeleteWorker删除资源

3.定期执行gc.runAttemptToOrphanWorker孤立资源

二启动GraphBuilder

1.调用gb.startMonitors()启动各个资源的monitor

1.1调用各个资源的monitorinformerstart函数开始监控各资源。

2.定期调用runProcessGraphChanges()处理monitor产生的event

2.1event中获取资源对象

2.2如果event是第一次做增加或者更新,则将对象封装成node

type objectReference struct { metav1.OwnerReference // This is needed by the dynamic client Namespace string } type node struct { //node本身资源的详细信息 identity objectReference dependentsLock sync.RWMutex //node的所有依赖,例如rc的依赖是pod dependents map[*node]struct{} //依赖是否需要删除,即deletionTimestamp不为nil且含有DeleteDependentsFinalizer deletingDependents bool deletingDependentsLock sync.RWMutex //本身是否将要被删除,即deletionTimestamp不为nil beingDeleted bool beingDeletedLock sync.RWMutex //nodeowner信息 owners []metav1.OwnerReference }

2.3调用insertNode将该node加入到gb.uidToNode,该函数将为node找到所有owner并将该node将入到ownerdependents列表中。

2.3.1调用gb.uidToNode.Write(n)node加入到gb.uidToNode中。

2.3.2获取node的所有owner,如果owner存在gb.uidToNode中,则将node加入到ownerdependents列表中。

2.3.3如果owner不存在gb.uidToNode中,则为该owner生成一个virtualnode,也将该virtualnode加入到gb.uidToNode中,并将它加入到gb.attemptToDelete(后期会遍历它确认是否存在)。

2.3.4如果该node含有orphanDependentsFinalizer,则将node加入gb.attemptToOrphan

2.3.5如果该node含有DeleteDependentsFinalizer,则将nodenodedependents一起将入gb.attemptToDelete中。

2.4如果node已经存在gb.uidToNode,并且产生了增加或者更新操作

2.4.1调用referencesDiffs比较之前的nodeowner和当前的nodeowner

2.4.2将不属于当前nodeowner和当前node发生了改变的owner加入gb.attemptToDelete

2.4.3更新nodeowner,并将node添加到新的onwerdependents列表中

2.4.4node从属于老node而不属于当前nodeownerdependents列表中删除

2.4.5如果该node含有orphanDependentsFinalizer,则将node加入gb.attemptToOrphan

2.4.6如果该node含有DeleteDependentsFinalizer,则将nodenodedependents一起将入gb.attemptToDelete中。

2.5如果event是删除资源

2.5.1更新onwerdependents,将nodedependentsowner将入gb.attemptToDelete

.定期执行gc.runAttemptToDeleteWorker删除资源

1:依赖或者本身是否需要被删除,在构建node的时候就判断好了,是根据finalizerDeletionTimestamp来判断的

2:等所有依赖删除完之后,删除FinalizerDeleteDependents

3DeletePropagationForeground是等依赖全部删除后才将自己删除

4DeletePropagationBackground是立即将自己删除再由GC去删除依赖

1.调用attemptToDeleteItem删除node

1.0如果node需要被删除,但它的依赖不需要删除,则直接返回,即只删除node

1.1如果nodevirtualnode则为它生成一个virtualdelete event把该nodegb.uidToNode中删除

1.2调用processDeletingDependentsItem处理依赖,如果依赖全部被删除了,则将nodeFinalizerDeleteDependents删除,如果没有删除依赖,则将依赖加入gc.attemptToDelete

1.3调用classifyReferences找出nodeowner

1.3.1假如含有不被回收的owner,则只更新资源,将需要被删除的owner从资源的中删除,修改etcd中资源的数据。

1.3.2假如它的owner是需要被删除并且它的依赖还存在也是需要被删除的,则直接调用metav1.DeletePropagationForeground将资源删除

1.3.3否则,根据资源的finalizer来确定删除的policyDeletePropagationForeground或者

DeletePropagationBackground(没有finalizer应该使用DeletePropagationBackground,之前没有删除,是因为这段代码没有设置删除的policy)

总结:1.node需要删除依赖不需要删除,这是orphan,这里不处理

2.nodevirtual的,直接从graph中删除node

3.owner不需要删除,node需要删除,修改nodeowner属性值

4.node需要删除,依赖也要删除,则调用DeletePropagationForeground等依赖删除后,再删node

5.其它一律根据policy删除node

.定期执行gc.runAttemptToOrphanWorker孤立资源

1.调用orphanDependentsnode与所有依赖的关系断开,即从依赖的owner中删除node

2.将自己的FinalizerOrphanDependentsfinalizer删除。

1.发送删除deployment命令,apiserver删除orphanfianlizer并把deployment删除

2.GC检测到删除的event,将deployment对应的node删除,并将rs加入attemptToDelete队列

3.GC调用apiserver删除orphanfianlizer利用DeletePropagationForeground删除rs

4.GC检测到删除的event,将rs对应的node删除,并将pod加入attemptToDelete队列

5.GC调用apiserver删除pod

6.删除pod对应的node

PodGCController的流程 1.创建pod informer监视pod

2.定期执行gc()来回收pod

一:gc()

1.获取pod列表

2.如果定义了terminatedPodThreshold,则将需要删除的pod放入缓存,如果缓存数量超过了terminatedPodThreshold,则将超出的pod删除。

3.调用gcOrphanedpod对应不存在的node的情况,将pod删除

4.调用gcUnscheduledTerminating回收没有被调度且需要删除的pod

containerGC的流程

type ContainerGCPolicy struct { // Minimum age at which a container can be garbage collected, zero for no limit. MinAge time.Duration // Max number of dead containers any single pod (UID, container name) pair is // allowed to have, less than zero for no limit. MaxPerPodContainer int // Max number of total dead containers, less than zero for no limit. MaxContainers int }

1.根据用户启动kubelet的参数初始化ContainerGCPolicy对象

2.调用kubecontainer.NewContainerGC初始化containerGC

3.StartGarbageCollection()调用kl.containerGC.GarbageCollect()开始垃圾回收container

.kuberuntimegc

// Note that gc policy is not applied to sandboxes. Sandboxes are only removed when they are not ready and containing no containers. // GarbageCollect consists of the following steps: // * gets evictable containers which are not active and created more than gcPolicy.MinAge ago. // * removes oldest dead containers for each pod by enforcing gcPolicy.MaxPerPodContainer. // * removes oldest dead containers by enforcing gcPolicy.MaxContainers. // * gets evictable sandboxes which are not ready and contains no containers. // * removes evictable sandboxes.

1.调用evictContainers删除需要被删除的container

1.1调用evictableContainers获取所有需要被删除的container,即状态为notrunning并且创建时间超过MinAge

1.1.1调用runtimeService.ListContainers获取所有containers

1.1.2去除状态为runningcontainer,去除创建时间小于MinAgecontainer,按age排序生成container列表

1.2调用manager.removeContainer删除container列表中的所有container

1.3调用enforceMaxContainersPerEvictUnit确保每个pod中的deadcontainer个数不超过MaxPerPodContainer,否则删除最老的超过个数的container

1.4确保总的deadcontainer个数不超过MaxContainers,如果超过个数过大,则每个pod删除相应个数的container,如果还是超过,则删除最老的超过个数的container

2.调用evictSandboxes删除空的sandboxes

2.1过滤不是ready状态的sandbox,过滤仍然含有containersandbox,将剩下的sandbox删除

3.调用evictPodLogsDirectories删除podsandboxlog

3.1将“/var/log/pods中找出所有已经删除了的pod对应的目录,将这些目录删除

3.2将“"/var/log/containers"中找出所有已经deadcontainer对应的symlinks,将他们删除

ImageGCManager的流程 type ImageGCPolicy struct { // Any usage above this threshold will always trigger garbage collection. // This is the highest usage we will allow. HighThresholdPercent int // Any usage below this threshold will never trigger garbage collection. // This is the lowest threshold we will try to garbage collect to. LowThresholdPercent int // Minimum age at which an image can be garbage collected. MinAge time.Duration }

1.初始化ImageGCPolicy对象

2.创建imageManager对象

3.调用imageManager.GarbageCollect()垃圾回收镜像

3.1调用im.cadvisor.ImagesFsInfo()获取文件系统中容器镜像的使用情况

3.2如果使用的空间大于HighThresholdPercent,则计算出需要回收多少空间(bytesToFree)才能达到LowThresholdPercent,并调用freeSpace()回收

3.2.1依次删除不使用的并且存在时间超过minage的老containerimage,直到回收空间大于bytesToFree

evictionManager的流程 OOM Killer回收资源的缺点(主要在还没有触发eviction,却触发了node上的linux kernel oom_killeroom阈值由pod qos为每个container设置): 1.它会停止node直到完成了OOM Killing Process 2.OOM Killer干掉container后,scheduler可能很快又会调用新的pod到该node上,导致再次OOM 3.如果Pod中某个容器被oom_killer干掉之后,会根据该容器的RestartPolicy决定是否restart这个容器,如果再次restart又会触发oom eviction manager的优点(缺点处理时间久): 1.当触发eviction后,eviction manager直接fail pod来回收资源,而不是通过Linux OOM killer这样本身耗资源的组件进行回收 2.evictedpod会在其他node上重新调度(会设置node conditoins,并继续按照--node-status-update-frequency(default 10s)配置的时间间隔,周期性的与kube-apiserver进行node status updates,如果node conditions标识为资源不足(相当于做一个准入检查),kubelet拒绝创建pod中的container,并标记podPodFailed状态,导致重新调度。。再者kubelet会定期将node condition传给kubeapiserver并存如etcd中,kube-scheduler watchnode condition pressure之后,会阻止pod bind到该node),不会再次触发eviction

KubeletEviction Policy的工作机制。

kubelet预先监控本节点的资源使用,并且阻止资源被耗尽,这样保证node的稳定性。

kubelet会预先FailN(>= 1)Pod以回收出现紧缺的资源。

kubeletFail一个pod时,会将Pod内所有Containnerskill掉,并把PodPhase设为Failed

kubelet通过事先人为设定EvictionThresholds来触发Eviction动作以回收资源。

触发eviction的资源有memorynodefs(存储volumelogs等数据的空间)和imagefsdocker/rkt存放镜像的空间和容器的writable layer eviction含有Soft Eviction ThresholdsHard Eviction Thresholds soft:达到eviction阈值后,再次监测grace period一段时间,这段时间仍然达到阈值则触发eviction action hard:达到eviction阈值后,直接触发eviction action 注:

Kubelet通过EvictionSignal来记录监控到的Node节点使用情况。

EvictionSignal支持:memory.available,nodefs.available, nodefs.inodesFree, imagefs.available,imagefs.inodesFree

通过设置HardEviction ThresholdsSoftEviction Thresholds相关参数来触发Kubelet进行EvictPods的操作。

EvictPods的时候根据PodQoS和资源使用情况挑选Pods进行Kill

Kubelet通过eviction-pressure-transition-period防止NodeCondition来回切换引起scheduler做出错误的调度决定。

Kubelet通过--eviction-minimum-reclaim来保证每次进行资源回收后,Node的最少可用资源,以避免频繁被触发EvictPods操作。

NodeConditionMemoryPressure时,Scheduler不会调度新的QoSClassBestEffortPods到该Node

NodeConditionDiskPressure时,Scheduler不会调度任何新的Pods到该Node

流程: 0.调用ParseThresholdConfig根据用户传入的参数生成各资源的Threshold 1.定期调用synchronize()驱逐pod,并返回需要被驱逐回收的pod 2.调用waitForPodsCleanup等待pod上的资源被回收,防止再次触发eviction 2.1假如pod含有还在runningcontainer,即没有被回收完,会导致等待其回收完 2.2假如podterminatedpodcontainer没有被删除,即没有被回收完,会导致等待其回收完 2.3假如podvolume是需要删除的但还存在,即没有被回收完,会导致等待其回收完 2.4假如podcgroup sandbox没有被删除,即没有被回收完,会导致等待其回收完 一:synchronize() 1.调用makeSignalObservations创建观察者获取当前资源情况 1.1创建以下观察者: memory.available:可用的内存 nodefs.available:系统可用的volume空间 nodefs.inodesFreeinode可用的volume空间 imagefs.available:系统可用的镜像空间 imagefs.inodesFreeinode可用的镜像空间 allocatableMemory.available:可分配给pod的内存大小 allocatableNodeFs.available:可分配给pod的存储大小 2.根据参数为memory.available创建soft/hard threshold notifier 3.将用户设置的Thresholds跟各观察者获取的当前资源情况比较,将触发了阈值的Threshold和之前没有处理完的Threshold存入Thresholds列表 4.设置第一次触发threshold的时间和最近一次触发threshold的时间 5.调用thresholdsMetGracePeriod获取在grace period时间段内一直触发阈值的threshold 6.如果本地卷临时存储中存在资源使用违规,则会调用localStorageEviction驱逐pod 6.1localStorageEviction判断pod使用的EmptyDir volumeephemeral-storage 是否超过限制,超过则将pod驱逐, 6.2localStorageEviction判断container使用的EmptyDir volumeephemeral-storage 是否超过限制,超过则将container对应的pod驱逐 6.3evictPod()执行驱逐操作,它调用podWorkers设置status managerpod的状态,并调用containerRuntime.KillPodpod中的container全部删除,遗留下一个空的pod 7.从触发阈值的thresholds中获取资源类型,并调用byEvictionPriority将资源类型排序,获取最迫切需要被回收的资源,memory优先,其它同级 8.调用reclaimNodeLevelResources尝试回收node级别的资源(非memory资源),即删除已经终止的container和删除不使用的镜像,如果能满足threthold则不驱逐pod 8.1调用containerGC.DeleteAllUnusedContainers()删除所有不使用的container 8.2调用imageGC.DeleteUnusedImages()删除所有不使用的image,最终调用freeSpace释放空间 9.根据最迫切需要被回收的资源获取rank 10.rank将所有active pod排序, memory通过rankMemoryPressure来排序 其它存储资源通过rankDiskPressureFunc来排序 11.调用killPodFunc删除排序靠前的一个pod(只删除一个pod返回,仍旧触发threthold就再次回收一个pod),跟6的步骤类似 OOM watcher :调用cadvisor.WatchEvents监视系统的OOM, 如果发生了OOM 就产生相应的event QOS服务质量管理 Kubelet根据Pod QoS给每个container都设置了oom_score_adj oom_killer根据container使用的内存占Node总内存的百分比计算得到该containeroom_score,然后再将该oom_sore和前面对应的oom_score_adj相加作为最终的oom_scoreNode上所有containers的最终oom_score进行排名,将oom_score得分最高的container kill掉。通过如此方式进行资源回收。

oom_killer这样做的目标就是干掉QoS低的又消耗最多内存(相对request)的容器首先被kill掉,如此回收内存。

对于每一种Resource都可以将容器分为3QoSClasses: Guaranteed, Burstable, andBest-Effort,它们的QoS级别依次递减。

Guaranteed如果Pod中所有Container的所有Resourcelimitrequest都相等且不为0,则这个PodQoSClass就是Guaranteed

Best-Effort如果Pod中所有容器的所有Resourcerequestlimit都没有赋值,则这个PodQoSClass就是Best-Effort.

BurstablePOD中只要有一个容器,这个容器requestslimits的设置同其他容器设置的不一致,那么这个PODQoS就是Burstable级别

KUBELET根据limitrequset数值判断qos类型,并为container设置对应的OOM分数,kubelet在创建container的时候会把这个分数写到container对应的进程pid对应的/proc/<pid>/oom_score_adj中,OOMkiller根据这个分数kill进程

OOM分数值是根据OOM_ADJ参数计算出来的,对于Guaranteed级别的PODOOM_ADJ参数设置成了-998,对于BestEffort级别的PODOOM_ADJ参数设置成了1000,对于Burstable级别的PODOOM_ADJ参数取值从2999,对于kube保留的资源,比如kubeletOOM_ADJ参数设置成了-999OOM_ADJ参数设置的越大,通过OOM_ADJ参数计算出来的OOM分数越高,OOM分数越高,这个POD的优先级就越低,在出现资源竞争的时候,就越早被kill

注意,如果一个容器只指明了limit,而未指明request,则表明request的值等于limit的值。

从容器的角度出发,为了限制容器使用的CPU和内存,是通过cgroup来实现的,目前kubernetesQoS只能管理CPU和内存,所以kubernetes现在也是通过对cgroup的配置来实现QoS管理的

newActiveDeadlineHandler 调用statusManager获取pod的状态,从状态中获取pod的开始时间,再用当前时间减去开始时间的值与podActiveDeadlineSeconds的值比较,如果超过了ActiveDeadlineSeconds,则认为这个pod需要被驱逐
转载请注明原文地址: https://www.6miu.com/read-1950005.html

最新回复(0)