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

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 和 PR #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。降低此因子会为容器 cgroup 设置较低的 high 限制,从而增加回收压力。增加此因子会减少回收压力。默认值最初为 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] "Observed pod startup duration" 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 Scalability 的职责范围。