本文发布已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。

Kubernetes 1.27:加速 Pod 启动的更新

如何在大型集群中的节点上加速 Pod 的启动?这是集群管理员可能面临的常见问题。

这篇博文侧重于从 kubelet 方面加速 Pod 启动的方法。它不涉及 controller-manager 通过 kube-apiserver 创建 Pod 的时间,也不包括 Pod 的调度时间或在其上执行的 Webhook。

我们在这里提到了一些从 kubelet 角度考虑的重要因素,但这并非详尽列表。随着 Kubernetes v1.27 的发布,本博文重点介绍了 v1.27 中有助于加速 Pod 启动的重大变化。

并行容器镜像拉取

拉取镜像总是需要一些时间,更糟糕的是,默认情况下,镜像拉取是串行执行的。换句话说,kubelet 一次只会向镜像服务发送一个镜像拉取请求。其他镜像拉取请求必须等待正在处理的请求完成。

要启用并行镜像拉取,请在 kubelet 配置中将 serializeImagePulls 字段设置为 false。当 serializeImagePulls 被禁用时,镜像拉取请求会立即发送到镜像服务,并且可以并发拉取多个镜像。

最大并行镜像拉取将有助于保护您的节点免于镜像拉取的过载

我们在 kubelet 中引入了一个新功能,该功能在节点级别设置了并行镜像拉取的数量限制。此限制限制了可以同时拉取的最大镜像数量。如果存在超出此限制的镜像拉取请求,它将被阻止,直到其中一个正在进行的镜像拉取完成。在启用此功能之前,请确保您的容器运行时的镜像服务可以有效地处理并行镜像拉取。

要限制同时镜像拉取的数量,您可以在 kubelet 中配置 maxParallelImagePulls 字段。通过将 maxParallelImagePulls 设置为值 n,将只并发拉取 n 个镜像。超出此限制的任何其他镜像拉取都将等待,直到至少一个正在进行的拉取完成。

您可以在相关的 KEP 中找到更多详细信息:Kubelet 并行镜像拉取限制 (KEP-3673)。

提高了 kubelet 的默认 API 每秒查询限制

为了改进节点上存在多个 Pod 的场景中的 Pod 启动,特别是在突然扩容的情况下,Kubelet 需要同步 Pod 状态并准备 ConfigMap、Secret 或卷。这需要大量的带宽来访问 kube-apiserver。

在 v1.27 之前的版本中,默认的 kubeAPIQPS 为 5,kubeAPIBurst 为 10。但是,v1.27 中的 kubelet 将这些默认值分别增加到了 50 和 100,以便在 Pod 启动期间获得更好的性能。值得注意的是,这并非我们提高 Kubelet 的 API QPS 限制的唯一原因。

  1. 现在它有可能会被严重限流 (默认 QPS = 5)
  2. 在大型集群中,它们无论如何都会产生显著的负载,因为有很多这样的实例
  3. 它们具有我们可以轻松控制的专用 PriorityLevel 和 FlowSchema

以前,我们在 Pod 启动期间,在具有超过 50 个 Pod 的节点上的 kubelet 中经常遇到 volume mount timeout。我们建议集群操作员将 kubeAPIQPS 提升到 20,将 kubeAPIBurst 提升到 40,尤其是在使用裸金属节点时。

更多详细信息可以在 KEP https://kep.k8s.io/1040 和拉取请求 #116121 中找到。

事件触发的容器状态更新

Evented PLEG(PLEG 是“Pod 生命周期事件生成器”的缩写)在 v1.27 中设置为 Beta 版,Kubernetes 为 kubelet 提供了两种检测 Pod 生命周期事件的方式,例如容器中最后一个进程关闭。在 Kubernetes v1.27 中,_基于事件_ 的机制已升级到 Beta 版,但默认情况下仍处于禁用状态。如果您明确切换到基于事件的生命周期更改检测,则 kubelet 能够比使用依赖于轮询的默认方法更快地启动 Pod。默认机制(轮询生命周期更改)会增加明显的开销;这会影响 kubelet 并行处理不同任务的能力,并导致性能和可靠性问题。出于这些原因,我们建议您将节点切换为使用基于事件的 Pod 生命周期更改检测。

更多详细信息可以在 KEP https://kep.k8s.io/3386从轮询切换到基于 CRI 事件的容器状态更新 中找到。

如果需要,提高您的 Pod 资源限制

在启动期间,某些 Pod 可能会消耗大量的 CPU 或内存。如果 CPU 限制较低,则会显著减慢 Pod 启动过程。为了改进内存管理,Kubernetes v1.22 为 kubelet 引入了一个名为 MemoryQoS 的功能门。此功能使 kubelet 能够在容器、Pod 和 QoS 级别设置内存 QoS,以便在使用 cgroups v2 运行时更好地保护和保证内存质量。尽管它具有优点,但如果 Pod 启动消耗大量内存,则启用此功能门可能会影响 Pod 的启动速度。

Kubelet 配置现在包括 memoryThrottlingFactor。此因子乘以内存限制或节点可分配内存,以设置 cgroupv2 memory.high 值,以强制实施 MemoryQoS。减小此因子会为容器 cgroups 设置较低的上限,从而增加回收压力。增加此因子将减少回收压力。默认值最初为 0.8,在 Kubernetes v1.27 中将更改为 0.9。此参数调整可以减少此功能对 Pod 启动速度的潜在影响。

更多详细信息可以在 KEP https://kep.k8s.io/2570 中找到。

还有什么?

在 Kubernetes v1.26 中,添加了一个新的直方图指标 pod_start_sli_duration_seconds,用于 Pod 启动延迟 SLI/SLO 详细信息。此外,kubelet 日志现在将显示有关 Pod 启动相关时间戳的更多信息,如下所示

Dec 30 15:33:13.375379 e2e-022435249c-674b9-minion-group-gdj4 kubelet[8362]: I1230 15:33:13.375359 8362 pod_startup_latency_tracker.go:102] "观察到的 Pod 启动持续时间" pod="kube-system/konnectivity-agent-gnc9k" podStartSLOduration=-9.223372029479458e+09 pod.CreationTimestamp="2022-12-30 15:33:06 +0000 UTC" firstStartedPulling="2022-12-30 15:33:09.258791695 +0000 UTC m=+13.029631711" lastFinishedPulling="0001-01-01 00:00:00 +0000 UTC" observedRunningTime="2022-12-30 15:33:13.375009262 +0000 UTC m=+17.145849275" watchObservedRunningTime="2022-12-30 15:33:13.375317944 +0000 UTC m=+17.146157970"

带有挂载选项的 SELinux 重新标记功能在 v1.27 中移至 Beta 版。此功能通过使用正确的 SELinux 标签挂载卷来加速容器启动,而不是递归更改卷上的每个文件。更多详细信息可以在 KEP https://kep.k8s.io/1710 中找到。

要确定 Pod 启动缓慢的原因,分析指标和日志可能会有所帮助。其他可能影响 Pod 启动的因素包括容器运行时、磁盘速度、节点上的 CPU 和内存资源。

SIG Node 负责确保快速的 Pod 启动时间,而解决大型集群中的问题也属于 SIG 可扩展性的范畴。