节点关闭
在 Kubernetes 集群中,节点可以按计划的体面方式关闭, 也可能因断电或其他某些外部原因被意外关闭。如果节点在关闭之前未被排空,则节点关闭可能会导致工作负载失败。 节点可以体面关闭或非体面关闭。
节点体面关闭
Kubernetes v1.21 [beta]
(enabled by default: true)
kubelet 会尝试检测节点系统关闭事件并终止在节点上运行的所有 Pod。
在节点终止期间,kubelet 保证 Pod 遵从常规的 Pod 终止流程, 且不接受新的 Pod(即使这些 Pod 已经绑定到该节点)。
节点体面关闭特性依赖于 systemd,因为它要利用 systemd 抑制器锁机制, 在给定的期限内延迟节点关闭。
节点体面关闭特性受 GracefulNodeShutdown
特性门控控制,
在 1.21 版本中是默认启用的。
注意,默认情况下,下面描述的两个配置选项,shutdownGracePeriod
和
shutdownGracePeriodCriticalPods
都是被设置为 0 的,因此不会激活节点体面关闭功能。
要激活此功能特性,这两个 kubelet 配置选项要适当配置,并设置为非零值。
一旦 systemd 检测到或通知节点关闭,kubelet 就会在节点上设置一个
NotReady
状况,并将 reason
设置为 "node is shutting down"
。
kube-scheduler 会重视此状况,不将 Pod 调度到受影响的节点上;
其他第三方调度程序也应当遵循相同的逻辑。这意味着新的 Pod 不会被调度到该节点上,
因此不会有新 Pod 启动。
如果检测到节点关闭正在进行中,kubelet 也会在 PodAdmission
阶段拒绝 Pod,即使是该 Pod 带有 node.kubernetes.io/not-ready:NoSchedule
的容忍度,也不会在此节点上启动。
同时,当 kubelet 通过 API 在其 Node 上设置该状况时,kubelet 也开始终止在本地运行的所有 Pod。
在体面关闭节点过程中,kubelet 分两个阶段来终止 Pod:
- 终止在节点上运行的常规 Pod。
- 终止在节点上运行的关键 Pod。
节点体面关闭的特性对应两个
KubeletConfiguration
选项:
shutdownGracePeriod
:- 指定节点应延迟关闭的总持续时间。这是 Pod 体面终止的时间总和,不区分常规 Pod 还是关键 Pod。
shutdownGracePeriodCriticalPods
:- 在节点关闭期间指定用于终止关键 Pod
的持续时间。该值应小于
shutdownGracePeriod
。
- 在节点关闭期间指定用于终止关键 Pod
的持续时间。该值应小于
说明:
在某些情况下,节点终止过程会被系统取消(或者可能由管理员手动取消)。
无论哪种情况下,节点都将返回到 Ready
状态。然而,已经开始终止进程的
Pod 将不会被 kubelet 恢复,需要被重新调度。
例如,如果设置了 shutdownGracePeriod=30s
和 shutdownGracePeriodCriticalPods=10s
,
则 kubelet 将延迟 30 秒关闭节点。
在关闭期间,将保留前 20(30 - 10)秒用于体面终止常规 Pod,
而保留最后 10 秒用于终止关键 Pod。
说明:
当 Pod 在正常节点关闭期间被驱逐时,它们会被标记为关闭。
运行 kubectl get pods
时,被驱逐的 Pod 的状态显示为 Terminated
。
并且 kubectl describe pod
表示 Pod 因节点关闭而被驱逐:
Reason: Terminated
Message: Pod was terminated in response to imminent node shutdown.
基于 Pod 优先级的节点体面关闭
Kubernetes v1.24 [beta]
(enabled by default: true)
为了在节点体面关闭期间提供更多的灵活性,尤其是处理关闭期间的 Pod 排序问题, 节点体面关闭机制能够关注 Pod 的 PriorityClass 设置,前提是你已经在集群中启用了此功能特性。 此特性允许集群管理员基于 Pod 的优先级类(Priority Class) 显式地定义节点体面关闭期间 Pod 的处理顺序。
前文所述的节点体面关闭特性能够分两个阶段关闭 Pod, 首先关闭的是非关键的 Pod,之后再处理关键 Pod。 如果需要显式地以更细粒度定义关闭期间 Pod 的处理顺序,需要一定的灵活度, 这时可以使用基于 Pod 优先级的体面关闭机制。
当节点体面关闭能够处理 Pod 优先级时,节点体面关闭的处理可以分为多个阶段, 每个阶段关闭特定优先级类的 Pod。可以配置 kubelet 按确切的阶段处理 Pod, 且每个阶段可以独立设置关闭时间。
假设集群中存在以下自定义的 Pod 优先级类。
Pod 优先级类名称 | Pod 优先级类数值 |
---|---|
custom-class-a |
100000 |
custom-class-b |
10000 |
custom-class-c |
1000 |
regular/unset |
0 |
在 kubelet 配置中,
shutdownGracePeriodByPodPriority
看起来可能是这样:
Pod 优先级类数值 | 关闭期限 |
---|---|
100000 | 10 秒 |
10000 | 180 秒 |
1000 | 120 秒 |
0 | 60 秒 |
对应的 kubelet 配置 YAML 将会是:
shutdownGracePeriodByPodPriority:
- priority: 100000
shutdownGracePeriodSeconds: 10
- priority: 10000
shutdownGracePeriodSeconds: 180
- priority: 1000
shutdownGracePeriodSeconds: 120
- priority: 0
shutdownGracePeriodSeconds: 60
上面的表格表明,所有 priority
值大于等于 100000 的 Pod 停止期限只有 10 秒,
所有 priority
值介于 10000 和 100000 之间的 Pod 停止期限是 180 秒,
所有 priority
值介于 1000 和 10000 之间的 Pod 停止期限是 120 秒,
其他所有 Pod 停止期限是 60 秒。
用户不需要为所有的优先级类都设置数值。例如,你也可以使用下面这种配置:
Pod 优先级类数值 | 关闭期限 |
---|---|
100000 | 300 秒 |
1000 | 120 秒 |
0 | 60 秒 |
在上面这个场景中,优先级类为 custom-class-b
的 Pod 会与优先级类为 custom-class-c
的 Pod 在关闭时按相同期限处理。
如果在特定的范围内不存在 Pod,则 kubelet 不会等待对应优先级范围的 Pod。 kubelet 会直接跳到下一个优先级数值范围进行处理。
如果此功能特性被启用,但没有提供配置数据,则不会出现排序操作。
使用此功能特性需要启用 GracefulNodeShutdownBasedOnPodPriority
特性门控,
并将 kubelet 配置
中的 shutdownGracePeriodByPodPriority
设置为期望的配置,
其中包含 Pod 的优先级类数值以及对应的关闭期限。
说明:
在节点体面关闭期间考虑 Pod 优先级的能力是作为 Kubernetes v1.23 中的 Alpha 功能引入的。 在 Kubernetes 1.31 中该功能是 Beta 版,默认启用。
kubelet 子系统中会生成 graceful_shutdown_start_time_seconds
和
graceful_shutdown_end_time_seconds
度量指标以便监视节点关闭行为。
处理节点非体面关闭
Kubernetes v1.28 [stable]
(enabled by default: true)
节点关闭的操作可能无法被 kubelet 的节点关闭管理器检测到, 或是因为该命令没有触发 kubelet 所使用的抑制器锁机制,或是因为用户错误, 即 ShutdownGracePeriod 和 ShutdownGracePeriodCriticalPod 配置不正确。 请参考以上节点体面关闭部分了解更多详细信息。
当某节点关闭但 kubelet 的节点关闭管理器未检测到这一事件时, 在那个已关闭节点上、属于 StatefulSet 的 Pod 将停滞于终止状态,并且不能移动到新的运行节点上。 这是因为已关闭节点上的 kubelet 已不存在,亦无法删除 Pod, 因此 StatefulSet 无法创建同名的新 Pod。 如果 Pod 使用了卷,则 VolumeAttachments 无法从原来的已关闭节点上删除, 因此这些 Pod 所使用的卷也无法挂接到新的运行节点上。 最终,那些以 StatefulSet 形式运行的应用无法正常工作。 如果原来的已关闭节点被恢复,kubelet 将删除 Pod,新的 Pod 将被在不同的运行节点上创建。 如果原来的已关闭节点没有被恢复,那些在已关闭节点上的 Pod 将永远滞留在终止状态。
为了缓解上述情况,用户可以手动将具有 NoExecute
或 NoSchedule
效果的
node.kubernetes.io/out-of-service
污点添加到节点上,标记其无法提供服务。
如果在 kube-controller-manager
上启用了 NodeOutOfServiceVolumeDetach
特性门控,
并且节点被污点标记为无法提供服务,如果节点 Pod 上没有设置对应的容忍度,
那么这样的 Pod 将被强制删除,并且该在节点上被终止的 Pod 将立即进行卷分离操作。
这样就允许那些在无法提供服务节点上的 Pod 能在其他节点上快速恢复。
在非体面关闭期间,Pod 分两个阶段终止:
- 强制删除没有匹配的
out-of-service
容忍度的 Pod。 - 立即对此类 Pod 执行分离卷操作。
说明:
- 在添加
node.kubernetes.io/out-of-service
污点之前, 应该验证节点已经处于关闭或断电状态(而不是在重新启动中)。 - 将 Pod 移动到新节点后,用户需要手动移除停止服务的污点, 并且用户要检查关闭节点是否已恢复,因为该用户是最初添加污点的用户。
存储超时强制解除挂接
在任何情况下,当 Pod 未能在 6 分钟内删除成功,如果节点当时不健康,
Kubernetes 将强制解除挂接正在被卸载的卷。
任何运行在使用了强制解除挂接卷的节点之上的工作负载,
都将违反 CSI 规范,
该规范指出 ControllerUnpublishVolume
"必须在调用卷上的所有 NodeUnstageVolume
和 NodeUnpublishVolume
执行且成功后被调用"。
在这种情况下,相关节点上的卷可能会遇到数据损坏。
强制存储解除挂接行为是可选的;用户可以选择使用"非体面节点关闭"特性。
可以通过在 kube-controller-manager
中设置 disable-force-detach-on-timeout
配置字段来禁用超时时存储强制解除挂接。
禁用超时强制解除挂接特性意味着,托管在异常超过 6 分钟的节点上的卷将不会保留其关联的
VolumeAttachment。
应用此设置后,仍然关联卷到不健康 Pod 必须通过上述非体面节点关闭过程进行恢复。
说明:
- 使用非体面节点关闭过程时必须小心。
- 偏离上述步骤可能会导致数据损坏。
接下来
了解更多以下信息: