前言

今天推荐一篇关于Kubernetes上服务滚动更新相关的配置选项的文章,文章列出了最常用的几个配置项,解释了他们是怎么影响调度器对服务进行滚动更新的,同时还带出了Kubernetes项目中Pod这个逻辑单元的Ready状态是怎么确定的,并不是容器运行起来后Pod就进入Ready状态的。总之个人觉得是篇非常好的普及Kubernetes基础的文章,文章由本人完全手工翻译,尽量做到通顺易懂,英文好的同学可以直接看原文。

原文标题:Kubernetes Deployments 滚动更新配置/ Kubernetes Deployments Rolling Update Configration.

发布时间:February 26, 2020

原文链接:https://www.bluematador.com/blog/kubernetes-deployments-rolling-update-configuration

文章作者:Keilan Jackson

文章译者:Keivn Yan

DeploymentKubernetes中一种常用的Pod控制器,它对Pod提供了细粒度的全面控制:如何进行Pod配置、如何执行Pod更新,应运行多少Pod以及何时终止Pod。有许多这方面的资源会教你如何配置Deployment,但是你可能很难理解每个选项是如何影响滚动更新的执行方式的。在此博客文章中,我们将涵盖以下主题,以帮助您成为Kubernetes Deployment的专家:

  • Kubernetes Deployment概貌;

  • Kubernetes服务的滚动更新;

  • 怎么定义Pod的Ready状态;

  • Pod Affinity和Anti-Affinity;

Deployment 概貌

Deployment实质上是ReplicaSet的上层包装。ReplicaSet管理正在运行的Pod数量,Deployment在其之上实现功能从而拥有了Pod滚动更新,对Pod的运行状况进行健康检查以及轻松回滚更新的能力。在常规运行期间,Deployment仅管理一个ReplicaSet,以确保期望数量的Pod正在运行:

Deployment-Replicate-Pod之间的关系

Kubernetes里我们不应直接操作由Deployment创建的ReplicaSet,对ReplicaSet执行的所有操作应改为在Deployment上执行,然后由Deployment管理更新ReplicaSet的过程。以下是一些通常在Deployment上执行的操作的示例kubectl命令:

# 列出默认命名空间下的所有Deployment
kubectl get deploy# 通过定义文件更新Deployment
kubectl apply -f test.yaml# 监控"test"这个Deployment的状态更新
kubectl rollout status deploy/test# 暂停"test"这个Deployment的更新流程:
kubectl rollout pause deploy/test# 恢复"test"这个Deployment的更新流程:
kubectl rollout resume deploy/test# 查看"test"这个Deployment的更新历史:
kubectl rollout history deploy/test# 回退test最近的更新
kubectl rollout undo deploy/test# 把test这个Deployment回滚到指定版本
kubectl rollout undo deploy/test --to-revision=1

Pod的滚动更新

使用Deployment来控制Pod的主要好处之一是能够执行滚动更新。滚动更新允许你逐步更新Pod的配置,并且Deployment提供了许多选项来控制滚动更新的过程。

控制滚动更新最重要的选项是更新策略。在DeploymentYAML定义文件中,由spec.strategy.type字段指定Pod的滚动更新策略,它有两个可选值:

  • RollingUpdate (默认值):逐步创建新的Pod,同时逐步终止旧Pod,用新Pod替换旧Pod。

  • Recreate:在创建新Pod前,所有旧Pod必须全部终止。

大多数情况下,RollingUpdateDeployment的首选更新策略。如果你的Pod需要作为单例运行,并且不允许在任何时间存在多副本,这种时候Recreate更新策略会很有用。

使用RollingUpdate策略时,还有两个选项可以让你微调更新过程:

  • maxSurge:在更新期间,允许创建超过期望状态定义的Pod数的最大值。

  • maxUnavailable:在更新期间,容忍不可访问的Pod数的最大值

maxSurgemaxUnavailable选项都可以使用整数(比如:2)或者百分比(比如:50%)进行配置,而且这两项不能同时为零。当指定为整数时,表示允许超期创建或者不可访问的Pod数。当指定为百分比时,将使用期望状态里定义的Pod数作为基数。比如:如果对maxSurgemaxUnavailable都使用默认值25%,并且将更新应用于具有8个容器的Deployment,那么意味着maxSurge为2个容器,maxUnavailable也将为2个容器。这意味着在更新过程中,将满足以下条件:

  • 最多有10个Pod(8个期望状态里指定的Pod和2个maxSurge允许超期创建的Pod)在更新过程中处于Ready状态。

  • 最少有6个Pod(8个期望状态里指定的Pod和2个maxUnavailable允许不可访问的Pod)将始终处于Ready状态。

值得注意的一点是,在考虑Deployment应在更新期间运行的Pod数量时,使用的是在Deployment的更新版本中指定的副本数,而不是现有Deployment版本的期望状态中指定的副本数。

可以用另外一种方式理解这两个选项:maxSurge是一次将创建的新Pod的最大数量,maxUnavailable是一次将被删除的旧Pod的最大数量。让我们具体看一下使用以下更新策略将具有3个副本的Deployment从"v1"更新为" v2"的过程:

replicas: 3
strategy:type: RollingUpdaterollingUpdate:maxSurge: 1maxUnavailable: 0

这个更新策略是,我们想一次新建一个Pod,并且应该始终保持Deployment中的Pod有三个是Ready状态。下面的动图说明了滚动更新的每一步都发生了什么。如果Deployment看到Pod已经完全部署好了将会把Pod标记为Ready,创建中的Pod标记为NotReady,正在被删除的Pod标记为Terminating。

Deployment滚动更新Pod的过程

怎么判读Pod是否Ready

Kubernetes自身实现了一个叫做Ready Pod的概念来辅助滚动更新。具体来说就是,ReadinessProbe (就绪探针)可以使Deployment逐步更新Pod,同时也可以使用它控制何时才能进行滚动更新,Service也使用它来确定应该将哪些Pod包含在服务的Endpoints中。就绪探针与活动性探针相似但不相同,活性探针使kubelet可以根据其“重新启动策略”来确定哪些Pod需要重新启动,并且它们与就绪性探针分开配置,不会影响Deployment的滚动更新的过程。

译者注:关于就绪探针和活性探针详细的解释可以看我以前的文章:浅析Kubernetes Pod重启策略和健康检查

一个Ready状态的Pod是指:Pod通过了就绪探针的测试,并且自创建以来经过了spec.minReadySeconds指定的秒数则被视为已经Ready。这些选项的默认值将导致Pod内部的容器启动后Pod立即进入Ready状态。

事实上,有几个原因通常让你并不想让容器启动后Pod立即进入Ready状态:

  • 希望在接收流量前,Pod能够先通过健康检查。

  • 服务需要先预热,然后再提供流量。

  • 你要放慢部署速度,以减少对运行中的系统的影响。

对于Web应用程序,要求通过健康检查非常常见,这对于以最小的中断执行更新至关重要。下面是为了对一个Web应用进行健康检查而配置的就绪探针:

readinessProbe:periodSeconds: 15timeoutSeconds: 2successThreshold: 2failureThreshold: 2httpGet:path: /healthport: 80

这个探针要求对端口80上的/health的调用在2秒内成功完成,每15秒执行一次,并且在Pod准备就绪之前必须进行2次成功的调用。这意味着在最佳情况下,Pod将在约30秒内准备就绪。许多应用程序在启动后2秒钟之内无法立即提供服务,即使是简单的请求,因此应该为前1项或2次检查的失败做好准备,这种情况下实际需要约60秒的准备时间Pod才能进入Ready状态。

您还可以配置在容器上执行命令的就绪探针。这让你可以编写可执行的自定义脚本,并确定Pod是否已准备好,Deployment是否可以继续执行滚动更新:

readinessProbe:exec:command:- /startup.shinitialDelaySeconds: 5periodSeconds: 15successThreshold: 1

在这个配置中,DeploymentPod创建成功后将等待5秒钟,然后每15秒钟执行一次命令。脚本的exit code为0被认为是执行成功。使用命令脚本的灵活性让你可以执行以下类似操作,例如将数据加载到缓存中或预热JVM,或在不修改应用程序代码的情况下对下游服务进行运行状况检查。

我们将在这里讨论的最后一种情况是故意减慢更新过程,以最大程度地减少对系统的影响。乍一看似乎不需要,但是在某些情况下它可能非常有用,包括事件处理系统,监视工具和预热时间较长的Pod。通过在Deployment定义中指定spec.minReadySeconds字段可以轻松实现此目标。当指定minReadySeconds时,Pod必须运行这么多秒,而且其容器中的任何一个都不能崩溃,才能被Deployment视为进入Ready状态。

比如说,假设一个Deployment管控着5个Pod副本,它们从事件流读取、处理事件,然后把数据保存在数据库中。每个Pod需要预热60秒后才能全速处理事件,如果使用默认的选项值,Pod创建后立即进入Ready状态,但是它们在第一分钟内处理事件的速度会很慢。更新完成后,由于所有Pod同时预热,因此事件将会堆积在事件流里面。相反,你可以将maxSurge设置为1,将maxUnavailable设置为0,将minReadySeconds设置为60。这将确保一次创建一个新的Pod,在经过一分钟预热后新建的Pod才能进入Ready状态,并且旧Pod在新Pod就绪之前不会被删除。这样,就可以在大约5分钟的时间内更新所有Pod,并且能保持更新期间服务的稳定。

Pod Affinity和Anti-Affinity

PodAffinityPodAntiAffinity这两个配置让你可以控制将DeploymentPod调度到哪些节点上。尽管这个功能并非Deployment控制器特有的,但对于许多应用程序来说可能非常有用。在配置PodAffinityPodAntiAffinity时,你必须选择在不同环境条件下要添加给新建的Pod的调度偏好。这里有两个选项:

  • requiredDuringSchedulingIgnoredDuringExecution:除非节点与Affinity配置相匹配,否则即使没有匹配的节点,直接调度失败,也不会把Pod调度到不匹配的节点上去。

  • preferredDuringSchedulingIgnoredDuringExecution:调度程序将尝试在与配置匹配的节点上调度Pod,但如果无法这样做,则仍将Pod调度在另一个节点上。

podAffinity用于将Pod调度到某些节点上。如果知道某个Pod具有特定的资源要求(例如,只有一组特定的,带有GPU的节点或者某个区域中的节点),则通常需要进行podAffinity配置。或者你希望Pod与其他Pod并置在一个节点上:

affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: appoperator: Invalues:- cachetopologyKey: "kubernetes.io/hostname"podAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100podAffinityTerm:labelSelector:matchExpressions:- key: appoperator: Invalues:- webtopologyKey: kubernetes.io/hostname

相反地,podAntiAffinity在确保同属一个DeploymentPod不被调度到同一个节点上时非常有用,上面例子偏向于将标签为app:webPod部署到不同的节点上,降低服务的所有Pod因为节点出问题同时出故障的可能性。

使用affinity配置时,要注意非常重要的一点是,affinity规则是在对Pod进行调度时进行评估的。而调度器无法预见Pod的调度位置,这意味着affinity配置可能无法达到预期的效果。考虑有一个拥有3个节点的集群,一个拥有3个Pod副本的使用了上面示例affinity规则的DeploymentDeploymentmaxSurge配置成1。你期望的调度目标可能是,在每个节点上运行Deployment的一个Pod,但是由于maxSurge设置为1,在滚动更新期间调度器每次只能创建一个新Pod。这意味着随着时间的流逝,你可能最终会得到一个更新后没有任何这些Pod的节点,然后所有或大多数Pod将在下一次更新时移至该节点。调度程序不知道你将要终止旧的Pod,在做affinity规则判断时仍然会算上旧Pod,这就是导致上面说的可能情况的原因。如果确实需要在每个节点上只能运行一个Pod副本,则应使用DaemonSets控制器。如果您的应用程序可以接受,另一种选择是将更新策略更改为“重新创建”。这样,当调度程序评估affinity规则时,将不会算上旧的Pod

podAffinityPodAntiAffinity有很多选项可以影响Pod的调度方式,但通常无法保证滚动更新也能满足规则。在某些情况下,这是一个非常有用的功能,但是除非真的需要控制Pod的运行位置,否则应让kubernetes调度程序来做出这些决定。可以在此处[1]找到有关podAffinitypodAntiAffinity的详细文档。

总结

我们已经介绍了Deployment的基本用法,滚动更新的工作方式以及用于微调更新和Pod调度的许多配置选项。此时,你应该能够使用更新策略,就绪探针和Pod关联性(affinity)来自信地创建和修改Deployment的定义文件,以达到应用程序期望的状态。有关Deployment支持的所有选项的详细参考,请查看Kubernetes文档。

引用链接

[1]

此处: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity

  推荐阅读

  • 深入理解StatefulSet,用Kubernetes编排有状态应用

  • 常用限流算法的应用场景和实现原理

  • 觉得WaitGroup不好用?试试ErrorGroup吧!

- END -

关注公众号,获取更多精选技术原创文章

Kubernetes--玩转Pod滚动更新123相关推荐

  1. 容器编排技术 -- 使用kubectl实现应用滚动更新

    容器编排技术 -- 使用kubectl实现应用滚动更新 更新应用 用户需求:需要应用始终正常运行,开发人员每天需要部署新的版本(一个简单例子,大家在玩游戏时常常碰到这类公告:8月8日凌晨:2点-6点服 ...

  2. 不停机与停机更新_Istio的零停机滚动更新

    不停机与停机更新 本系列文章的第一部分介绍了如何在Kubernetes集群中实现真正的零停机时间更新. 我们专门解决了将流量从旧实例切换到新实例时出现的请求失败. 本文将展示如何使用Istio群集实现 ...

  3. Istio的零停机滚动更新

    本系列文章的第一部分介绍了如何在Kubernetes集群中实现真正的零停机时间更新. 我们专门解决了将流量从旧实例切换到新实例时出现的请求失败. 本文将展示如何使用Istio群集实现相同的目标. 服务 ...

  4. 深入玩转K8S之智能化的业务弹性伸缩和滚动更新操作

    在上篇我们讲到了较为傻瓜初级的弹性伸缩和滚动更新,那么接下来我们来看看较为高级的智能的滚动更新.本节的知识点呢是K8S的liveness和readiness探测,也就是说利用健康检查来做更为智能化的弹 ...

  5. 万字长文带你全面认识 Kubernetes 中如何实现蓝绿部署、金丝雀发布和滚动更新...

    Kubernetes 中的部署策略 在本文中,我们将学习使用 Kubernetes 容器编排系统部署容器时的部署策略.在本文的最后,我们将学习如何在 Kubernetes 集群中使用不同的方式进行部署 ...

  6. Kubernetes滚动更新速率控制解读

    女主宣言 利用kubernetes的滚动更新时,可能经常遇到发布"太快不稳定"或"太慢体验差"的情况.本文将介绍kubernetes滚动更新控制速率的特性. P ...

  7. Kubernetes部署策略:重建、滚动更新、蓝绿部署、金丝雀部署

    Kubernetes原生支持重建.滚动更新两种部署策略.通过修改Service的label,切换流量转发可以实现蓝绿部署.金丝雀部署. 部署前的准备 1.需要有一个k8s集群.没有可查看此博客 htt ...

  8. Kubernetes滚动更新(无中断平滑发布)

    Kubernetes支持名为Rolling Update的功能,允许您不间断地, 接近几乎无缝地平滑升级部署应用程序 ,即在不停止对外服务的前提下完成应用的更新. 什么是滚动更新? 为了应用升级部署时 ...

  9. kubernetes(七)项目部署方式:蓝绿部署,灰度发布/金丝雀发布,滚动更新

    在项目迭代的过程中,不可避免需要上线进行部署. 目前项目部署的方式有很多种:像重新部署,蓝绿部署,金丝雀部署(灰度部署),滚动更新.本文简单介绍下这些常见的部署方案以及使用k8s怎么进行对应部署 重新 ...

最新文章

  1. retinaface训练笔记
  2. C++实现图片的base64编码
  3. 语言语法糖_【c#】几种常用语法糖
  4. excel去重_数据处理之EXCEL的高效技巧分享
  5. IntelliJ IDEA里的项目搞崩了怎么办,本地历史版本回退拯救你崩溃的心灵
  6. 计算机网络学习笔记(17. 计算机网络作业一)
  7. 创业者如何防止合伙股东退出,给公司造成损失?
  8. ScheduledThreadPoolExecutor Usage
  9. Exchange2003不能自动删除日志
  10. C#环境下利用VS2017使用MapXtreme7.0.0开发桌面应用实例
  11. iTunes未能备份iPhone 多种详细解决方法
  12. 互联网晚报 | 1月4日 星期二 | 中国移动1月5日在上交所上市;元旦档总票房破10亿;特斯拉连续6个季度交付量创纪录...
  13. 2022年打工人转行实录!你后悔转行了吗?
  14. STING 与 cGAS的结合导致TBK1 激酶募集和活化
  15. linux命令 ---rm
  16. 好用的PERL正则表达式在线测试工具
  17. JavaWeb(华清远见)
  18. maccamy fuchs matlab,近海固定式风机单桩大直径基础波浪载荷研究
  19. Linux进程KILL--Quit,INT,HUP,QUIT,和TERM、PIPE的解释
  20. SC8701同步升降压控制芯片

热门文章

  1. DBA必知的mysql备份与还原的几大方法
  2. mariadb数据库服务
  3. firfox 和 chrome 移动端Web开发页面调试
  4. 电商、快递最后一公里的解决方案
  5. 用反射方法使用户控件动态调用父页面的方法
  6. OpenStack快速入门-queens版本
  7. C语言解析Ini格式文件
  8. Cocos Studio 2.3.2不再支持直接导入PSD文件
  9. 《JAVA与模式》之装修者模式
  10. Android SlidingMenu 仿网易新闻客户端布局