节点重启后会发生什么
节点上的系统组件有时会重新启动,可能是因为升级、崩溃或管理员的显式操作。本文描述了当 kubelet、 容器运行时 或整个节点重新启动时,Pod 和节点会发生什么。
在健康的集群中,这些重启通常是安全的,不会破坏正在运行的工作负载。以下部分描述需要注意的影响, 这些影响在大型或负载较重的节点上会更加明显。 最具破坏性的情况是节点重启, 它包含容器运行时重启和 kubelet 重启,但后果更严重,因为节点上的每个容器都会首先停止。
kubelet 重启的影响
如果只有 kubelet 重新启动,已经运行的容器继续运行。 kubelet 重新建立其对节点的视图,并根据期望状态协调正在运行的容器。 在此期间,会发生以下情况:
- kubelet 重新初始化并重新同步其缓存,这会向 API 服务器发送大量请求。 在有许多 Pod 的大型节点上,这种请求突发可能会很显著。
- 在 kubelet 完成初始化之前,节点暂时报告为
NotReady。当节点处于NotReady状态时, 调度器不会在其上放置新的 Pod。
- 节点心跳在 kubelet 宕机期间暂停,
一旦 kubelet 重新启动并完成初始化(此时 kubelet 会更新其
Lease对象并再次发布节点状态),心跳就会恢复。
- kubelet 在重启期间会保留运行中容器的就绪状态。每个 Pod 的就绪状态会驱动
EndpointSlice、
Endpoints 以及下游配置(如 Gateway 或 Ingress);这意味着每次重启时重置容器就绪状态会给
API 服务器和监视端点状态的组件带来很大负载,
并可能短暂地将健康的 Pod 从 Service 负载均衡中移除。此行为在
KEP-4781:修复 kubelet 重启后容器就绪状态不一致的问题
中有描述。长期以来,每次重启时将容器就绪状态重置为
false是默认行为。ChangeContainerStatusOnKubeletRestart特性门控 允许你恢复到该行为,但这是一个已弃用的遗留后门,计划移除,因此你不应依赖它。 有关详细信息,请参阅 kubelet 重启期间的 Pod 行为。
- 在 kubelet 初始启动期间,未使用的镜像和容器的 垃圾回收 以及由节点压力驱动的 Pod 驱逐都会暂停。 此暂停在 kubelet 完成其主要启动例程后仍会持续一段短暂的宽限期。 这种延迟可能会减慢节点对内存或磁盘压力的反应。
- 正在进行的镜像拉取会被取消。根据容器运行时的不同,取消的拉取在重试时可能必须从头开始。
- 当 kubelet 重新通过其准入检查时,节点上的 Pod 会再次运行 Pod 准入。如果节点的 标签或 污点在 kubelet 宕机期间发生了变化, 即使 Pod 已经在运行,它也可能在准入时失败并被拒绝。这是现有行为,是否应将其视为 BUG 仍在讨论中; 有关讨论和详细信息,请参阅 kubernetes/kubernetes#123859。
总体而言,在健康的集群中,kubelet 重启不会破坏正在运行的工作负载。然而,在节点过度使用的大型集群中, 重新初始化负载以及暂停的垃圾回收和驱逐可能会导致系统不稳定。
Kubernetes 没有定义容器运行时重启时的行为。 根据你使用的容器运行时,重启可能会触发所有本地容器的停止或重启。 然而,大多数与 Kubernetes 一起使用的容器运行时都使用一种允许你重启运行时而让容器继续执行的配置。
容器运行时重启的影响
当容器运行时(如 containerd 或 CRI-O) 重新启动时,kubelet 会失去与运行时的连接,直到运行时恢复。在此期间:
- 在重启期间,
exec探针 会失败,因为 kubelet 无法在容器内运行命令。如果超时时间短且失败阈值低,失败的存活探针会导致容器重启, 失败的就绪探针会导致 Pod 在Ready状态之间波动。
- kubelet 将节点报告为
NotReady,这会阻止在该节点上调度新的 Pod。
- 容器操作(如重启、初始化和状态更新)会延迟,直到运行时再次可用。
- 如果在运行时重启时,某个 Init 容器正在执行, 其执行状态可能会丢失,在这种情况下,Init 容器会再次运行。
在极少数情况下,在精确时刻中断操作可能会导致状态不一致:
中断的镜像拉取可能会留下不一致的镜像层,这可能会使镜像无法使用,直到再次拉取。
中断的 Sandbox 创建,如果在 CNI 或 NRI 调用中途终止,可能会使 Sandbox 处于不一致状态, CNI 仅部分初始化,并可能出现资源泄漏。
在精确时刻中断操作是低概率事件,因此重启容器运行时通常是安全的操作。在负载较重的节点上, 每个操作都较慢,中断关键操作的时间窗口更大,遇到这些边缘情况的概率会增加。
节点重启的影响
节点重启是这些事件中最具破坏性的,因为节点上的每个容器都会停止。重启包含容器运行时重启和 kubelet 重启,但后果更严重:独立的 kubelet 或运行时重启会让已运行的容器保持不变, 而重启会首先停止每个容器。节点启动后,kubelet 和容器运行时重新启动,但没有任何容器实际运行。
在计划重启之前,你可以通过隔离(cordon)节点来减少影响,使调度器停止在其上放置新的 Pod, 然后腾空节点以优雅地驱逐现有 Pod。 当启用体面节点关闭时, kubelet 还会在检测到节点正在关闭时尝试干净地停止运行中的 Pod。
当节点恢复时:
- 重启会停止所有容器,当节点恢复时,kubelet 会重新创建它们。如果节点宕机时间超过下面描述的配置容忍期, 只有由控制器管理的 Pod(如 Deployment、 StatefulSet 或 DaemonSet) 会获得替换 Pod。替换的 Pod 可能会调度到不同的节点上。独立 Pod(没有其他对象或控制器管理它们) 在删除后不会重新创建。
- 节点更新租约并协调其状态。在 kubelet、容器运行时和网络准备就绪之前,节点会报告为
NotReady。 当节点处于NotReady状态时,节点可能会被标记污点node.kubernetes.io/not-ready,在配置的容忍期过后,控制平面可以驱逐不容忍该污点的 Pod。
- kubelet 对分配给该节点的 Pod 重新运行准入,因此 kubelet 重启下描述的标签和污点注意事项也适用于此。
- 对于请求设备的 Pod,kubelet 会再次调用相关的 设备插件 来确认正在节点上恢复的 Pod 的设备分配。设备插件必须在重启后重新向 kubelet 注册,以便协调这些分配。
- 与容器或 Pod 生命周期绑定的本地存储可能会丢失。当容器重新创建时,其可写层会被丢弃,
因此写入那里的数据无法在重启后存活。
emptyDir卷的生命周期与 Pod 在节点上的时间相同: 内存支持的emptyDir(medium: Memory)在重启时总是会丢失,因为它存储在 RAM 中, 而磁盘支持的emptyDir只要 Pod 不被驱逐或删除,就能在重启后存活,只有当 Pod 离开节点时才会被删除。
对于必须容忍节点重启的工作负载,请通过控制器运行 Pod, 对必须存活的数据使用持久卷, 并配置中断预算和探针, 以便只在 Pod 就绪后才向其发送流量。