kubelet 是 Kubernetes 中主要节点组件,执行许多关键任务,特别是:

  • 用 kube-apiserver 注册节点。
  • 监视 kube-apiserver 中已经调度的 Pod,通知容器运行时在调度新 Pod 之后重新启动容器。
  • 监视运行中的容器并将其状态报告给 kube-apiserver。
  • 执行 liveness 探针并在容器运行失败后重新启动容器。
  • 运行由 kubelet 直接管理的静态 Pod。
  • 与 Core Metrics Pipeline 和容器运行时进行交互以收集容器和节点度量。

本文中要讨论的是 kubelet 另一个重要功能:当节点资源耗尽时,“主节点代理”驱逐 Pod 的能力。当磁盘、RAM 或 CPU 等计算资源不足时,kubelet 可以极大维护节点稳定性。对于 Kubernetes 管理员而言,了解配置资源外的处理很必要,可以保持节点资源灵活性以及系统的整体容错性和关键系统进程的稳定性。

kubelet 如何确定资源不足?

kubelet 可以从节点上驱逐工作负载,以释放资源来处理其他 Pod 或系统任务,例如容器运行时或 kubelet 本身,但 kubelet 是如何确定资源不足的?

其实 kubelet 是通过 eviction signal(驱逐信号)和 eviction threshold(驱逐阈值)确定何时回收资源。驱逐信号是系统资源(如内存或存储)的当前容量。驱逐阈值则是 kubelet 维护的资源最小值。换句话说,每个驱逐信号都与某个驱逐阈值相关联,该驱逐阈值会告诉 kubelet 何时开始回收资源。目前,kubelet 支持以下驱逐信号:

  • memory.available:描述集群内存状态的信号。内存的默认驱逐阈值为 100Mi。换句话说,当内存下降到 100Mi 时,kubelet 就会开始驱逐 Pod。
  • nodefs.availablenodefs 是 kubelet 用于卷、守护进程日志等的文件系统。默认情况下,如果 nodefs.available<10%,kubelet 将开始回收节点资源。
  • nodefs.inodesFree:描述 nodefs 索引节点内存状态的信号。默认情况下,如果 nodefs.inodesFree<5%,kubelet 将开始驱逐工作负载。
  • imagefs.available—imagefs:文件系统是容器运行时使用的可选文件系统,用于存储容器镜像和容器可写层。默认情况下,如果 imagefs.available<15%,kubelet 将开始逐出工作负载。
  • imagefs.inodesFree:索引 imagefs 节点内存的状态。它没有默认驱逐阈值。

上述驱逐阈值都是合理的默认值。用户可以通过在 kubelet binary 上设置适当的 flag 来配置其自定义驱逐阈值。这些用户定义的阈值可以更改默认的 kubelet 驱逐行为。

目前,Kubernetes 支持硬驱和软驱逐阈值。

如果达到硬驱逐阈值,kubelet 将立即开始回收资源,而没有任何宽限期。软驱逐阈值则会包含用户定义的宽限期。在超过宽限期前,kubelet 不会回收与驱逐信号关联的资源。

我们可以使用 kubelet binary 上的 --eviction-hardkubelet flag 定义硬驱逐阈值。例如,kubelet —-eviction-hard=memory.available<1Gi 当节点的 memory.available 大小低于 1Gi 时,kubelet 将开始回收资源。

如果要在驱逐之前允许宽限期,可以将 —-eviction-soft 与 —-eviction-soft-grace-period 结合使用。例如,kubelet —-eviction-soft=memory.available<2Gi 以及 kubelet —-eviction-soft-grace-period=1m30s 能将 90 秒的驱逐阈值保持在触发驱逐前。

用户还可以通过设置 —-eviction-max-pod-grace-period 秒数来指定允许的最大宽限期。

kubelet 如何回收资源?

kubelet 会通过终端用户的 Pod 来回收资源,但它首先会尝试回收未使用的容器镜像或已经终止的 Pod 这类资源。

如果节点具有特定 imagefs 文件系统和 nodefs 文件系统,kubelet 会以不同的方式回收节点资源。当 nodefs 达到驱逐阈值时,kubelet 会删除所有已经终止的 Pod 及其容器。相应地,如果 imagefs 达到驱逐阈值,kubelet 会删除所有未使用的容器镜像。

如果没使用 imagefs,kubelet 将首先删除所有已经终止的 Pod 及其容器,然后删除所有未使用的镜像。有关这一过程的详细信息,请参阅 Kubernetes 文档。

文档信息:https://kubernetes.io/docs/tasks/administer-cluster/out-of-resource/

如果回收容器镜像、已经终止的 Pod 和其他资源没有解决资源匮乏问题,kubelet 最后会开始删除终端用户的 Pod。kubelet 根据 Pod 的 QoS 类、Pod 优先级和许多其他参数来决定回收哪个终端用户的 Pod。这里先介绍一下 Kubernetes 中的基本 QoS 类。

  • Guaranteed pod 是在所有容器中为 CPU 和 RAM 设置资源限制(limit)和请求(request)的 Pod,限制和请求必须一致。
  • Burstable Pod 是为一个或多个容器中为资源(例如CPU、RAM)设置请求和限制的容器,限制和请求不用明确指定。
  • Best-Effort Pod 是未设置资源请求和限制的 Pod。

该 QoS 由 kubelet 在其 Pod 排序方案中隐式使用。通常,kubelet 使用以下规则对驱逐进行排序:

  • Pod 是否已超出其资源请求。在 Kubernetes 中,Pod 是根据其请求(request)而不是限制(limit)进行调度的。因此,要保证所有容器和 Pod 都具有它们所请求的 RAM、CPU 数量。但是,如果未设置限制,并且 Pod 超出了其资源请求,那么在保证 Pod 或某些系统任务需要受限资源的情况下,可以终止或限制该 Pod。在某些情况下,某些消耗少于要求量的 Pod 也会被杀死。例如,当系统任务内存严重不足并且没有较低优先级的 Pod 被回收时。
  • 按 Pod 优先级。如果没有 Pod 超出其请求,kubelet 会检查 Pod Priority。它将尝试先驱逐优先级较低的 Pod。
  • 相对于 Pod 的资源请求消耗量计算资源(例如 RAM)。

根据这些规则,kubelet 会按以下顺序逐出终端用户的 Pod:

  • 首先驱逐是受限资源的使用超出了请求的 Best-Effort 和 Burstable Pod。如果有多个这样的 Pod,kubelet 会按优先级对它们进行排序。
  • 最后是资源低于请求的 Guaranteed 和 Burstable Pod。如果某些系统任务(如 kubelet 或 Docker)需要资源,并且节点上 Best-Effort Pod,kubelet 可以驱逐消耗量低于请求的 Guaranteed Pod。在这种情况下,它会以最低优先级驱逐 Guaranteed 和 Burstable Pod。

最低驱逐回收

如果 kubelet 回收的资源量很小,系统可能反复达到驱逐阈值,这可能导致会不良的调度决策和 Pod 频繁驱逐。为了避免这种情况,用户可以使用 kubelet binary —-eviction-minimum-reclaimkubelet 的 flag 来设置每个资源的最小回收级别。例如:

—-eviction-minimum-reclaim 设置确保 nodefs 回收后的最小可用存储量为 3Gi,imagefs最小可用存储量为 202 Gi。因此,以上配置可确保系统具有足够的可用资源,以避免频繁达到驱逐阈值。资源不足以处理配置还会遇到另一个问题:节点状态(condition)的波动。当 kubelet 收到驱逐信号后,后者会映射到相应的节点状态。例如,memory.available 达到驱逐阈值时,kubelet 会将 MemoryPressure 节点状态分配给该节点。此状态与相应的污点(taint) 关联,该污点可防止在具有 MemoryPressure 节点状态的节点上调度新 Pod。

但如果使用了具有较长宽限期的软驱逐阈值,节点条件会在宽限期 true 和 false 之间振荡。这可能导致调度计划的不确定性。为了避免这种情况,我们可以在 kubelet 上使用 —-eviction-pressure-transition-period,以定义 kubelet 在满足驱逐条件之前必须等待多长时间。

简单 Out-of-Resource 处理方案

以下将说明如何处理 K8s 集群配置资源不足。想象一个简单的场景,仅考虑节点 RAM。假设节点的内存容量为 10Gi RAM。我们希望为系统守护进程(例如内核、kubelet、Docker 等)保留 10% 的总内存并以 95% 的内存利用率驱逐 Pod。

使用默认驱逐阈值启动 kubelet,并且没有 system-reserved 设置。我们需要在 kubelet 上显式设置几个 flag:

虽然直观上应该将 system-reserved 设置为 1.5Gi,但实际将其设置为 10%=1Gi。System reserved 应包括驱逐阈值(1Gi+0.5Gi)覆盖的内存量。

根据配置 K8s 集群的方式,可以不同地设置 kubelet flag。例如,如果计划使用 Kops 设置 K8s 集群,请运行 kops edit cluster $NAME 以使用集群配置打开编辑器。如果是 VI 辑器,则按 “I” 进入插入模式以编辑文件。上述资源不足处理策略的 kubelet flag 应如下所示:

结论

本文我们讨论了一些有用的 Kubernetes 管理实践,用于 Kubernetes 中自定义 kubelet 资源不足管理。该平台允许管理员设置自定义驱逐阈值和驱逐宽限期,能决定哪些条件对节点稳定性有用。Kubernetes 附带了资源不足管理默认设置,在将驱逐阈值设置得太高或将驱逐宽限期设置得太长时,要保持谨慎。

原文链接:https://medium.com/kubernetes-tutorials/efficient-node-out-of-resource-management-in-kubernetes-67f158da6e59