1. Requests 和 Limits 对不同计算机资源类型的限制机制

容器的资源配置满足以下两个条件:

  • Requests ≤ 节点可用资源
  • Requests ≤ Limits

1) 可压缩资源

  • Kubernetes 目前支持的可压缩资源是 CPU。
  • Pod 可以得到 Requests 配置的CPU 使用量,而能否使用超过 Requests 值的部分取决于系统的负载和调度。
  • 空闲的 CPU 资源按照容器 Requests 值的比例分配。比如容器 A 的 CPU 配置为 Requests 1 Limits 10,容器 B 的 CPU 配置为 Requests 2 Limits 8。初始状态下容器可以用的 CPU 为 3cores,那么 A 和 B 恰好得到再其 Requests 中定义的 CPU 用量,即 1CPU 和 2CPU。如果 A 和 B都需要更多的 CPU 资源,而恰好此时系统的其他任务释放了 1.5CPU,那么这 1.5CPU 将按照 A 和 B 的 Requests 值的比例 1:2 分配给 A 和 B,即最终 A 可使用 1.5CPU,B 可使用 3CPU。
  • 如果 Pod 的 CPU 用来超过了在 Limits 10 中配置的 CPU 用量,那么 cgroups 会对 Pod 中容器的 CPU 用来进行限流(Throttled);如果 Pod 没有配置 Limits 10,那么 Pod 会尝试抢占所有空间的 CPU 资源。

2) 不可压缩资源

  • Kubernetes 目前支持的不可压缩资源是内存。
  • Pod 可以得到在 Requests 中配置的内存。
    • 如果 Pod 的内存用量小于它的 Requests 的配置,那么这个 Pod 可以正常运行(除非出现操作系统级别内存不足等严重问题)。
    • 如果 Pod 的内存用量超过它的 Requests 的配置,那么这个 Pod 有可能会被 Kubernetes “杀掉”:
      • 比如 Pod A 使用的内存量在 Requests ~ Limits 之间,同一机器上的另一个 Pod B 之前使用的内存小于 Requests 的值,此时 Pod B 压力增大,Pod B 向系统申请的总量不超过自己的 Requests值的内存,那么 Pod A 可能会直接被 Kubernetes “杀掉”。
      • 另一种情况是 Pod A 使用的内存量在 Requests ~ Limits 之间,此时 Kubernetes 将一个新的 Pod 调度到这台机器上,只有 Pod A 使用量超过自己的 Requests 值的内存,那么也可能会直接被 Kubernetes “杀掉”。
  • 如果 Pod 的内存用量超过它的 Limits 值,那么操作系统内核会“杀掉”Pod 所有容器的所有进程中内存使用量最多的一个,直到内存不超过 Limits 时为止。

2. 对调度策略的影响。

  • Kubernetes 的 kube-scheduler 通过计算 Pod 中所有容器的 Requests 总和来决定对 Pod 的调度。
  • 不管是 CPU 还是内存,Kubernetes 调度器和 kubelet 都会确保节点上的所有 Pod 的 Requests 总和不会超过在该节点上可分配给容器使用的资源容量上限。

3. 服务质量等级(QoS Classes)

在一个超用(Over Committed,容器 Limits 总和大于系统容量上限)系统中,容器负载的波动可能导致操作系统的资源不足,最终导致部分容器被“杀掉”。Kubernetes 将容器划分成 3 个 QoS 等级:Guaranteed(完全可靠的)、Burstable(弹性波动、较可靠的)和 BestEffort(尽力而为、不太可靠的),这三种依次递减的优先级来决定优先“杀掉”哪些容器。

1) Guaranteed(完全可靠的)

如果 Pod 中的所有容器对所有资源类型都定义了 Limits 和 Requests,并且所有容器的 Limits 值都和 Requests 值相等(且都不为0),那么该 Pod 的 QoS 级别就是 Guaranteed。

注意:在这种情况下,容器可以不定义 Requests,因为 Requests 值在未定义时默认等于 Limits。

2) Burstable(弹性波动、较可靠的)

Burstable 级别的 Pod 涉及两种情况:

  1. Pod 中的一部分容器在一种或多种类型的资源配置中定义了 Requests 值和 Limits 值(都不为 0),且Requests 值小于 Limits 值。
  2. Pod 中的一部分容器未定义资源配置(Requests 和 Limits 都未定义)。

注意:容器未定义 Limits 时,Limits 值默认等于节点资源容量的上限。

3) BestEffort(尽力而为、不太可靠的)

如果 Pod 中所有容器都未定义资源配置(Requests 和 Limits 都未定义),那么该 Pod 的 QoS 级别就是 BestEffort。

4) Kubernetes QoS 的工作特点

在 Pod 的 CPU Requests 无法得到满足(比如节点的系统任务占用过多的 CPU 导致无法分配足够的 CPU 给容器使用)时,容器得到的 CPU 会被限流

由于内存是不可压缩的资源,所以针对内存资源紧缺的情况,会按照以下逻辑处理:

  1. BestEffort Pod 的优先级最低,在这类 Pod 中运行的进程会在系统内存紧缺时被第一优先“杀掉”。从另一个角度看,由于没有设置资源 Limits,所以在资源充足时它们可以充分利用所有闲置资源。
  2. Burstable Pod 的优先级居中,这类 Pod 在初始时会被分配较少的可靠资源,但可以按需申请更多资源。如果整个系统内存紧缺,又没有 BestEffort 容器可以被“杀掉”以释放资源,那么这类 Pod 中的进程可能会被“杀掉”。
  3. Guaranteed Pod 的优先级最高,而且一般情况下这类 Pod 只要不超过其资源 Limits 的限制就不会被“杀掉”。如果整个系统内存紧缺,又没有其他优先级更低的容器可以被“杀掉”以释放资源,那么这类 Pod 中的进程也可能会被“杀掉”。

5) OOM 计分规则

OOM(Out Of Memory)计分规则包括如下内容:

  • OOM 计分的计算方法:计算进程所使用的内存在系统中所占的百分比,取其中不含百分号的数值,再乘以10,该结果是进程 OOM 的基础分;将进程 OOM 基础分的分值再加上这个进程 OOM_SCORE_ADJ(分数调整)值,作为进程OOM的最终分值(除 root 启动的进程外)。在系统发生 OOM 时,OOM Killer 会优先“杀掉”OOM 计分更高的进程。
  • 进程的 OOM 计分的基本分数范围是 0 ~ 1000,如果 A 进程的调整值 OOM_SCORE_ADJ 减去 B 进程的调整值的结果大于 1000,那么 A 进程的 OOM 计分最终值必然大于 B 进程,会优先“杀掉”A 进程。
  • 不论调整 OOM_SCORE_ADJ 值为多少,任何进程的最终分值范围也是 0 ~ 1000。

在 Kubernetes 中,不同 QoS 的 OOM 计分调整值如表所示:

QoS oom_score_adj
Guaranteed -998
BestEffort 1000
Burstable min(max(2, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes), 999)

参考:《Kubernetes权威指南(第五版)》