Kubernetes群集的零停机服务器更新
原文标题:Zero Downtime Server Updates For Your Kubernetes Cluster
发布时间:Jan 26, 2019
原文链接:https://blog.gruntwork.io/zero-downtime-server-updates-for-your-kubernetes-cluster-902009df5b33
文章作者:yorinasub17
在Kubernetes集群的生命周期中,总会有某个时候,你需要对集群的宿主机节点进行维护。这可能包括程序包更新,内核升级或部署新的VM映像。在Kubernetes中,这些操作被视为“自愿中断”。
译注:参考Kubernetes对自愿中断和非自愿中断的定义
链接:https://kubernetes.io/docs/concepts/workloads/pods/disruptions/#voluntary-and-involuntary-disruptions
这篇文章是一个分为四部分的博客系列的第一篇,下一篇文章会介绍如何优雅地关闭 Pod。
在这个系列中我们会介绍 Kubernetes 提供的所有用来实现集群中工作节点的零宕机时间更新的工具。
问题说明
我们将从简单直接的维护宿主机节点的方法开始,确定该方法的挑战和潜在风险,并逐步构建解决我们在整个系列中发现的每个问题的方法。在这个博客系列结束时我们将完成一个Kubernetes配置,该配置利用生命周期钩子,就绪探针(redinessProbe)和 PodDisruptionBudgets 来实现 Kubernetes集群的零停机时间部署。
译注:推荐阅读「Pod的活性探针和就绪探针」
为了开始我们的旅程,让我们看一个具体的例子。假设我们有一个两节点的 Kubernetes 集群,集群上运行着一个使用了两个 Pod 和一个 Service 资源的应用程序。
现在的问题是我们要升级集群中两个宿主机节点的内核版本。我们将如何执行升级?简单粗暴的方法是使用更新的配置启动新节点,在启动新节点后关闭旧节点。尽管这种方法有效,但是这种方法存在一些问题:
当关闭旧节点时,节点上的 Pod 也会被删除。如果 Pod 需要清理以正常关闭上面运行的应用程序该怎么办?底层的VM技术可能不会等待 Pod 执行清理过程。 如果同时关闭所有节点怎么办?在将 Pod 重新启动到新节点中时,你的应用程序服务会短暂中断。
我们想要的是一种从旧节点上正常迁移 Pod 的方法,以确保在对节点进行更改时,没有任何工作负载在运行。或者,如示例中所述,如果要完全替换群集(例如替换VM镜像),我们希望将工作负载从旧节点移到新节点。在这两种情况下,我们都希望避免调度新的 Pod 到旧节点上,我们可以使用kubectl drain
命令来实现这一点。
把Pod调度到节点之外
排出操作(kubectl drain
)实现了将所有 Pod 重新调度到节点之外的目的。在排出操作期间,该节点会被标记为不可调度(通过给节点添加 NoSchedule 污点实现)。这样可以防止新建的Pod被调度到节点上。之后,排出操作开始从节点上驱逐 Pod,通过将 TERM 信号发送到 Pod 的底层容器来关闭当前在节点上运行的容器。
尽管kubectl drain
将很好地处理将Pod 逐出节点的工作,但仍有两个因素可能会在kubectl drain
触发的操作运行期间导致服务中断:
运行中的应用程序需要能够优雅地处理 TERM 信号,当一个 Pod 驱逐时,Kubernetes 会向 Pod 发送 TERM 信号,然后在强制终结容器前会等待一段时间让容器自己关闭,这个等待时间是可以配置的。但是,如果 Pod 里的应用程序不能优雅地处理 TERM 信号,则仍然会导致不干净地关闭 Pod,比如应用程序正在工作期间(例如提交数据库事务等)。 应用程序将失去为其提供服务的所有 Pod 。在新节点上启动新容器时,您的服务会遭遇停机。
避免停机
为了最大程度地减少因维护集群等自愿性中断而导致的停机时间,Kubernetes 提供以下中断处理功能:
Graceful termination Lifecycle hooks PodDisruptionBudgets
本系列的其余部分中,我们将使用 Kubernetes 的这些功能来减轻驱逐事件造成的服务中断。为了使后续操作更容易,我们将在上面的示例中使用以下资源配置:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.15
ports:
- containerPort: 80
---
kind: Service
apiVersion: v1
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
targetPort: 80
port: 80
上面的配置里包含了一个 Deployment 资源的最简示例,这个 Deployment 会始终维持集群中有两个标签为 app: nginx
的 Pod,另外配置里还提供了一个可用于访问集群内 Nginx Pod 的 Service 资源的定义。
我们将在本系列的整个过程中逐步增加其内容,以构建最终配置,以实现Kubernetes提供的所有功能,最大程度地减少维护操作期间的停机时间。
下一篇文章,我们将会讲解如何利用 Kubernetes 的生命周期钩子(Lifecycle Hooks)来正常关闭 Pod。