kubernetes集群升级的正确姿式

kubernetes社区很是活跃,每季度都会发布一个release。可是线上集群业务可用性要求较高,场景复杂,任何微小的变动都须要很是当心,此时跟随社区版本进行升级略显吃力。可是为了可以使用到最新的一些feature咱们必须不按期进行一些升级操做,在经历了一次线上集群的升级操做,踩完一些坑以后,分享一些收获与感悟。原来的集群版本是1.10,为了提升GPU集群的资源利用率,须要在调度器层面支持一些抢占调度等新特性,因此升级到1.14,这次升级的集群规模不是特别大,可是有一些在线任务,仍是须要慎重操做。目前kubernetes社区中全部的工具(包括使用较多的kubeadm,kops等工具)对于生产环境高可用的集群升级都显的比较乏力,因此仍是建议手动升级,更加安全可控,出现问题便于定位修复。node

升级策略

升级方式能够分为原地升级和异地升级:git

  • 原地升级是指在原有集群直接替换二进制进行升级,此种方式升级方便,运维代价较小,理论上来讲多副本的业务不会有downtime,在充分测试的状况下优先选择原地升级.
  • 异地升级是指新搭建一个如出一辙的高版本集群,而后手动或自动迁移业务到新集群。此种方式须要上层提供滚动升级的能力,支持两个backend,须要额外的开发工做。且默认double一份原集群pod到新集群不会对业务形成影响,对于使用ceph等持久化存储的集群来讲,可能会有问题。但此种方式升级更加安全可控,风险更低。

官方建议升级过程

  1. 首先阅读相关release node,重点关注其中几部分: Known Issues,Action Requireed,Deprecations and removals。社区会将一些变化highlight到这里,阅读这些变化能够明确本身须要采起哪些行动。
  2. kubernetes 建议不断地进行小版本升级,而不是一次进行大的版本跳跃。具体的兼容策略是: slave组件能够与master组件最多延迟两个小版本(minor version),可是不能比master组件新。client不能与master组件落后一个小版本,可是能够高一个版本,也就是说: v1.3的master能够与v1.1,v1.2,v1.3的slave组件一块儿使用,与v1.2,v1.3,v1.4 client一块儿使用。官方建议每次升级不要跨越两个版本,升级顺序为: master,addons,salve。
  3. slave节点的升级是滚动升级,官方建议首先使用kubectl drain驱逐pod以后,而后升级kubelet,由于kubelet会有一些状态信息文件存储在node节点上,社区并不保证这些状态文件在版本间的兼容性。
  4. apiserver升级以前须要确保resource version被正常支持,目前kubernetes会逐步废弃掉,例如: DaemonSet,Deployment,ReplicaSet 所使用的 extensions/v1beta1,apps/v1beta1,apps/v1beta2 将会在v1.16中彻底废弃掉,届时,若是你再去读取这些版本的资源,apiserver将不会认识这些资源,任何的增删改查都没法进行,只能经过etcdctl进行删除。目前社区正在开发迁移工具,而且在支持该工具以前,全部的版本移除操做都会被冻结,因此目前(2019.5.20)来讲是相对安全的。

生产实践升级过程

  1. 若是采用官方建议的升级策略须要小版本不断升级,可是线上运维压力较大,业务场景复杂,大多数状况下不可能跟随社区版本不断升级,因此能够在充分测试的前提下进行大版本跳跃升级。
  2. 官方建议升级kubelet以前先将全部pod驱逐,可是此种方式可能会形成业务容器频繁重启。例如升级nodeA时pod漂移到未升级节点nodeB,后续升级nodeB可能须要继续驱逐该pod。为避免频繁重启业务,在充分测试的状况下不用驱逐,直接原地升级便可。目前(2019.6.5)在master组件升级以后重启或升级kubelet会致使大部分容器重启,由于kubelet经过hash(container spec)来生成容器的惟一标识,不一样版本间container spec会发生变化,引发hash值变化,进而致使容器重启,参见kubernetes/kubernetes: Issue #63814。在不驱逐pod的状况下原地升级最坏状况下只有一次重启,并且volume等信息不会发生变化,对于集群的扰动也较小。 有能力的同窗能够研究下如何作到升级kubelet不重启容器。
  3. 虽然kubernetes建议先升级master组件,而后再升级node组件,可是实际应用过程当中建议先停掉controller-manager,而后升级master组件,node组件,最后再升级controller-manager,由于controller-manager中会进行一些状态的调谐(reconcile),对于actual status不符合desire status的对象会触发一些操做。升级过程当中尽管咱们会进行充分的测试,可是也不免出现一些非预期的状况下,例如apiserver中某些资源对象的兼容性很差,或者其中的某些字段进行调整,触发controller-manager执行非预期操做,例如重建一个deployment下全部的pod,更糟糕的是,若是此时kubelet还未升级,就可能不认识新版本一些资源对象中的新增的某些字段,此时老的pod被删掉了,可是新的pod没有起来,就形成必定的服务中断。(后面会有相关的案例)
  4. 升级过程当中建议调高日志级别,方便定位问题,在升级完成以后在下降日志级别。
  5. 备份etcd数据,并进行故障恢复的演练,做为升级失败的最后一道防线。
  6. 升级以前review集群状态,确保全部组件正常工做,review重要业务的实例数,避免副本数为1,而且适当设置PDB; 须要检查是否有单个node带有特殊的label,可能有pod依赖于该label,若是该node异常致使pod也会发生故障(这个坑真的有人踩过:参见:ingress-cordoned)。

升级过程当中发现的“坑” (已填)

  1. 上面的实践3中就是笔者测试的时候发现的一个坑: 目前升级会致使部分controller(daemonset)重建容器,升级以后由于pod spec发生变化,部分controller会建立新的controllerrevision ,升级更新全部其控制的容器。若是先升级master/controller-manager,由于此时kubelet还未升级,版本较低,会出现不兼容的字段,严重状况下kubelet会reject掉该pod,致使最终该node上此daemonset container退出并没有法重启。若是daemonset 设置为滚动升级,而且maxUnavailable设置为1的话,能够避免必定损失,最多只容许同时挂掉一个daemonset container。参见kubernetes/kubernetes: Issue #78633。因此最佳实践仍是升级以前停掉Controller-manager,集群实际完毕以后最后升级controller-manager。设置全部controller的升级策略为滚动升级并设置最大不可用实例数。对于上述Issue官方在新版本中已经进行修复并backport到旧版本中。
  2. 对于一些刚从alpha转到beta的特性,beta版本是默认开启的版本,对于这些特性必定要很是当心,可能仍然会存在一些bug,建议仍是手动关闭,对于一些可用性要求较高的集群建议只使用GA的feature。此次踩的一个坑就是node lease特性,该特性在1.14中恰好是Beta版本默认开启,这是一个很是有用的feature,对于大规模集群能够有效下降apiserver,etcd的负载,可是kubelet中node lease的默认renew周期是hardcode的10s中且不可调整,也就是说此时node与master心跳的频率只能是10s,此时指定--node-status-update-frequencyflag没有任何做用,若是controller-manager中配置的--node-monitor-grace-period恰好是10s,这时候node会不断地在ready和non-ready直接摇摆,参见kubernetes/kubernetes issue#80172,该issue已经被笔者给修复掉了 :)。

写在后面

kubernetes仍是在高速的迭代过程当中,升级过程当中出现不兼容是在所不免的, 惟有搞懂内部的实现机制才能保障集群的长治久安。发现问题也须要咱们有能力去解决而且反馈到社区中,取之于社区,理应回报于社区。github

reference

Kubernetes Release Versioningapi

相关文章
相关标签/搜索