kubernetes pod资源分配最佳实践

一、服务质量等级 QoS

  • Guaranteed

    pod内所有容器的cpu和内存都配置了request和limit的限额,并且都各自相等

  • Burstable

    至少一个容器配置了,且不满足Guaranteed要求

  • BestEffort

    Pod 里的容器必须没有任何内存或者 CPU的限额配置

二、驱逐 与 OOM_Killer(内存相关)

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

oom_socre_adj分数越高被kill的优先级越高

QoS oom_score_adj
BestEffort 1000
Burstable(2~999) min(max(2, 1000 - (1000 * 内存使用数) / 总内存数), 999)
Guaranteed -998
pod-infra-container -998
kubelet, docker daemon, systemd service -999

三、资源不足 vs 处理操作 vs 调度

kubelet 首先根据他们对短缺资源的使用是否超过请求来排除 pod 的驱逐行为,然后通过 优先级,然后通过相对于 pod 的调度请求消耗急需的计算资源。

kubelet 按以下顺序对要驱逐的 pod 排名:

  • BestEffortBurstable,其对短缺资源的使用超过了其请求,此类 pod 按优先级排序,然后使用高于请求。
  • Guaranteed pod 和 Burstable pod,其使用率低于请求,最后被驱逐。

1. DiskPressure

kubelet 默认的关于节点存储的驱逐触发条件

  • nodefs.available<10%(容器 volume 使用的文件系统的可用空间,包括文件系统剩余大小和 inode 数量)
  • imagefs.available<15%(容器镜像使用的文件系统的可用空间,包括文件系统剩余大小和 inode 数量)

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

2. MemoryPressure

kubelet 默认的关于节点内存资源的驱逐触发条件

  • memory.available<100Mi

当内存使用量超过阈值时,kubelet 就会向 API Server 注册一个 MemoryPressure condition,此时 kubelet 不会接受新的 QoS 为 Best Effort 的 Pod 在该节点上运行,并按照以下顺序来驱逐 Pod:

  • Pod 的内存使用量是否超过了 request 指定的值
  • 根据 priority 排序,优先级低的 Pod 最先被驱逐
  • 比较它们的内存使用量与 request 指定的值之差。
节点状况 调度行为
MemoryPressure 不再分配新的 BestEffort Pod 到这个节点(因为来新的BestEffort可能马上又被驱逐)
DiskPressure 不再向这一节点分配 Pod

四、驱逐方式与驱逐信号

驱逐方式

  • 硬驱逐

    直接触发驱逐

  • 软驱逐

    根据配置给到一个容忍时间,时间内恢复则不驱逐pod

驱逐信号

驱逐信号 描述
memory.available memory.available:=node.status.capacity[memory]-node.stats.memory.workingSet
nodefs.available nodefs.available:=node.stats.fs.available
nodefs.inodesFree nodefs.inodesFree:=node.stats.fs.inodesFree
imagefs.available imagefs.available:=node.stats.runtime.imagefs.available
imagefs.inodesFree imagefs.inodesFree:=node.stats.runtime.imagefs.inodesFree

上述每个值支持字面值或百分比。

举例说明,如果一个节点有 10Gi 内存,希望在可用内存下降到 1Gi 以下时引起驱逐操作,则驱逐阈值可以使用下面任意一种方式指定(但不是两者同时)。

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

五、分配方案

  1. 防止触发OOM-Killer(OOM-Killer是linux内核防护能力,不是最佳实践)

    比如kubelet启动命令作如下修整:

    1
    2
    3
    4
    # 指定内存低于500M时开始驱逐防止触发OOM
    --eviction-hard=memory.available<500Mi
    # 为系统守护进程(内核、kubelet等)保留一定的内存容量(这里的1.5G包含上面的500M,所以这里预留给系统守护进程的为1G)
    --system-reserved=memory=1.5Gi
  2. 因为DaemonSet 的Pod不应被驱逐,应确保DaemonSet的Pod声明为Guaranteed的QoS

  3. 理论上用户定义的Pod的QoS等级都应该大于BestEffort,同时应估算最大使用内存和磁盘,给出明确的requestlimit的定义