容器技术 k8s pod 调度浅析

269 阅读9分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天。

在 Kubernetes 中,将 pod 调度到集群中特定节点的任务由 kube-scheduler 完成. 该组件的默认行为是根据创建的 pod 中每个容器的资源请求和限制来过滤节点。然后对可用节点进行评分,以找到最适合放置 pod 的节点。

在很多场景下,基于资源约束调度 Pod 是一种理想的行为。但是,在某些用例中,特别是一些高级调度场景,Kubernetes 管理员希望根据其他约束将 Pod 调度到特定节点。在本文中,我将回顾 Kubernetes 中高级 pod 调度的一些场景,以及在实际情况中实现它的最佳实践。这对于希望实施高级应用程序部署模式的应用程序工程师和 K8s 管理员特别有帮助,这些模式涉及数据本地化、Pod 共存、高可用性和 K8s 集群的资源高效利用。

1手动将 Pod 调度到节点的场景

在生产 Kubernetes 设置中,自定义如何将 Pod 调度到节点是非常重要的。如下是一些最常见的最常见的调度场景:

  • 在具有专用硬件的节点上运行 Pod:一些 Kubernetes 应用程序可能有特定的硬件要求。例如,运行 ML 作业的 Pod 需要高性能 GPU 而不是 CPU,而 Elasticsearch Pod 在 SSD 上比 HDD 更高效。因此,任何资源感知型 K8s 集群管理的最佳实践是将 pod 分配给具有正确硬件的节点。

  • Pod 托管和相互依赖:在微服务设置或紧密耦合的应用程序堆栈中,某些 Pod 应该托管在同一台机器上以提高性能,避免网络延迟问题和连接失败。例如,在与内存缓存服务或数据库相同的机器上运行 Web 服务器是一种很好的做法。

  • 数据位置:数据密集型应用程序的数据局部性要求类似于以前的使用情况。为了确保更快的读取和更好的写入吞吐量,这些应用程序可能需要将数据库部署在所在应用程序的同一台机器上。

  • 高可用性和容错:为了使应用程序部署具有高可用性和容错性,在不同可用分区的节点上部署和运行 Pod 是一个很好的做法。

2Pod 高级调度方法

Kubernetes 提供了许多 API 资源和策略来帮助实现这些场景。下面,我将介绍 nodeSelector、节点亲和性和 Pod 间亲和性概念。我还将向您介绍一些示例,并向您展示如何在您的 K8s 集群中实现它们。

使用 nodeSelector 手动调度 Pod

在早期的 K8s 版本中,用户可以使用 PodSpec 的 nodeSelector 字段来实现手动 Pod 调度。本质上,nodeSelector 是一种基于标签的 pod-to-node 调度方法,用户将某些标签分配给节点,并确保 nodeSelector 字段与这些标签匹配。例如,假设节点标签之一是“storage=ssd”以指示节点上的存储类型。

图片

要使用此标签将 Pod 调度到节点上,我将在 Pod yaml 文件中指定具有该标签的 nodeSelector 字段。

图片

节点选择器是最简单的高级 Pod 调度方法。但是,当在 pod 调度期间应该考虑其他规则和条件时,显得不是很有用。

节点亲和度

与上面讨论的手动放置 pod 方法相比,节点亲和特性是一个质的改进。它使用逻辑运算符和约束提供了一种富有表现力的亲和语言,可以对 pod 放置进行细粒度控制。它还支持“软”和“硬”调度规则,允许根据用户要求控制节点关联约束的严格程度。在下面的示例中,我们使用节点关联将 Pod 放置在特定可用的节点上。让我们看看下面的编排文件:

图片

“硬”关联规则在 pod 编排文件 nodeAffinity 部分的required during scheduling ignored during execution字段下指定。在此示例中,我告诉调度程序仅将 pod 放置在标签为 kubernetes.io/cp-az-name 且值为 cp-1a 或 cp-1b 的节点上。为了实现这一点,我使用了 In 逻辑运算符来过滤现有标签值的数组。我可以使用的其他运算符包括 NotIn、Exists、DoesNotExist、Gt、Lt。“软”规则在规范的preferred during scheduling ignored during execution字段下指定。在此示例中,它指出在满足“硬”标准的节点中,我希望使用带有标签的节点,该标签的键名为“custom-key”,值为“custom-value”。但是,如果不存在这样的节点,我不反对将 pod 调度给其他符合“硬”标准的候选者。结合“硬”和“软”规则的方式构建节点关联规则是一种很好的做法。遵循这种“尽力而为”的方法——尽可能使用某个选项,但如果该选项不可用,则不拒绝调度——使部署调度更加灵活和可预测。

Pod 间亲和性

Kubernetes 中的 Pod 间亲和性是一项功能,它允许您根据 Pod 与其他 Pod 的关系来安排 Pod。此功能支持各种有趣的场景,例如作为相互依赖服务的一部分的 Pod 的托管或数据本地化的实现,其中数据 Pod 与主服务 Pod 运行在同一台机器上。Pod 间亲和性的定义类似于节点亲和性。但是,在这种情况下,我将使用 pod 规范的 podAffinity 字段。

图片

与节点亲和性类似,Pod 亲和性支持表达式匹配和逻辑运算符。然而,在这种情况下,它们被应用于在特定节点上运行的 pod 标签选择器。如果指定的表达式与目标 pod 的 pod 标签匹配,则新的 pod 与目标 pod 放置在同一台机器上。

反亲和性

在某些情况下,最好采用“黑名单”方法进行 Pod 调度。在这种方法中,当某些条件不满足时,Pod 被阻止被调度到特定节点上。这个功能是在 Kubernetes 节点到 Pod 反亲和性和 Pod 间反亲和性中实现的。pod-to-node anti-affinity 的主要用途是使用专用节点。为了控制集群中的资源利用率,K8s 管理员可以将某些节点分配给特定的 pod 类型或应用程序。Pod 间反亲和性的其他使用场景包括:

  • 避免单点故障:这可以通过将相同服务的 Pod 分布在不同机器上来实现,这需要防止 Pod 与其他相同类型的 Pod 并存。

  • 防止服务间的资源竞争:为了提高某些服务的性能,避免将它们与消耗大量资源的其他服务放在一起。

Pod 到节点的反亲和性可以通过 Kubernetes 中的污点和容忍来实现。让我们仔细看看这个功能。

污点和容忍

污点(条件)和容忍度可以帮助您控制 pod 到特定节点的调度,而无需修改现有的 pod。默认情况下,所有对污点没有容忍度的 pod 都将被拒绝或从节点中驱逐。这种行为允许灵活的集群和应用程序部署模式,如果您不希望 pod 在特定节点上运行,则无需更改 pod 定义。实现污点和容忍非常简单。首先,向需要应用一些非标准调度行为的节点添加污点。例如:

图片

污点格式为 <taintKey>=<taintValue>:<taintEffect> 。我在这里使用的污点效果可以防止任何没有匹配容忍度的 pod 被调度到这个节点。其他支持的污点效果包括 NoExecute 和 PreferNoSchedule(NoSchedule 的“软”版本)。如果应用了 PreferNoSchedule 污点,kube-scheduler 不会将没有所需容忍度的 pod 放置到污点上。最后,NoExecute 效应会导致所有 Pod 立即被驱逐,而节点没有一定的容忍度。如果您已经在节点上运行了 Pod 并且不再需要它们,则可以使用该标签。创建污点只是配置的第一部分。为了允许在污点上调度 pod,我们需要添加容忍度:

图片

在此示例中,我使用“Equal”运算符添加了对上述污点的容忍度,也可以使用“Exists”操作符,它将容忍任何与污点 key 匹配的节点。然而 value 值不是必须指定的。在这种情况下,我将使用污点“storage=ssd: NoSchedule”将我们上面定义的 pod 调度到该节点。

Pod 反亲和性

可以通过 pod anti-affinity 功能将 pod 相互排斥。如上所述,Kubernetes 中的最佳实践之一是通过将 Pod 分布在不同的可用区来避免单点故障。我可以在 pod 规范的 anti-affinity 部分配置类似的行为。对于 pod anti-affinity,我们需要两个 pod:第一个 Pod:

图片

请注意,第一个 pod 的标签为“security: s1”。

图片

第二个 pod 指的是 spec.affinity.podAntiAffinity 下的标签选择器“security:s1”。因此,该 Pod 不会被调度到已经托管任何带有“security:s1”标签的 Pod 的节点。

3总结

Kubernetes 中的高级 pod 调度允许实施许多有趣的场景和最佳实践,以在 Kubernetes 上部署复杂的应用程序和微服务。借助 Pod 亲和性,您可以为紧密耦合的应用程序堆栈和微服务实现 Pod 托管和数据本地化。在下面,您可以找到每种资源类型的一些关键调度场景的备忘单。

图片

用于高级 Pod 调度的 Kubernetes 资源概览

使用节点互斥和污点,您可以运行具有专用于特定应用程序和服务的硬件的节点,从而实现集群中的高效资源利用。通过 pod 反亲和和节点反亲和,您还可以通过让不同的组件运行在不同的节点上来确保应用程序的高可用性并避免单点故障。