evictionManager 的运行

和oom的对比

首先对比一下 evictionManager 和 oom

* System OOM events本来就是对资源敏感的,它会stall这个Node直到完成了OOM Killing Process。

* 当OOM Killer干掉某些containers之后,kubernetes Scheduler可能很快又会调度一个新的Pod到该Node上或者container 直接在node上restart,马上又会触发该Node上的OOM Killer启动OOM Killing Process,事情可能会没完没了的进行。

接下来看看 evictionManager

* Kubelet通过pro-actively监控并阻止Node上资源的耗尽,一旦触发Eviction Signals,就会直接Fail一个或者多个Pod以回收资源,而不是通过Linux OOM Killer这样本身耗资源的组件进行回收。

* 这样的Eviction Signals的可配置的,可以做到Pro-actively。

* 另外,被Evicted Pods会在其他Node上重新调度,而不会再次触发本Node上的再次Eviction。

工作机制

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

kubelet会通过监控Eviction Signal的值,当达到配置的阈值时,就会触发Evict Pods。 kubelet对应的监控周期,就通过cAdvisor的 evictionMonitoringPeriod  配置的,默认10s。

如果一个Node上监控到的Soft Eviction Signals的值,一直在eviction-soft水平线上下波动,那么为了避免 Node Condition 在true和false频繁切换,配置了  EvictionPressureTransitionPeriod 的值。一旦  MemoryPressure Or DiskPressure为True的前提下, 发生了Soft Evction Singal低于Soft Eviction Thresholds的情况,则需要等待配置时间(默认5m)才会切回 false。

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

如果 eviction pods 回收了一小部分资源就满足了要求,可能随着资源使用的波动或者新的调度Pod使得在该Node上很快又会触发evict pods的动作,这个是非常不划算的。所以在回收资源的时候,不仅要保证比  Eviction Thresholds 低,还要比它低  EvictionMinimumReclaim 所配置的数量。

* kubelet会Fail一个Pod时,会将Pod内所有Containners都kill掉,并把PodPhase设为Failed。

在每一个监控周期内,如果Eviction Thresholds被触及,则:

获取候选Pod

Fail the Pod

等待该Pod被Terminated 如果该Pod由于种种原因没有被成功Terminated,Kubelet将会再选一个Pod进行Fail Operation。其中,Fail Pod的时候,Kubelet是通过调用容器运行时的KillPod接口,如果接口返回True,则认为Fail Pod成功,否则视为失败。

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

Eviction Thresholds 支持绝对值和相对百分比两种形式

*  memory.available<10%

*  memory.available<1Gi

Soft Eviction Thresholds 指的是,当Eviction Signal中值达到配置的值时,并不会马上触发Kubelet去Evict Pods,而是会等待一个用户配置的grace period之后,再触发。

Hard Eviction Thresholds  就是, 当Eviction Signal中值达到Hard Eviction Thresholds配置的值时,会立刻触发Kubelet去Evict Pods 。 这个值,要设置的比eviction-soft更低才有意义。

代码学习

1. evictionManager  的创建

首先,Kubelet 在初始化的时候,初始化一个 evictionManager 对象;

然后,在初始化 runtime 的时候启动这个 evictionManager;

接下来,看一下 manager 的定义;

managerImpl struct {

    clock clock.Clock

    config Config    // 配置,包括

            PressureTransitionPeriod( –eviction-pressure-transition-period)

            MaxPodGracePeriodSeconds(–eviction-max-pod-grace-period)

            Thresholds(–eviction-hard, –eviction-soft)

            KernelMemcgNotification(–experimental-kernel-memcg-notification)

    killPodFunc KillPodFunc    // evict pod时kill pod的接口,kubelet NewManager的时候,赋值为killPodNow方法

    imageGC ImageGC    // 当node出现diskPressure condition时,imageGC进行unused images删除操作以回收disk space

    containerGC ContainerGC    // 使用  containerGC  来对容器进行删除操作

    sync.RWMutex

    nodeConditions []v1.NodeConditionType

    nodeConditionsLastObservedAt nodeConditionsObservedAt

    nodeRef *v1.ObjectReference    // 记录有关节点的事件

    recorder record.EventRecorder    // used to measure usage stats on system

    summaryProvider stats.SummaryProvider    // 提供node和node上所有pods的最新status数据汇总,既NodeStats and []PodStats。

    thresholdsFirstObservedAt thresholdsObservedAt    // 记录threshold第一次观察到的时间

    thresholdsMet []evictionapi.Threshold    // 保存已经触发但还没解决的Thresholds,包括那些处于grace period等待阶段的Thresholds。

    resourceToRankFunc map[v1.ResourceName]rankFunc    // 定义各种Resource进行evict 挑选时的排名方法。

    resourceToNodeReclaimFuncs map[v1.ResourceName]nodeReclaimFuncs    // 定义各种Resource进行回收时调用的方法。

    lastObservations signalObservations    // 上一次获取的eviction signal的记录,确保每次更新thresholds时都是按照正确的时间序列进行。

    notifiersInitialized bool    // 表示threshold notifier是否已经初始化,以确定是否可以利用kernel memcg notification功能来提高evict的响应速度。目前创建manager时该值为false,是否要利用kernel memcg notification,完全取决于kubelet的--experimental-kernel-memcg-notification参数。

    dedicatedImageFs *bool

}

NewManager不但返回evictionManager对象,还返回了一个lifecycle.PodAdmitHandler实例evictionAdmitHandler,它其实和evictionManager的内容相同,但是不同的两个实例。evictionAdmitHandler用来kubelet创建Pod前进行准入检查,满足条件后才会继续创建Pod,通过 Admit 方法来检查

这里EvictionManager就影响了 Pod调度的逻辑,在机器有压力的时候,禁止了新的pod产生。

2. evictionManager  的运行

start 方法,启动了一个goroutine,来调用 synchronize 方法,获取已经被驱逐的pods,并等待清理操作完成。

在 synchronize  方法中

注册Evict Pod时各种Resource的排名函数 (对于内存,硬盘的使用量进行排序)

m.resourceToRankFunc = buildResourceToRankFunc(hasImageFs)

注册回收Node Resource的Reclaim函数 (清理所有没有用到的容器和镜像)

m.resourceToNodeReclaimFuncs = buildResourceToNodeReclaimFuncs(m.imageGC, m.containerGC, hasImageFs)

通过makeSignalObservations从cAdvisor中获取Eviction Signal Observation和Pod的StatsFunc(后续对Pods进行Rank时需要用)

observations, statsFunc, err := makeSignalObservations(m.summaryProvider, capacityProvider, activePods)

如果kubelet配置了--experimental-kernel-memcg-notification且为true

if m.config.KernelMemcgNotification && !m.notifiersInitialized {

     通过startMemoryThresholdNotifier启动soft & hard memory notification 当system usage第一时间达到soft & hard memory thresholds时,会立刻通知kubelet,并触发evictionManager.synchronize进行资源回收的流程。这样提高了eviction的实时性。(下面调用时候的最后一个参数即handler)

     err = startMemoryThresholdNotifier(m.config.Thresholds, observations, false, func(desc string) { m.synchronize(diskInfoProvider, podFunc, capacityProvider)})

    err = startMemoryThresholdNotifier(m.config.Thresholds, observations, true, func(desc string) { m.synchronize(diskInfoProvider, podFunc, capacityProvider)})

}

根据从cAdvisor数据计算得到的Observation(observasions)和配置的thresholds通过thresholdsMet计算得到此次Met的thresholds

thresholds = thresholdsMet(thresholds, observations, false)

再根据从cAdvisor数据计算得到的Observation(observasions)和thresholdsMet通过thresholdsMet计算得到已记录但还没解决的thresholds,然后与上一步中的thresholds进行合并。

thresholdsNotYetResolved := thresholdsMet(m.thresholdsMet, observations, true)

根据lastObservations中Signal的时间,对比observasions的中Signal中的时间,过滤thresholds。

thresholds = mergeThresholds(thresholds, thresholdsNotYetResolved)

更新thresholdsFirstObservedAt, nodeConditions

nodeConditionsLastObservedAt := nodeConditionsLastObservedAt(nodeConditions, m.nodeConditionsLastObservedAt, now)

nodeConditions = nodeConditionsObservedSince(nodeConditionsLastObservedAt, m.config.PressureTransitionPeriod, now)

过滤出那些从observed time到now,已经历过grace period时间的thresholds。

thresholds = thresholdsMetGracePeriod(thresholdsFirstObservedAt, now)

更新evictionManager对象的内部数据: nodeConditions,thresholdsFirstObservedAt,nodeConditionsLastObservedAt,thresholds,observations。

m.nodeConditions = nodeConditions

m.thresholdsFirstObservedAt = thresholdsFirstObservedAt    

m.nodeConditionsLastObservedAt = nodeConditionsLastObservedAt    

m.thresholdsMet = thresholds

m.lastObservations = observations

根据thresholds得到starvedResources

starvedResources := getStarvedResources(thresholds)

进行排序,如果memory属于starvedResources,则memory排序第一

sort.Sort(byEvictionPriority(starvedResources))

取starvedResources排第一的Resource,调用reclaimNodeLevelResources对Node上这种Resource进行资源回收。如果回收完后,available满足thresholdValue+evictionMinimumReclaim,则流程结束,不再evict user-pods

resourceToReclaim := starvedResources[0]

m.reclaimNodeLevelResources(resourceToReclaim, observations)

如果reclaimNodeLevelResources后,还不足以达到要求,则会继续evict user-pods,首先根据前面buildResourceToRankFunc注册的方法对所有active Pods进行排序

rank, ok := m.resourceToRankFunc[resourceToReclaim]

rank(activePods, statsFunc)

按照前面的排序,顺序的调用killPodNow将选出的pod干掉。如果kill某个pod失败,则会跳过这个pod,再按顺序挑下一个pod进行kill。只要某个pod kill成功,就返回结束,也就是说这个流程中,最多只会kill最多一个Pod

for i := range activePods {

     m.killPodFunc(pod, status, &gracePeriodOverride)

     return []*v1.Pod{pod}

}

其中,资源回收 reclaimNodeLevelResources 就是调用 GC 的方法进行资源的回收。

killPodNow 则调用了 podWorkers.UpdatePod 方法进行pod的驱逐。


一些总结

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

Eviction Signal支持:memory.available, nodefs.available, nodefs.inodesFree, imagefs.available, imagefs.inodesFree。

通过设置Hard Eviction Thresholds和Soft Eviction Thresholds相关参数来触发Kubelet进行Evict Pods的操作。

Evict Pods的时候根据Pod QoS和资源使用情况挑选Pods进行Kill。

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

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

当Node Condition为MemoryPressure时,Scheduler不会调度新的QoS Class为BestEffort的Pods到该Node。

当Node Condition为DiskPressure时,Scheduler不会调度任何新的Pods到该Node。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 在startKubelet 方法中开始运行创建好的kubelet。kubelet可以只运行一次,即RunOnce,...
    shinwing阅读 1,839评论 1 0
  • kubelet kubelet 的声明 Kubelet struct { kubeletConfiguration...
    shinwing阅读 845评论 0 1
  • pkg/kubelet/kubelet.go --> HandlePodAdditions方法 Pod创建首先来...
    shinwing阅读 3,310评论 1 0
  • kubelet运行机制分析 在Kubernetes集群中,每个Node节点(又称Minion)上都会启动一个Kub...
    c84f3109853b阅读 5,981评论 1 5
  • (一) 她耳上一双明月珰在月光下晃动着,说...
    祁门小谢阅读 648评论 0 1