kubernetes Pod 驱逐机制

2019-07-19

kubernetes Pod 驱逐机制

作者: 张首富
时间: 2019-06-26
个人博客: www.zhangshoufu.com
QQ群: 895291458

在kubernetes中,pod使用的资源最重要的是CPU、内存和磁盘IO,这些资源大致可被分为两类:
* 1,可被压缩资源:如cpu
* 2,不可被压缩资源:如内存和磁盘IO(内存和磁盘IO对于一个节点来说是固定的)

可压缩资源不可能导致Pod被node驱逐,因为当Pod的cpu使用量很多时,系统可以通过重新分配权重来限制Pod的Cpu使用率,而对于不可压缩资源来说,如果资源不足,就无法继续申请资源(内存用没有了就是没有了,除非关机扩内存),此时kubernetes会从该节点上驱逐一定数量的Pod,以保证该节点有充足的资源。

当不可压缩资源不足时,kubernetes是通过kubelet来驱逐Pod的,kubelet也不是随机驱逐的,他也有自己的一套驱逐规则,每个计算节点的kubelet都会通过抓取cAdvisor的指标来监控节点的资源使用量。

名词解释

在阅读下面内容的时候,我们首先要明白Qos是什么东西?
QosQuality of Service 又称 服务质量等级,当我们创建一个Pod的时候,他会给这个Pod分配一个Qos,
Qos分为三个级别:从高到低排列如下
* Guaranteed:我们为pod指定了limit和request,并且这两个值相等
* Burstable: 我们为Pod指定了limit和request,但是这个两个值并不相等,request小于limit的值(不可以大于)
* BestEffoe:我们未指定limit和request

存储资源不足(磁盘)

kubelet默认的节点存储的驱逐触发条件:
* nodefs.availables < 10%(容器volume使用的文件系统的可用空间,包括文件系统剩余大小和inode数量)
* imagefs.available < 15%(容器镜像使用的文件系统的可用空间,包括文件系统剩余大小和inode数量)

imagesfs.available量达到触发阀值时,kubelet会尝试删除不使用的镜像来清理磁盘空间。
nodefs.available量达到阀值时,kubelet就会拒绝在该节点上运行新的Pod,并像API Server注册一个Disk Pressure Condition。然后kubelet会尝试删除死亡的Pod和容器来回收磁盘空间,如果此时nodefs使用量仍然没有低于阀值,kubelet就会开始驱逐Pod,从 Kubernetes 1.9 开始,kubelet 驱逐 Pod 的过程中不会参考 Pod 的 QoS,只是根据 Pod 的 nodefs 使用量来进行排名,并选取使用量最多的 Pod 进行驱逐。所以即使 QoS 等级为 Guaranteed 的 Pod 在这个阶段也有可能被驱逐(例如 nodefs 使用量最大)。如果驱逐的是 Daemonsetkubelet 会阻止该 Pod 重启,直到 nodefs 使用量超过阈值。

如果一个 Pod 中有多个容器,kubelet 会根据 Pod 中所有容器的 nodefs 使用量之和来进行排名。即所有容器的 container_fs_usage_bytes 指标值之和。

举个例子,假设某计算节点上运行着一系列已知 QoS 等级和 nodefs 使用量的 Pod:
image.png
当 nodefs 的使用量超过阈值时,kubelet 会根据 Pod 的 nodefs 使用量来对 Pod 进行排名,首先驱逐使用量最多的 Pod。排名如下图所示:
image.png

可以看到在本例中,QoS 等级为 Guaranteed 的 Pod 最先被驱逐。 所以当磁盘满了是不会看你的Qos服务等级的,其实我们也可以想象出来,limit和request都没有磁盘这一项。

内存资源不足

下面是 kubelet 默认的关于节点内存资源的驱逐触发条件:
* memory.available<100Mi

当内存使用量超过阈值时,kubelet 就会向 API Server 注册一个 MemoryPressure condition,此时 kubelet 不会接受新的 QoS 等级为 Best Effort 的 Pod 在该节点上运行,并按照以下顺序来驱逐 Pod:
* Pod 的内存使用量是否超过了 request 指定的值;
* 根据 priority 排序,优先级低的 Pod 最先被驱逐;
* 比较它们的内存使用量与 request 指定的值之差。

按照这个顺序,可以确保 QoS 等级为 Guaranteed 的 Pod 不会在 QoS 等级为 Best Effort 的 Pod 之前被驱逐,但不能保证它不会在 QoS 等级为 Burstable 的 Pod 之前被驱逐。

如果一个 Pod 中有多个容器,kubelet 会根据 Pod 中所有容器相对于 request 的内存使用量与之和来进行
排名。即所有容器的(container_memory_usage_bytes 指标值与 container_resource_requests_memory_bytes 
指标值的差)之和。

假设某计算节点上运行着一系列已知 QoS 等级和内存使用量的 Pod:
image.png

当节点的内存使用量超过阈值时,kubelet 会根据 Pod 相对于 request 的内存使用量来对 Pod 进行排名。排名如下所示:
image.png

可以看到在本例中,可以看到在本例中,QoS 等级为 Guaranteed 的 Pod 在 QoS 等级为 Burstable 的 Pod 之前被驱逐。

当内存资源不足时,kubelet 在驱逐 Pod 时只会考虑 requests 和 Pod 的内存使用量,不会考虑 limits。

Node OOM (Out Of Memory)

因为 kubelet 默认每 10 秒抓取一次 cAdvisor 的监控数据,所以有可能在 kubelet 驱逐 Pod 回收内存之前发生内存使用量激增的情况,这时就有可能触发内核 OOM killer。这时删除容器的权利就由kubelet 转交到内核 OOM killer 手里,但 kubelet 仍然会起到一定的决定作用,它会根据 Pod 的 QoS 来设置其 oom_score_adj 值:
image.png

如果该节点在 kubelet 通过驱逐 Pod 回收内存之前触发了 OOM 事件,OOM killer 就会采取行动来降低系统的压力,它会根据下面的公式来计算 oom_score 的值:

容器使用的内存占系统内存的百分比 + oom_score_adj = oom_score

OOM killer 会杀掉 oom_score_adj 值最高的容器,如果有多个容器的 oom_score_adj 值相同,就会杀掉内存使用量最多的容器(其实是因为内存使用量最多的容器的 oom_score 值最高).关于 OOM 的更多内容请参考: Kubernetes 内存资源限制实战。

假设某节点运行着 4 个 Pod,且每个 Pod 中只有一个容器。每个 QoS 类型为 Burstable 的 Pod 配置的内存 requests 是 4Gi,节点的内存大小为 30Gi。每个 Pod 的 oom_score_adj 值如下所示:
image.png

当调用 OOM killer 时,它首先选择 oom_score_adj 值最高的容器(1000),这里有两个容器的 oom_score_adj 值都是 1000,OOM killer 最终会选择内存使用量最多的容器。

总结:

  • 因为 kubelet 默认每 10 秒抓取一次 cAdvisor 的监控数据,所以可能在资源使用量低于阈值时,kubelet 仍然在驱逐 Pod。
  • kubelet 将 Pod 从节点上驱逐之后,Kubernetes 会将该 Pod 重新调度到另一个资源充足的节点上。但有时候 Scheduler 会将该 Pod 重新调度到与之前相同的节点上,比如设置了节点亲和性,或者该 Pod 以 Daemonset 的形式运行。

想法:

1, 我们可以尽量的给Pod赋予limitrequest,提高他的服务优先级
2, 我们通过自定义更改磁盘满之后的驱逐策略,让他驱逐之前判断下Qos,Qos高的说明是我们重要的应用,我们不该因为磁盘满了就把我们重要的业务给驱逐了

参考:

https://www.yangcs.net/posts/kubernetes-eviction/
https://github.com/google/cadvisor
https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/


标题:kubernetes Pod 驱逐机制
作者:shoufuzhang
地址:https://www.zhangshoufu.com/articles/2019/07/19/1563529639064.html
名言:The master has failed more times than the beginner has tried.
评论
发表评论