零停机给Kubernetes集群节点打系统补丁

点击上方 "编程技术圈"关注, 星标或置顶一起成长

后台回复“大礼包”有惊喜礼包!

日英文

A person who knows how to laugh at himself will never cease to be amused. 

一个知道如何自嘲的人,会永远生活在快乐里。

每日掏心话

若想看淡世态炎凉、静观花开花落,必先经历颠沛流离和茫然失所。昨天已经过去,明天还没到来。每个人都有他的路,每条路都是正确的。
责编:乐乐 | 来自:架构头条

编程技术圈(ID:study_tech)第 1240 次推文

往日回顾:放弃 Notepad++,事实证明,还有 5 款更牛逼……

     

   正文   

作者 | Vaishnavi Galgali译者 | 王者策划 | 万佳1背景简介Salesforce 的 Einstein Vision 和语言服务部署在 AWS Elastic Kubernetes Service(EKS) 集群上。其中有一个最主要的安全和合规性需求,就是给集群节点的操作系统打补丁。部署服务的集群节点需要通过打补丁的方式进行系统的定期更新。这些补丁减少了可能让虚拟机暴露于攻击之下的漏洞。
 打补丁的过程爱因斯坦服务以 Kubernetes Pod 的形式部署在不可变的 EC2 节点组 (也称为 AWS 自动伸缩组,缩写为 ASG) 中。打补丁的过程包括构建新的 Amazon Machine Image (AMI),镜像中包含了所有更新的安全补丁。新的 AMI 用于更新节点组,每一次需要启动一个新的 EC2 实例。当新实例通过运行健康状况检查后,旧实例将被终止。这个过程将会持续下去,直到节点组中的所有 EC2 实例都被新实例替换,这个过程也称为滚动更新。
然而,这个打补丁的过程给我们带来了一个挑战。当旧的 EC2 实例被终止时,在这些 EC2 实例上运行的服务 Pod 也会被终止。如果 Pod 的终止过程没有得到妥善处理,可能会导致用户请求处理失败。要优雅地终止 Pod,需要基础设施组件 (Kubernetes API 和 AWS ASG) 和应用程序组件 (服务 / 应用程序容器) 的支持。
2优雅终止应用程序在这个过程中,首先要优雅地终止应用程序。终止一个 Pod 可能会导致 Pod 中的 Docker 容器突然终止,在 Docker 容器中运行的进程也会突然终止。这可能会导致正在处理中的请求被终止,最终导致当时正在调用应用程序的上游服务调用失败。
当一个 EC2 实例在打补丁过程中被终止,该实例上的 Pod 也将被驱逐。Pod 被标志为终止,在 EC2 实例上运行的 kubelet 就开始了关闭 Pod 的过程。kubelet 将发出 SIGTERM 信号。如果在 Pod 中运行的应用程序没有处理 SIGTERM 信号的逻辑,正在执行的任务可能会被突然终止。因此,你需要更新应用程序来处理这个信号,并实现优雅的终止。
例如,对于 Java 应用程序,有一种方法可以实现优雅的终止(不同的框架处理方式有所不同):
public static final int gracefulShutdownTimeoutSeconds = 30;@Overridepublic void onApplicationEvent(@NotNull ContextClosedEvent contextClosedEvent) {    this.connector.pause();    Executor executor = this.connector.getProtocolHandler().getExecutor();    if (executor instanceof ThreadPoolExecutor) {    try {        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;        threadPoolExecutor.shutdown();        logger.warn("Gracefully shutdown the service.");        if (!threadPoolExecutor.awaitTermination(gracefulShutdownTimeoutSeconds, TimeUnit.SECONDS)) {        logger.warn("Forcefully shutdown the service after {} seconds.", gracefulShutdownTimeoutSeconds);        threadPoolExecutor.shutdownNow();        }    } catch (InterruptedException ex) {        Thread.currentThread().interrupt();    }    }}在上面的代码片段中,关闭信号被触发,并在 30 秒后强制终止应用程序,这给了应用程序 30 秒的时间来处理正在执行中的任务。
如果 Pod 由多个容器组成,并且容器终止的顺序很重要,那么最好要定义一个容器 preStop 钩子,以确保容器能以正确顺序终止(例如,在终止日志边车容器前先终止应用程序容器)。
https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/
在关闭 Pod 的过程中,kubelet 会执行容器生命周期钩子 (如果定义了的话)。在我们的例子中,一个 Pod 中有多个容器,因此,对我们来说,终止顺序很重要。我们为应用程序容器定义了一个 preStop 钩子,如下所示:
lifecycle:    preStop:        exec:          command:          - /bin/sh          - -c          - kill -SIGTERM 1 && while ps -p 1 > /dev/null; do sleep 1; done;preStop 钩子中定义的动作将向 Docker 容器中的进程 (PID 1) 发送一个 SIGTERM 信号,并以 1 秒为等待时间间隔,直到进程成功终止。进程可以完成任何一个挂起的任务,并正常终止。
preStop 钩子的默认超时时间是 30 秒。在我们的例子中,这提供了足够多的时间让进程优雅地终止。如果默认的时间不够,可以在 preStop 钩子中使用terminationGracePeriodSeconds字段来指定其他值。
3优雅地终止 EC2 实例如上所述,我们的服务运行在 EC2 实例的节点组上。优雅地终止 EC2 实例可以通过使用 AWS ASG 生命周期钩子和 AWS Lambda 服务来实现。
 AWS EC2 自动伸缩生命周期钩子有了生命周期钩子,我们就可以实现在启动新实例或终止旧实例前暂停实例状态,并执行自定义操作。一旦实例被暂停,你就可以通过触发 Lambda 函数或在实例上运行命令来完成生命周期操作。实例会一直保持等待状态,直到生命周期操作完成。
搜索公众号后端架构师后台回复“架构整洁”,获取一份惊喜礼包。
我们使用 Terminating:Wait 生命周期钩子将要终止的实例置于等待状态。有关 ASG 生命周期钩子的更多细节,请参阅 AWS 文档。
http://ttps//docs.aws.amazon.com/autoscaling/ec2/userguide/lifecycle-hooks.html
 AWS Lambda我们使用 SAM 框架来部署 Lambda 函数(这个 Lambda 函数是内部开发的,我们把它叫作 node-drainer),当发生特定的 ASG 生命周期钩子事件时被触发。下图显示了优雅地终止节点组中的 EC2 实例所涉及的事件序列。


当 Patching Automation 请求终止实例时,生命周期钩子将启动,并将实例置于 Terminating:Wait 状态。
当实例处于 terminate:Wait 状态,生命周期钩子就会触发 AWS Lambda 函数。
Lambda 函数调用 Kubernetes API 并隔离被终止的实例。隔离实例可防止在被终止的实例上启动新的 Pod。
隔离实例后,该实例所有的 Pod 都将被驱逐,并放在一个正常的节点上。
Kubernetes 负责为健康实例提供新的 Pod。
生命周期钩子等待,直到所有 Pod 被驱逐出实例,并且新 Pod 出现在一个正常的实例中。
一旦节点被完全清空,生命周期钩子将移除 WAIT 状态,并继续执行终止操作。
这确保了全部现有的请求都已处理完成,然后将 Pod 从节点中移除。
在这样做的同时,我们要确保新 Pod 能处理新的请求。
这种优雅的关闭过程确保没有 Pod 是被突然关闭的,也不会出现服务中断。
4RBAC(基于角色的访问控制)为了能从 AWS Lambda 函数访问 Kubernetes 资源,我们创建了一个 IAM 角色、一个clusterrole和一个clusterrolebinding。IAM 角色用于授予访问 ASG 的权限,clusterrole和clusterrolebinding为node-drainer Lambda 函数授予驱逐 Kubernetes Pod 的权限。
 IAM 角色策略{    "Version": "2012-10-17",    "Statement": [        {            "Action": [                "autoscaling:CompleteLifecycleAction",                "ec2:DescribeInstances",                "eks:DescribeCluster",                "sts:GetCallerIdentity"            ],            "Resource": "*",            "Effect": "Allow"        }    ]} Clusterrolekind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata:  name: lambda-cluster-accessrules:  - apiGroups: [""]    resources: ["pods", "pods/eviction", "nodes"]    verbs: ["create", "list", "patch"] Clusterrolebindingkind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:  name: lambda-user-cluster-role-bindingsubjects:  - kind: User    name: lambda    apiGroup: rbac.authorization.k8s.ioroleRef:  kind: ClusterRole  name: lambda-cluster-access  apiGroup: rbac.authorization.k8s.io5结论通过结合使用 AWS Lambda、AWS EC2 自动伸缩生命周期钩子和优雅的应用程序进程终止,我们确保了在打补丁期间实现零停机频繁滚动更新 EC2 实例。
原文链接:
https://engineering.salesforce.com/zero-downtime-node-patching-in-a-kubernetes-cluster-cdceb21c8c8c
PS:欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,欢迎转发分享给更多人。


版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢!

欢迎加入后端架构师交流群,在后台回复“学习”即可。

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。在这里,我为大家准备了一份2021年最新最全BAT等大厂Java面试经验总结。
别找了,想获取史上最简单的Java大厂面试题学习资料
扫下方二维码回复「面试」就好了


猜你还想看
阿里、腾讯、百度、华为、京东最新面试题汇集
SpringBoot库存管理系统,拿来学习真香

刘强东的代码水平到底有多牛? 网友:95年一个晚上赚5万!

对比安卓!鸿蒙OS 2.0流畅度实测:差距到底多大?

嘿,你在看吗?

你可能感兴趣的:(kubernetes,java,分布式,docker,etcd)