kubernetes集群升级的正确姿势
kubernetes社区非常活跃,每季度都会发布一个release。但是线上集群业务可用性要求较高,场景复杂,任何微小的变更都需要非常小心,此时跟随社区版本进行升级略显吃力。但是为了能够使用到最新的一些feature我们必须不定期进行一些升级操作,在经历了一次线上集群的升级操作,踩完一些坑之后,分享一些收获与感悟。原来的集群版本是1.10,为了提高GPU集群的资源利用率,需要在调度器层面支持一些抢占调度等新特性,所以升级到1.14,此次升级的集群规模不是特别大,但是有一些在线任务,还是需要慎重操作。目前kubernetes社区中所有的工具(包括使用较多的kubeadm,kops等工具)对于生产环境高可用的集群升级都显的比较乏力,所以还是建议手动升级,更加安全可控,出现问题便于定位修复。
升级策略
升级方式可以分为原地升级和异地升级:
- 原地升级是指在原有集群直接替换二进制进行升级,此种方式升级方便,运维代价较小,理论上来说多副本的业务不会有downtime,在充分测试的情况下优先选择原地升级.
- 异地升级是指新搭建一个一模一样的高版本集群,然后手动或自动迁移业务到新集群。此种方式需要上层提供滚动升级的能力,支持两个backend,需要额外的开发工作。且默认double一份原集群pod到新集群不会对业务造成影响,对于使用ceph等持久化存储的集群来说,可能会有问题。但此种方式升级更加安全可控,风险更低。
官方建议升级过程
- 首先阅读相关
release node
,重点关注其中几部分: Known Issues,Action Requireed,Deprecations and removals。社区会将一些变化highlight到这里,阅读这些变化可以明确自己需要采取哪些行动。 - 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。
- slave节点的升级是滚动升级,官方建议首先使用
kubectl drain
驱逐pod之后,然后升级kubelet,因为kubelet会有一些状态信息文件存储在node节点上,社区并不保证这些状态文件在版本间的兼容性。 - apiserver升级之前需要确保resource version被正常支持,目前kubernetes会逐步废弃掉,例如: DaemonSet,Deployment,ReplicaSet 所使用的 extensions/v1beta1,apps/v1beta1,apps/v1beta2 将会在v1.16中完全废弃掉,届时,如果你再去读取这些版本的资源,apiserver将不会认识这些资源,任何的增删改查都无法进行,只能通过
etcdctl
进行删除。目前社区正在开发迁移工具,并且在支持该工具之前,所有的版本移除操作都会被冻结,所以目前(2019.5.20)来说是相对安全的。
生产实践升级过程
- 如果采用官方建议的升级策略需要小版本不断升级,但是线上运维压力较大,业务场景复杂,大多数情况下不可能跟随社区版本不断升级,所以可以在充分测试的前提下进行大版本跳跃升级。
- 官方建议升级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不重启容器。 - 虽然kubernetes建议先升级master组件,然后再升级node组件,但是实际应用过程中建议先停掉controller-manager,然后升级master组件,node组件,最后再升级controller-manager,因为controller-manager中会进行一些状态的调谐(reconcile),对于actual status不符合desire status的对象会触发一些操作。升级过程中尽管我们会进行充分的测试,但是也难免出现一些非预期的情况下,例如apiserver中某些资源对象的兼容性不好,或者其中的某些字段进行调整,触发controller-manager执行非预期操作,例如重建一个deployment下所有的pod,更糟糕的是,如果此时kubelet还未升级,就可能不认识新版本一些资源对象中的新增的某些字段,此时老的pod被删掉了,但是新的pod没有起来,就造成一定的服务中断。(后面会有相关的案例)
- 升级过程中建议调高日志级别,方便定位问题,在升级完成之后在降低日志级别。
- 备份etcd数据,并进行故障恢复的演练,作为升级失败的最后一道防线。
- 升级之前review集群状态,确保所有组件正常工作,review重要业务的实例数,避免副本数为1,并且适当设置PDB; 需要检查是否有单个node带有特殊的label,可能有pod依赖于该label,如果该node异常导致pod也会发生故障(这个坑真的有人踩过:参见:ingress-cordoned)。
升级过程中发现的“坑” (已填)
- 上面的实践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到旧版本中。 - 对于一些刚从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-frequency
flag没有任何作用,如果controller-manager中配置的--node-monitor-grace-period
刚好是10s,这时候node会不断地在ready和non-ready直接摇摆,参见kubernetes/kubernetes issue#80172,该issue已经被笔者给修复掉了 :)。
写在后面
kubernetes还是在高速的迭代过程中,升级过程中出现不兼容是在所难免的, 唯有搞懂内部的实现机制才能保障集群的长治久安。发现问题也需要我们有能力去解决并且反馈到社区中,取之于社区,理应回报于社区。
reference
kubernetes集群升级的正确姿势的更多相关文章
- Kubernetes 集群升级docker版本
Kubernetes 集群升级docker版本 原则:升级完一台正常后再接着升下一台. Work Node 一.迁移上的pod(保证业务,但期间会出现抖动) kubectl drain $NODE ...
- Kubernetes集群升级(kubeadm升级方式)
1.升级前的版本确认(相同的大版本号下的小版本升级还是跨版本升级) 例如:从1.12.0升级到1.12.7 或者 从1.12.7升级到1.13.0 2.配置kubernetes安装源(已配置kuber ...
- Kubernetes 集群无损升级实践 转至元数据结尾
一.背景 活跃的社区和广大的用户群,使 Kubernetes 仍然保持3个月一个版本的高频发布节奏.高频的版本发布带来了更多的新功能落地和 bug 及时修复,但是线上环境业务长期运行,任何变更出错都可 ...
- Kubernetes实践技巧:集群升级k8s版本
更新证书 使用 kubeadm 安装 kubernetes 集群非常方便,但是也有一个比较烦人的问题就是默认的证书有效期只有一年时间,所以需要考虑证书升级的问题,本文的演示集群版本为 v1.16.2 ...
- kubernetes集群部署
鉴于Docker如此火爆,Google推出kubernetes管理docker集群,不少人估计会进行尝试.kubernetes得到了很多大公司的支持,kubernetes集群部署工具也集成了gce,c ...
- 二进制安装部署kubernetes集群---超详细教程
本文收录在容器技术学习系列文章总目录 前言:本篇博客是博主踩过无数坑,反复查阅资料,一步步搭建完成后整理的个人心得,分享给大家~~~ 本文所需的安装包,都上传在我的网盘中,需要的可以打赏博主一杯咖啡钱 ...
- 使用Kubespray部署Kubernetes集群
转载请标明出处: http://blog.csdn.net/forezp/article/details/82730382 本文出自方志朋的博客 Kubespray是Google开源的一个部署生产级别 ...
- 二进制文件方式安装kubernetes集群
所有操作全部用root使用者进行,高可用一般建议大于等于3台的奇数,我们使用3台master来做高可用 练习环境说明: 参考GitHub master: kube-apiserver,kube-con ...
- ingress-nginx 的使用 =》 部署在 Kubernetes 集群中的应用暴露给外部的用户使用
文章转载自:https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng==&mid=2247488189&idx=1&sn=8175f067 ...
随机推荐
- Ruby字符串(1):String基本用法
String字符串 字符串由String类提供,除了直接使用单双引号或其它字面量创建字符串,也可以使用String.new()方法来创建. a = "hello" b = Stri ...
- java虚拟机-JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)
一.背景 1.1 永久代(PermGen)在哪里? 根据,hotspot jvm结构如下(虚拟机栈和本地方法栈合一起了): 上图引自网络,但有个问题:方法区和heap堆都是线程共享的内存区域. 关于方 ...
- python初识(3)
bool 字符串 for循环 bool 数字非零全都是True 字符串非空全都是True 字符串 索引 从0开始 0 切片选取 [x:y] 左闭右开区间 [x:y:z] 选取x到y之间 每隔z选取一次 ...
- MacBook强制清除gardle缓存
背景:在日常的工作开发中,为了方便维护一般采用gardle+Nexus的模式管理jar包,但方便的同时也会存在一些问题 例如:test-1.0.3.jar jar包提供方修改了一些问题上传到Nexu ...
- Zimg—轻量级图片服务器搭建利器
在一个互联网应用中,图片扮演着越来越重要的角色.有稳定的可扩展的图片存储服务器就显得尤为的重要,云厂商们提供了便利的图片存储服务,花钱就可以解决了.这里简单介绍一个开源的一个分布式图片存储服务器--z ...
- 基于SpringCloud的Microservices架构实战案例-架构拆解
自第一篇< 基于SpringCloud的Microservices架构实战案例-序篇>发表出来后,差不多有半年时间了,一直也没有接着拆分完,有如读本书一样,也是需要契机的,还是要把未完成的 ...
- 从四个属性的角度来理解C语言的指针也许会更好理解
文章会在文末更新! 关于指针是什么,很多教材已经作出了定义,大多数都会定义为"存放变量内存地址的变量".从这句话中我觉得除了让我知道这个定义有11个字以外,其他就没什么用了.我个人 ...
- k8s学习 - 概念 - ReplicaSet
k8s学习 - 概念 - ReplicaSet 首先,ReplicaSet 和 ReplicationController 基本上一样,除了上篇说到的selector有不同之外,没有啥区别.(官网也是 ...
- 深入学习SpringMVC
1.什么是SpringMVC? SpringMVC是Spring框架内置的MVC的实现.SpringMVC就是一个Spring内置的MVC框架.MVC框架,它解决WEB开发中常见的问题(参数接收.文件 ...
- xx.exe 中的 0x014180bd 处有未经处理的异常: 0xC0000005: 读取位置 0xfeeefeee 时发生访问冲突(当指针访问异常时,应考虑是不是对象未创建)。
xx.exe 中的 0x014180bd 处有未经处理的异常: 0xC0000005: 读取位置 0xfeeefeee 时发生访问冲突