作者:David Porter(谷歌)、Mrunal Patel(Red Hat)和 Tim Bannister(The Scale Factory)
优雅的节点关闭(1.21 beta 版)使 kubelet 能够在节点关闭期间优雅地逐出 pod。
Kubernetes 是一个分布式系统,因此我们需要为不可避免的故障做好准备——节点可能故障,容器可能崩溃或重新启动,而且——理想情况下——你的工作负载将能够承受这些灾难性事件。
常见的一类问题是节点关闭或重启时的工作负载失败。在关闭你的节点之前的最佳实践是安全地排干和隔离你的节点[1]。这将确保在此节点上运行的所有 pod 都可以安全地被逐出。逐出将确保你的 pod 可以遵循预期的pod 终止生命周期[2],这意味着在容器中接收 SIGTERM 和/或运行 preStopHooks。
在 Kubernetes 1.20 之前(优雅的节点关闭被作为 alpha 特性引入),安全的节点排干并不容易:它需要用户手动采取行动,并提前排干节点。如果某人或某物在没有先排干节点的情况下关闭了你的节点,那么很可能你的 pod 不会安全地从你的节点中退出并突然关闭。由于 pod 的突然退出,与这些 pod 交谈的其他服务可能会看到错误。这种情况的一些例子可能是由于安全补丁或抢占短期云计算实例而导致的重新启动。
Kubernetes 1.21 带来了优雅的节点关闭到测试阶段。优雅的节点关闭可以让你更好地控制一些意外关闭的情况。通过优雅的节点关闭,kubelet 能够意识到底层系统关闭事件,并可以将这些事件传播到 pod,确保容器能够尽可能优雅地关闭。这使容器有机会检查其状态或释放它们所持有的任何资源。
请注意,为了获得最佳可用性,即使在节点正常关闭的情况下,你仍然应该将部署设计为对节点故障具有容错性。
在 Linux 上,你的系统可以在许多不同的情况下关闭。例如:
这些情况中有许多可能是意外的,并且不能保证集群管理员在这些事件发生之前拍干了节点的资源。通过优雅的节点关闭功能,kubelet 使用了一种称为“Inhibitor Locks[3]”的系统机制,在大多数情况下允许拍干。使用 Inhibitor Locks,kubelet 指示 systemd 推迟系统关闭一段指定的时间,让节点有机会排干和逐出系统上的 pod。
Kubelet 利用这个机制来确保你的 pod 会被干净地终止。当 kubelet 启动时,它获得一个系统延迟型的 inhibitor lock。当系统即将关闭时,kubelet 可以利用其早先获得的延迟型 inhibitor lock,以可配置的短时间延迟关闭。这给了你们更多的时间来终止。因此,即使在意外关闭期间,你的应用程序也将收到一个 SIGTERM,preStop hooks[4]将执行,而 kubelet 将正确地将 Ready 节点情况和相应的 pod 状态更新到 api-server。
例如,在一个启用了优雅节点关闭的节点上,你可以看到 inhibitor lock 被 kubelet 占用:
kubelet-node ~ # systemd-inhibit --list
Who: kubelet (UID 0/root, PID 1515/kubelet)
What: shutdown
Why: Kubelet needs time to handle node shutdown
Mode: delay
1 inhibitors listed.
我们在设计这一功能时考虑的一个重要因素是,并非所有的 pod 都是平等的。例如,在一个节点上运行的一些 pod(例如与日志相关的守护进程)应该尽可能长时间地运行,以便在关机期间捕获重要的日志。因此,pod 分为两类:“regular”和“critical”。Critical pod[5]是那些将 priorityClassName 设置为 system-cluster-critical 或 system-node-critical 的 pod;其他的 pod 被认为是 regular 的。
在我们的示例中,日志 DaemonSet 将作为一个 critical pod 运行。在优雅的节点关闭期间,首先终止 regular 节点,然后终止 critical 节点。例如,这将允许与日志守护进程关联的关键 pod 继续运行,并在 regular pod 终止期间收集日志。
我们将在测试阶段评估是否需要为不同的 pod 优先级类提供更多的灵活性,并在需要时添加支持,如果你有一些想法,请告诉我们。
优雅的节点关闭是由 GracefulNodeShutdown特性门[6]控制的,在 Kubernetes 1.21 中默认启用。
你可以使用两个 kubelet 配置选项来配置优雅的节点关机行为:ShutdownGracePeriod 和 ShutdownGracePeriodCriticalPods。要配置这些选项,你可以编辑 kubelet 配置文件,它通过--config 标志传递给 kubelet;更多详细信息,请参见通过配置文件设置 kubelet 参数[7]。
在关机期间,kubelet 会分两个阶段终止 pod。你可以配置每个阶段的持续时间。
控制关机时间的设置有:
例如,ShutdownGracePeriod=30s,ShutdownGracePeriodCriticalPods=10s,则 kubelet 会延迟节点关机 30 秒。在此期间,前 20 秒(30-10 秒)将用于优雅地终止 regular pod,最后 10 秒将用于终止 critical pod。
注意,在默认情况下,上面描述的两个配置选项 ShutdownGracePeriod 和 ShutdownGracePeriodCriticalPods 都被设置为零,因此你需要根据你的环境对它们进行适当的配置,以激活优雅的节点关闭功能。
欢迎你的反馈!SIG Node 定期开会,可以通过Slack[11](#sig-node)或 SIG 的邮件列表[12]联系。
[1]
安全地排干和隔离你的节点: https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/
[2]
pod 终止生命周期: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination
[3]
Inhibitor Locks: https://www.freedesktop.org/wiki/Software/systemd/inhibit
[4]
preStop hooks: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks
[5]
Critical pod: https://kubernetes.io/docs/tasks/administer-cluster/guaranteed-scheduling-critical-addon-pods/#marking-pod-as-critical
[6]
特性门: https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates
[7]
通过配置文件设置 kubelet 参数: https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/
[8]
文档: https://kubernetes.io/docs/concepts/architecture/nodes/#graceful-node-shutdown
[9]
KEP 2000: https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2000-graceful-node-shutdown
[10]
代码: https://github.com/kubernetes/kubernetes/tree/release-1.21/pkg/kubelet/nodeshutdown
[11]
Slack: https://slack.k8s.io/
[12]
邮件列表: https://github.com/kubernetes/community/tree/master/sig-node#contact