k8s节点资源预留与 pod 驱逐

为何

K8S 的节点上的资源会被 pod 和系统进程所使用,若是默认什么都不配置,那么节点上的所有资源都是能够分配给pod使用的,系统进程自己没有保障,这样作很危险:node

  • 集群雪崩:若是节点上调度了大量pod,且pod没有合理的limit限制,节点资源将被耗尽,sshd、kubelet等进程OOM,节点变成 not ready状态,pod从新继续调度到其余节点,新节点也被打挂,引发集群雪崩。
  • 系统进程异常:就算 pod 设置了limit,但若是机器遇到资源不足,系统进程如 docker 没有资源保障,会频繁 OOM,或者进程 hang 住无响应,虽然能运行,但容器会反复出问题

节点资源主要分为两类:git

  • 可压缩资源:如CPU,即便cpu 超配,也能够划分时间片运行,只是运行变慢,进程不会挂。
  • 不可压缩资源:Memory/Storage,内存不一样于CPU,系统内存不足时,会触发 OOM杀死进程,按照oom score 来肯定先kill谁,oom_score_adj值越高,被kill 的优先级越高。

oom 分数:
imagegithub

因此,OOM 的优先级以下:docker

BestEffort Pod > Burstable Pod > 其它进程 > Guaranteed Pod > kubelet/docker 等 > sshd 等进程

所以须要对节点的内存等资源进行配置,以保证节点核心进程运行正常。bash

怎么作

节点资源的配置通常分为 2 种:服务器

  1. 资源预留:为系统进程和 k8s 进程预留资源
  2. pod 驱逐:节点资源到达必定使用量,开始驱逐 pod

image

  • Node Capacity:Node的全部硬件资源
  • kube-reserved:给kube组件预留的资源:kubelet,kube-proxy以及docker等
  • system-reserved:给system进程预留的资源
  • eviction-threshold:kubelet eviction的阈值设定
  • Allocatable:真正scheduler调度Pod时的参考值(保证Node上全部Pods的request resource不超过Allocatable)

allocatable的值即对应 describe node 时看到的allocatable容量,pod 调度的上限ssh

计算公式:节点上可配置值 = 总量 - 预留值 - 驱逐阈值

Allocatable = Capacity - Reserved(kube+system) - Eviction Threshold

以上配置均在kubelet 中添加,涉及的参数有:google

--enforce-node-allocatable=pods,kube-reserved,system-reserved
--kube-reserved-cgroup=/system.slice/kubelet.service
--system-reserved-cgroup=/system.slice
--kube-reserved=cpu=200m,memory=250Mi
--system-reserved=cpu=200m,memory=250Mi
--eviction-hard=memory.available<5%,nodefs.available<10%,imagefs.available<10%
--eviction-soft=memory.available<10%,nodefs.available<15%,imagefs.available<15%
--eviction-soft-grace-period=memory.available=2m,nodefs.available=2m,imagefs.available=2m
--eviction-max-pod-grace-period=30
--eviction-minimum-reclaim=memory.available=0Mi,nodefs.available=500Mi,imagefs.available=500Mi

配置含义

配置的含义以下:spa

(1)--enforce-node-allocatable3d

含义:指定kubelet为哪些进程作硬限制,可选的值有:

* pods
* kube-reserved
* system-reserve

这个参数开启并指定pods后kubelet会为全部pod的总cgroup作资源限制(经过cgroup中的kubepods.limit_in_bytes),限制为公式计算出的allocatable的大小。

假如想为系统进程和k8s进程也作cgroup级别的硬限制,还能够在限制列表中再加system-reserved和kube-reserved,同时还要分别加上--kube-reserved-cgroup和--system-reserved-cgroup以指定分别限制在哪一个cgroup里。
配置:--enforce-node-allocatable=pods,kube-reserved,system-reserved

(2)设置k8s组件的cgroup

含义:这个参数用来指定k8s系统组件所使用的cgroup。

注意,这里指定的cgroup及其子系统须要预先建立好,kubelet并不会为你自动建立好。
配置:--kube-reserved-cgroup=/system.slice/kubelet.service

(3)设置系统守护进程的cgroup

含义:这个参数用来指定系统守护进程所使用的cgroup。

注意,这里指定的cgroup及其子系统须要预先建立好,kubelet并不会为你自动建立好。
配置:--system-reserved-cgroup=/system.slice

(4)配置 k8s组件预留资源的大小,CPU、Mem

指定为k8s系统组件(kubelet、kube-proxy、dockerd等)预留的资源量,

如:--kube-reserved=cpu=1,memory=2Gi,ephemeral-storage=1Gi。

这里的kube-reserved只为非pod形式启动的kube组件预留资源,假如组件要是以static pod(kubeadm)形式启动的,那并不在这个kube-reserved管理并限制的cgroup中,而是在kubepod这个cgroup中。

(ephemeral storage须要kubelet开启feature-gates,预留的是临时存储空间(log,EmptyDir),生产环境建议先不使用)

ephemeral-storage是kubernetes1.8开始引入的一个资源限制的对象,kubernetes 1.10版本中kubelet默认已经打开的了,到目前1.11仍是beta阶段,主要是用于对本地临时存储使用空间大小的限制,如对pod的empty dir、/var/lib/kubelet、日志、容器可读写层的使用大小的限制。

(5)配置 系统守护进程预留资源的大小(预留的值须要根据机器上容器的密度作一个合理的值)

含义:为系统守护进程(sshd, udev等)预留的资源量,

如:--system-reserved=cpu=500m,memory=1Gi,ephemeral-storage=1Gi。

注意,除了考虑为系统进程预留的量以外,还应该为kernel和用户登陆会话预留一些内存。
配置:--system-reserved=cpu=200m,memory=250Mi

(6)配置 驱逐pod的硬阈值

含义:设置进行pod驱逐的阈值,这个参数只支持内存和磁盘。

经过--eviction-hard标志预留一些内存后,当节点上的可用内存降至保留值如下时,

kubelet 将会对pod进行驱逐。
配置:--eviction-hard=memory.available<5%,nodefs.available<10%,imagefs.available<10%

(7)配置 驱逐pod的软阈值

--eviction-soft=memory.available<10%,nodefs.available<15%,imagefs.available<15%

(8)定义达到软阈值以后,持续时间超过多久才进行驱逐

--eviction-soft-grace-period=memory.available=2m,nodefs.available=2m,imagefs.available=2m

(9)驱逐pod前最大等待时间=min(pod.Spec.TerminationGracePeriodSeconds, eviction-max-pod-grace-period),单位为秒

--eviction-max-pod-grace-period=30

(10)至少回收的资源量

--eviction-minimum-reclaim=memory.available=0Mi,nodefs.available=500Mi,imagefs.available=500Mi

以上配置均为百分比,举例:

以2核4GB内存40GB磁盘空间的配置为例,Allocatable是1.6 CPU,3.3Gi 内存,25Gi磁盘。当pod的总内存消耗大于3.3Gi或者磁盘消耗大于25Gi时,会根据相应策略驱逐pod。

硬驱逐与软驱逐

硬驱逐

kubelet 利用metric的值做为决策依据来触发驱逐行为,下面内容来自于 Kubelet summary API。

一旦超出阈值,就会触发 kubelet 进行资源回收的动做(区别于软驱逐,有宽限期),指标以下:

image

  • nodefs: 机器文件系统
  • imagesfs: Kubelet 可以利用 cAdvisor 自动发现这些文件系统,镜像存储空间

例如若是一个 Node 有 10Gi 内存,咱们但愿在可用内存不足 1Gi 时进行驱逐,就能够选取下面的一种方式来定义驱逐阈值:

  • memory.available<10%
  • memory.available<1Gi

能够配置百分比或者实际值,可是操做符只能使用小于号,即<

软驱逐

软阈值须要和一个宽限期参数协同工做。当系统资源消耗达到软阈值时,这一情况的持续时间超过了宽限期以前,Kubelet 不会触发任何动做。若是没有定义宽限期,Kubelet 会拒绝启动。

另外还能够定义一个 Pod 结束的宽限期。若是定义了这一宽限期,那么 Kubelet 会使用 pod.Spec.TerminationGracePeriodSeconds 和最大宽限期这两个值之间较小的那个(进行宽限),若是没有指定的话,kubelet 会不留宽限当即杀死 Pod。

软阈值的定义包括如下几个参数:

  • eviction-soft:驱逐阈值,例如 memory.available<1.5Gi,若是知足这一条件的持续时间超过宽限期,就会触发对 Pod 的驱逐动做。
  • eviction-soft-grace-period:驱逐宽限期,例如 memory.available=1m30s,用于定义达到软阈值以后,持续时间超过多久才进行驱逐。
  • eviction-max-pod-grace-period:达到软阈值以后,到驱逐一个 Pod 以前的最大宽限时间(单位是秒)

判断周期

Housekeeping interval 参数定义一个时间间隔,Kubelet 每隔这一段就会对驱逐阈值进行评估。

  • housekeeping-interval:容器检查的时间间隔。

节点表现

若是触发了硬阈值,或者符合软阈值的时间持续了与其对应的宽限期,Kubelet 就会认为当前节点压力太大,下面的节点状态定义描述了这种对应关系。

image

Kubelet 会持续报告节点状态的更新过程,这一频率由参数 —node-status-update-frequency 指定,缺省状况下取值为 10s。

若是一个节点的情况在软阈值的上下波动,可是又不会超过他的宽限期,将会致使该节点的状态持续的在是否之间徘徊,最终会影响下降调度的决策过程。

要防止这种情况,下面的标志能够用来通知 Kubelet,在脱离pressure以前,必须等待。

eviction-pressure-transition-period 定义了在脱离pressure状态以前要等待的时间

Kubelet 在把pressure状态设置为 False 以前,会确认在周期以内,该节点没有达到阈值


若是达到了驱逐阈值,而且超出了宽限期,那么 Kubelet 会开始回收超出限量的资源,直到回到阈值之内。

Kubelet 在驱逐用户 Pod 以前,会尝试回收节点级别的资源。若是服务器为容器定义了独立的 imagefs,他的回收过程会有所不一样。

有 Imagefs

若是 nodefs 文件系统到达了驱逐阈值,kubelet 会按照下面的顺序来清理空间:

  • 1.删除死掉的 Pod/容器

若是 imagefs 文件系统到达了驱逐阈值,kubelet 会按照下面的顺序来清理空间:

  • 1.删掉全部无用镜像

没有 Imagefs

若是 nodefs 文件系统到达了驱逐阈值,kubelet 会按照下面的顺序来清理空间。

  1. 删除死掉的 Pod/容器
  2. 删掉全部无用镜像

pod驱逐策略

Kubelet 会按照下面的标准对 Pod 的驱逐行为进行评判:

  • 根据服务质量:即BestEffort、Burstable、Guaranteed
  • 根据 Pod 调度请求的被耗尽资源的消耗量

接下来,Pod 按照下面的顺序进行驱逐(QOS):

  1. BestEffort:消耗最多紧缺资源的 Pod 最早驱逐。
  2. Burstable:请求(request)最多紧缺资源的 Pod 被驱逐,若是没有 Pod 超出他们的请求,会驱逐资源消耗量最大的 Pod。
  3. Guaranteed:请求(request)最多紧缺资源的 Pod 被驱逐,若是没有 Pod 超出他们的请求,会驱逐资源消耗量最大的 Pod。

参考 POD的QOS:服务质量等级

Guaranteed Pod 不会由于其余 Pod 的资源被驱逐。若是系统进程(例如 kubelet、docker、journald 等)消耗了超出 system-reserved 或者 kube-reserved 的资源,并且这一节点上只运行了 Guaranteed Pod,那么为了保证节点的稳定性并下降异常请求对其余 Guaranteed Pod 的影响,必须选择一个 Guaranteed Pod 进行驱逐。

本地磁盘是一个 BestEffort 资源。若有必要,kubelet 会在 DiskPressure 的状况下,kubelet 会按照 QoS 进行评估。若是 Kubelet 断定缺少 inode 资源,就会经过驱逐最低 QoS 的 Pod 的方式来回收 inodes。若是 kubelet 断定缺少磁盘空间,就会经过在相同 QoS 的 Pods 中,选择消耗最多磁盘空间的 Pod 进行驱逐。


有 Imagefs

  • 若是 nodefs 触发了驱逐,Kubelet 会用 nodefs 的使用对 Pod 进行排序 – Pod 中全部容器的本地卷和日志。
  • 若是 imagefs 触发了驱逐,Kubelet 会根据 Pod 中全部容器的消耗的可写入层进行排序。

没有 Imagefs

  • 若是 nodefs 触发了驱逐,Kubelet 会对各个 Pod 的全部容器的整体磁盘消耗进行排序 —— 本地卷 + 日志 + 写入层。
  • 在某些场景下,驱逐 Pod 可能只回收了不多的资源。这就致使了 kubelet 反复触发驱逐阈值。另外回收资源例如磁盘资源,是须要消耗时间的。
  • 要缓和这种情况,Kubelet 可以对每种资源定义 minimum-reclaim。kubelet 一旦发现了资源压力,就会试着回收至少 minimum-reclaim 的资源,使得资源消耗量回到指望范围。

例以下面的配置:

--eviction-hard=memory.available<500Mi,nodefs.available<1Gi,imagefs.available<100Gi

--eviction-minimum-reclaim="memory.available=0Mi,nodefs.available=500Mi,imagefs.available=2Gi"
  • 若是 memory.available 被触发,Kubelet 会启动回收,让 memory.available 至少有 500Mi。
  • 若是是 nodefs.available,Kubelet 就要想法子让 nodefs.available 回到至少 1.5Gi。
  • 而对于 imagefs.available, kubelet 就要回收到最少 102Gi。

缺省状况下,全部资源的 eviction-minimum-reclaim 为 0。

在节点资源紧缺的状况下,调度器将再也不继续向此节点部署新的 Pod

节点 OOM 时

若是节点在 Kubelet 可以回收内存以前,遭遇到了系统的 OOM (内存不足),节点就依赖 oom_killer 进行响应了。

kubelet 根据 Pod 的 QoS 为每一个容器设置了一个 oom_score_adj 值。

image

若是 kubelet 没法在系统 OOM 以前回收足够的内存,oom_killer 就会根据根据内存使用比率来计算 oom_score,得出结果和 oom_score_adj 相加,最后得分最高的 Pod 会被首先驱逐。

跟 Pod 驱逐不一样,若是一个 Pod 的容器被 OOM 杀掉,他是可能被 kubelet 根据 RestartPolicy 重启的。

Daemonset 的处理

由于 DaemonSet 中的 Pod 会当即重建到同一个节点,因此 Kubelet 不该驱逐 DaemonSet 中的 Pod。

可是目前 Kubelet 没法分辨一个 Pod 是否由 DaemonSet 建立。若是Kubelet 可以识别这一点,那么就能够先从驱逐候选列表中过滤掉 DaemonSet 的 Pod。

通常来讲,强烈建议 DaemonSet 不要建立 BestEffort Pod,而是使用 Guaranteed Pod,来避免进入驱逐候选列表。

已知问题

Kubelet 没法及时监测到内存压力

Kubelet 目前从 cAdvisor 定时获取内存使用情况统计。若是内存使用在这个时间段内发生了快速增加,Kubelet 就没法观察到 MemoryPressure,可能会触发 OOMKiller。咱们正在尝试将这一过程集成到 memcg 通知 API 中,来下降这一延迟,而不是让内核首先发现这一状况。

若是用户不是但愿得到终极使用率,而是做为一个过量使用的衡量方式,对付这一个问题的较为可靠的方式就是设置驱逐阈值为 75% 容量。这样就提升了避开 OOM 的能力,提升了驱逐的标准,有助于集群状态的平衡。

Kubelet 可能驱逐超出须要的更多 Pod

这也是由于状态搜集的时间差致使的。将来会加入功能,让根容器的统计频率和其余容器分别开来(https://github.com/google/cad...)。

Kubelet 如何在 inode 耗尽的时候评价 Pod 的驱逐

目前不可能知道一个容器消耗了多少 inode。若是 Kubelet 觉察到了 inode 耗尽,他会利用 QoS 对 Pod 进行驱逐评估。在 cadvisor 中有一个 issue,来跟踪容器的 inode 消耗,这样咱们就能利用 inode 进行评估了。例如若是咱们知道一个容器建立了大量的 0 字节文件,就会优先驱逐这一 Pod

最佳实践

资源预留

一、资源预留须要设置,pod 的 limit 也要设置。
二、cpu是可压缩资源,内存、磁盘资源是不可压缩资源。内存必定要预留,CPU能够根据实际状况来调整
三、预留多少合适:根据集群规模设置阶梯,以下(GKE建议):

Allocatable = Capacity - Reserved - Eviction Threshold

对于内存资源:

  • 内存少于1GB,则设置255 MiB
  • 内存大于4G,设置前4GB内存的25%
  • 接下来4GB内存的20%(最多8GB)
  • 接下来8GB内存的10%(最多16GB)
  • 接下来112GB内存的6%(最高128GB)
  • 超过128GB的任何内存的2%
  • 在1.12.0以前的版本中,内存小于1GB的节点不须要保留内存

对于 CPU 资源:

  • 第一个核的6%
  • 下一个核的1%(最多2个核)
  • 接下来2个核的0.5%(最多4个核)
  • 4个核以上的都是总数的0.25%

对于磁盘资源(不是正式特性,仅供参考):

image

效果:查看节点的可分配资源:

kubectl describe node [NODE_NAME] | grep Allocatable -B 4 -A 3

驱逐配置

--eviction-hard=memory.available<5%,nodefs.available<10%,imagefs.available<10%

--eviction-soft=memory.available<10%,nodefs.available<15%,imagefs.available<15%

--eviction-soft-grace-period=memory.available=2m,nodefs.available=2m,imagefs.available=2m

--eviction-max-pod-grace-period=30

--eviction-minimum-reclaim=memory.available=0Mi,nodefs.available=500Mi,imagefs.available=500Mi

原文连接:http://www.xuyasong.com/?p=1725

Reference

相关文章
相关标签/搜索