Kubernetes 的资源管理

作者:Kim Wuestkamp

翻译:Bach(才云)

校对:bot(才云)、星空下的文仔(才云)

在生产环境中使用 Kubernetes 之前,我们应该了解 K8s 的资源管理,资源管理的核心就是 Kubernetes 调度器处理资源请求和限制。

K8sMeetup

Pod 资源

在 K8s 中,一个 Pod 可以包含一个或多个容器,这些容器通常由 Docker 运行。

Kubernetes 的资源管理

Pod 可以看做是容器的外包装,这二者会在同一台机器或节点上运行。一般来说,总的 Pod 资源就等于容器资源的总和。

资源请求和限制

为每个容器制定资源请求和限制,例如:

Kubernetes 的资源管理

请求是保证得到的资源,其他 Pod 不可以使用这些资源。限制是允许使用比请求更多的资源。如果容器达到规定的限制,会被 CPU 超售并驱逐出内存。

K8sMeetup

调度器

K8s 调度器负责确定 Pod 可以在哪个节点上运行,它通过查看各种配置(例如亲和度、taints、tolerations)来实现。这里我们仅关注主要配置:闲置资源(free resources)。当调度器做出有关“闲置资源”的决定时,它会查看两个数据:Node Allocatable 和 Node Requests。

节点的 Allocatable 与容量

调度器会查看节点的 Allocatable,它是整个节点减去 Kubelet 的保留资源。

Kubernetes 的资源管理

我们可以这样查看 Allocatable 和容量:kubectl get node worker1 -oyaml。

重要的是,无论节点上运行多少个 Pod,这些数字都不会改变。只要节点通过 kubelet注册,它们就是固定不变的。

根据调度器释放资源

free = node allocatable — sum(all pod resource requests)

这意味着调度器实际上不会直接查看节点或 Pod 的 CPU 和内存使用情况,而只会考虑已请求的内容。

K8sMeetup

调度器启动

让我们看一下调度器决策工作流程示例,其中有两个节点,它们在刚启动时为空。

Kubernetes 的资源管理

蓝色 Pod1 有资源请求,在节点1 上调度

Kubernetes 的资源管理

绿色 Pod 在节点 2 上调度

Kubernetes 的资源管理

紫色 Pod 在节点 1 上调度

Kubernetes 的资源管理

调度器尝试调度节点 2 上的红色 Pod

Kubernetes 的资源管理

但是没有更多可用资源,因此处于 Pending 状态

现在我们再看一下上面的图像,其中 node1 有两个 Pod,node2 有一个 Pod 在运行。节点上显示的所有颜色均表示资源请求,但没有限制,因为调度器会忽略它们。现在我可以进一步理解低利用率(slack)和过度使用(overcommitment)。

K8s 中的低利用率(slack)和过度使用(overcommitment)

下图显示了示例指标,以可视化低利用率和过度使用。

Kubernetes 的资源管理

下面我们将更详细地讨论此示例指标和其他指标,但首先,我们要更加深入研究低利用率和过度使用以彻底了解这些内容。

低利用率(slack)

浪费的资源成为了低利用率。K8s 中的低利用率指请求了但未使用的资源。我们先看下面的例子:

Kubernetes 的资源管理

在这里,我们看到 node1 上调度了两个 Pod。蓝色的所有内容均显示对 Pod1 的资源请求,但是 Pod1 实际上只使用了它所请求内容的 1/3,这意味着其余部分闲置了。出现这种情况是因为 K8s 调度器不查看实际的使用情况,它仅查看指定的请求值。

过度使用(overcommitment)****

过度使用意味着,如果所有节点都开始使用资源,节点上会安排更多的 Pod。但只要不是所有的 Pod 同时在消耗所有的可用资源,它就可以起作用。让我们看一个例子:

Kubernetes 的资源管理

在这里,我们看到蓝色 Pod 的 CPU 使用率实际上已经超出了请求,这意味着它使用了过度使用的资源。这可能是因为我们指定的限制高于要求。

但是 K8s 调度程序只会查看请求,还会认为仍然有足够的可用资源,并可能在该节点上调度新的 Pod。

CPU 过度使用

只要过度使用不超过节点的容量,CPU 过度使用就不会有问题。让我们来看看这种情况:

Kubernetes 的资源管理

实际使用量超出容量,导致 CPU 超售

在这种情况下,节点上的两个 Pod 都将被驱逐,每个 Pod 都可以调回其请求的值。CPU 过度使用一般不会造成太严重的后果,因为它只会导致。

但是即使在这种情况下,k8s 调度器仍可能认为 node1 有足够的“空闲 CPU”可用于另一个 Pod,因为 K8s 调度器只会查看请求。

内存过度使用

内存会有点不同,因为它无法像 CPU 一样被限制。它不是基于时间的值,它包含了无法缩小或扩展的实际数据。

Kubernetes 的资源管理

实际使用量超出容量,导致 Pod 被驱逐、杀死或 OOM 异常

Kubelet 是在 VM 上运行并通过与 K8s API 通信将其注册为节点的进程。之后,它会从 API 接收 Pod 规范以运行。

Kubelet 具有一些处理 OOM 情况的机制,如上图所示。它可以注意到 Pod 是否开始使用超出要求的内存,是否很快用完,这样它可以根据优先级驱逐 Pod。

但是,我们无法保证 kubelet 有足够的时间注意到这一点并驱逐 Pod。节点本身可能就会遇到内存不足的问题,操作系统本身还会开始杀死随机进程(源)。

这就是不允许内存过度使用的原因,所以最好始终设置 requests=limits。

基于优先级的 Kubelet Pod 驱逐

Kubelet 考虑了 QoS(服务质量)和优先级,来决定驱逐哪些 Pod 以释放资源。

同时考虑 QoS 和 Pod 优先级的唯一组件是 kubelet out-of-resource eviction。kubelet 首先判断 Pod 的资源使用量是否超过请求,然后通过优先级,计算 Pod 调度请求消耗的计算资源,最后将 Pod 逐出。

K8sMeetup

CPU 限制导致不必要的 CPU 超售

如果我们为 Pod 设置了任何 CPU 限制,那么即使使用量不接近限制,它也会受到限制。就算 Pod 的使用量为 200 millicpu,限制为 1000 millicpu,它仍可能会受到限制并导致延迟或性能问题。

因此,建议不定义 CPU 限制或通过禁用 kubelet 中的 CPU 限制 --cpu-cfs-quota=false。

那么如何防止 Pod 使用过多的 CPU?

  • 监视 Pod 的使用情况,如果使用量超出请求,就创建警报。

  • 通过 HPA/VPA 实现扩展。

K8sMeetup

设置适当的请求和限制

让我们看一些场景,并讨论它们的优劣。这里只是一般性观察,对于特定用例可能有所不同。

场景 1

我们有低利用率并允许过度使用,这不好。

Kubernetes 的资源管理

场景 2

我们没有低利用率,但使用了过度使用的资源,这也不是很好。

Kubernetes 的资源管理

比较接近最佳方案

我们为资源设置了 requests=limits。对于 CPU,我们可以在 Kubelets 中禁用限制执行(cpuCfsQuota=false)。

Kubernetes 的资源管理

对于 CPU,没必要设置限制

更现实情况

现实使用情况永远不会很好,还会不断地变化。在这种情况下,我们仍应确保使用率始终低于我们的要求。为了可预测性和稳定性,这将导致低利用率。

Kubernetes 的资源管理

通过缩放减少低利用率

为了抚平曲线并使之保持在要求之下,我们可以使用 HPA 这样的伸缩。一旦注意到使用量趋向于请求和限制,它将创建更多实例来分散负载。

Kubernetes 的资源管理

上图显示了单个 Pod 的资源使用情况。如果创建更多副本,那么所有 Pod 使用的资源总数将增加。使用 HPA,可以将单个 Pod 的使用曲线抚平,从而可以更有效地进行调度。

Pod 在初始化期间会使用更多资源

这是最难的一个问题,如果 pod 在初始化期间使用更多资源,那就将请求设置为较高的初始化使用率,但这在后续运行期间会导致较高的延迟。因此,在这种情况下允许 CPU 过度使用是可以的,但不允许使用内存。

有人认为应该可以将 init-logic 移到 initContainer 中,该容器在实际应用程序容器之前运行,如下所示:

Kubernetes 的资源管理

总的 Pod 资源请求会是 initContainer 资源请求

但是,这实际上并没有降低 Pod 的总资源请求,因为调度计算这样的:

pod requests = max(max(initContainers), sum(containers))

K8sMeetup

VPA

HPA(Pod 水平自动伸缩)非常适合无状态(stateless)和现代(modern)服务。但是如果是有状态(stateful)服务或整体(monolithic)服务,可能就没那么容易了。

VPA(Pod 垂直自动伸缩)可以调整正在运行的 Pod 的资源请求,无需创建更多副本,它也可以用于推荐最佳请求值。

K8sMeetup

节点压力

本文中经常提到,K8s 调度程序不会查看节点上的实际资源使用情况,仅查看节点 Allocatable 和 Pod 请求。但一个节点可以给自己类似 MemoryPressure 或 DiskPressure 的 conditions。如果节点具有此 conditions,调度程序就会将其视为非最佳候选者。但有一点需要注意,如果没有 CpuPressure conditions,也就没有基于 CPU 的容器驱逐。

K8sMeetup

为什么会有资源限制?

为了获得最佳的可预测性和安全性,最好永远不要过度使用 requests=limits,那为什么还会存在限制呢?

简单来说

虽然我们目前不知道实际的用途,但有了广泛的请求和限制后,开始开发应用程序变得更加容易。

优化资源使用

从理论上讲,让非关键 Pod 利用过度使用来缓解低利用率是一个好主意。如果节点可以承受压力(QoS),那么过度使用的 Pod 将与第一个容器一起被驱逐。

如果 kubelet 在将来可以完美预测 OOM 问题并驱逐正确的 Pod,那么允许内存过度使用可能会可行。

K8sMeetup

结论

深入了解 K8s 管理资源可能非常复杂。通常,为了获得最佳的可预测性和稳定性,我们总是让使用量低于要求。一旦应用在生产环境中稳定运行,我们就可以尝试优化。

原文地址:https://mp.weixin.qq.com/s/NnutG4dDnm2zWY5MDiSGpQ

猜你喜欢

转载自blog.51cto.com/14133165/2518733