kubelet版本升级引起的容器重启机制与参考解决方案
背景
k8s能够帮助我们的服务实现服务高可用,其提供的副本机制能够有效的保证运行实例的副本数,从而当某个实例异常后服务可以重新被自动唤起,但在我们的生产环境中,某些特殊的服务(如广告资金服务或计费服务)因服务重启期间而导致的业务中断,对业务请求的延时响应也是不可忽略的问题;而在kubelet的部分版本升级中,也可能会因版本的升级进而导致已经运行的容器服务发生重启;而在特殊的生产环境中类似的操作直接引起的服务重启是需要我们尽可能去规避的;
此篇内容即是对kubelet版本升级与container自动重启这一关联过程原因分析以及供参考方案的简单介绍。
环境基本信息:
操作系统: CentOS 7
kubelet当前版本:1.7.4
目标kubelet版本:1.9.1
现象
当前kubelet版本1.7.4中已持续运行一些容器服务,在对kubelet版本升级1.9.1后,所升级机器上已持续运行多天的container服务出现自动重启,重启之后继续提供服务
原因
因为container的自动重启发生在kubelet版本升级后,所以需要沿kubelet启动后的接口函数调用链进行定位,以确认container重启的根本原因.
总体调用链关系图:(右下角computePodActions() 函数中containerChanged()函数即为决定container服务是否重启的判断逻辑)
高清无码图下载(https://pan.baidu.com/s/1dtMmbO)
链路跟踪过程:(整体kubelet调用关系比较复杂,后面仅针对问题的场景关键链路进行分析)
@kubernetes-1.9.1/cmd/kubelet/kubelet.go (@表示所处的源码文件,后面意义相同)
启动kubelet 的main()入口,风格上保持主入口代码最小化的设计风格
功能上仅是读取config初始化配置结构后调用真正的server中的Run()
@kubernetes-1.9.1/cmd/kubelet/app/server.go
判断启动模式,同时初始化证书相关和心跳等,以及进行健康度的检查
在RunKubelet中启动协程,进入真正的更新处理流程kubelet中的Run主体
@kubernetes-1.9.1/pkg/kubelet/kubelet.go
与场景现象相关的逻辑处理主入口,依次进行非依赖模块初始化initializeModules(“internal modules that do not require the container runtime to be up”),以及使用kubeClient与APIserver 同步node状态信息和本地网络状态及容器网络状态的检查
status、probe、pleg作用如下图示,可参照如上整体的调用链路简图
@kubernetes-1.9.1/pkg/kubelet/kubelet.go
syncLoop(),处理变化的主循环逻辑,监听3种来源的管道:file, apiserver, http;
只要有消息就会进行处理(监听的也是updates消息队列)
在syncLoopIteration迭代过程中,会根据不同的类型(config\pleg\sync…)进行相应的处理,处理是通过调用对应的Handle*()函数实现的
在各自的处理函数中,针对所有的类别,本质都是调用dispatchWork,只是传入的参数分别为不同的类别而已;如果pod已是终止状态了,则需要同时调用statusManager.TerminatePod(pod) 来通知apiserver状态的更新;
在dispatchWork函数中,会调用具体执行单元worker中UpdatePod函数,此时已将变化封装的信息交由执行单元worker。
@kubernetes-1.9.1/pkg/kubelet/pod_workers.go
在UpdatePod中,主要是启动持续处理逻辑managePodLoop(),此函数只运行一次,执行后会启动一个循环来处理syncPodFn指向函数。如果希望退出循环则如下2个函数被调用时会中止这个循环:ForgetWorker()与 ForgetNonExistingPodWorkers();控制这2个函数是否被调用则依赖于housekeepingCh 消息队列的处理。
managePodLoop会调用syncPodFn函数
syncPodFn指向kubelet.go中的syncPod处理函数。
@kubernetes-1.9.1/pkg/kubelet/kubelet.go
对于status, mkdir与volume简述如下
最后调用容器运行时的SyncPod函数,以便对POD中容器进行操作。
@ kubernetes-1.9.1/pkg/kubelet/container/runtime.go
containerRuntime.SyncPod进而调用
@ kubernetes-1.9.1/pkg/kubelet/kuberuntime/kuberuntime_manager.go
调用computePodActions()判断哪些container发生了变化,以便后面对变化的container进行处理(如重启等)
在computePodAction()中会对POD中运行的container进行判断,以此为基础来决定后面是否对这个运行的container进行重启(即决定是否重启container的入口判断逻辑)
检查运行时container与pod.spec中是否Hash一致,如果发生变化,则在changes结构中ContainersToStart 加入这个container,以便统一对ContainersToStart数组中的容器进行重启
@ kubernetes-1.9.1/pkg/kubelet/container/helpers.go
对container进行深层次HASH计算
@ kubernetes-1.9.1/pkg/util/hash/hash.go
使用spew对container对象元素进行字符拼接(github.com/davecgh/go-spew/spew)
由此可见,在版本升级启动后,定时触发的containerChanged()函数对container结构元素进行了内容上的HASH比对校验,从而将变化的container加入到ContainersToStart列表。
自syncPodFn至DeepHashObject链路如下所示:
由于DeepHashObject是对container结构的序列化进行HASH计算,所以对比升级前后版本Container结构不难发现,成员增了VolumeDevices等新成员。
所以在对container进行HASH计算时出现了新旧版本计算HASH值的不一致进而引起重启container服务。
方案
由于重启container是基于container HASH值的比较来判定的,所以可以将kubelet版本信息与启动时间记录并持久化,当container变化后计算HASH的时候,参考持久化的信息辅助决定是否忽略重启的条件。基本参考方案示例流图如下
更新源代码的补丁文件@ https://github.com/cloudusers/UpdateKubeletVersionIgnoreContainerRestart
适用场景及注意事项
适用于在kubelet版本升级情况下,container服务持续运行不希望重启的业务场景;同时因对源码的改动也引入了如下的注意事项或复杂点:
(1)版本升级前对比不同版本container结构的变化情况以便了解是否升级对容器HASH计算产生影响;
(2)目前采用的记录并缓存kubelet版本及启动时间信息至checkpoint持久化本地文件,所以版本升级前文件的损坏可能使得容器服务依旧会重新启动(因checkpoint内容较少,可以临时手动生成);
(3)在每次版本升级前需要对新版本进行源码更新重新编译生成二进制执行文件,过程稍显复杂。
综上所述,简单介绍了自kubelet启动至container服务重启的链路调用关系,同时确认版本升级后引起容器重启的判断条件及处理逻辑,以及提供可参考的避免因版本升级引起重启容器的解决方案;
另一方面简单说明了源码修改后所适用的场景及注意事项。因目前还处在研发阶段,但是我们可以发现,通过规避容器服务因版本升级导致的重启可以解决生产环境中的关键业务无服务中断的问题。
本文转自kubernetes中文社区-kubelet版本升级引起的容器重启机制与参考解决方案
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
在Kubernetes中使用Sateful Set部署Redis
面写过过几篇关于在Kubernetes上运行有状态服务相关的博文: Kubernetes资源对象之Persistent Volumes 在Kubernetes Pod中使用Ceph RBD Volume 在Kubernetes上部署有状态服务 最近需要在我们的一个Kubernetes集群上部署Redis,因此重新整理,写一下如何在Kubernetes上使用Sateful Set部署Redis。 1.需求和环境 我们的需求是需要部署三节点的Redis主从复制,并部署三个节点的Redis Sentinel实现Redis的高可用。 环境信息如下: Kubernetes 1.6.7集群 Ceph 11.2.0集群 Kubernetes的官方examples中已经给出了一个在k8s集群上部署Redis的例子Reliable, Scalable Redis on Kubernetes, 就是基于Redis主从复制+Sentinel实现的,但是这个例子是以无状态服务形式部署的,如果整个k8s集群重启了,Redis的状态就会丢失,因此不能用于生产环境。 但我们可以参考这个例子,以Satefult Se...
- 下一篇
Kubernetes 在知乎上的应用
从 Mesos 到 Kubernetes 之前的调度框架是基于 Mesos 自研的。采用的语言是 Python。运行了大概两年多的时间了,也一直比较稳定。但随着业务的增长,现有的框架的问题逐渐暴露。 调度速度遇到瓶颈,影响大业务的部署速度。 不能很好的支持有状态服务。 解决上述问题的方案有两个,一个是对现有系统进行改进重构,另一个是迁移到 Kubernetes。我们最终选择迁移到 Kubernetes,主要基于以下考虑。 Kubernetes 的架构设计简单明了,容器管理的抽像做的很好,重易进行复用和二次开发,没有必要造重复的轮子。比较典型的像Pod、Mesos 也已经引进了类似概念。 Kubernetes 已经逐渐成为业界主流。社区很活跃,新的特性不断地被添加进来,这导致 Kubernetes 变的越来越重,但基本的架构和核心功能是一直比较稳定的。 相对于 Mesos 来讲,基于 Kubernetes 的开发成本是要低一些的,尤其是在熟悉之后。便于 k8s 的推广使用。除了主要的业务运行平台 bay,我们的负载均衡平台、Kafka 平台以及定时任务平台全部都是基本 Kubernete...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Hadoop3单机部署,实现最简伪集群
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7安装Docker,走上虚拟化容器引擎之路