温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

基于Kubernetes和OpenKruise的方法是什么

发布时间:2022-01-07 14:59:13 来源:亿速云 阅读:163 作者:iii 栏目:云计算

这篇文章主要讲解了“基于Kubernetes和OpenKruise的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“基于Kubernetes和OpenKruise的方法是什么”吧!

1. 对于可变基础设施的思考

1.1 kubernetes中的可变与不可变基础设施

在云原生逐渐盛行的现在,不可变基础设施的理念已经逐渐深入人心。不可变基础设施最早是由Chad Fowler于2013年提出的,其核心思想为任何基础设施的实例一旦创建之后变成为只读状态,如需要修改和升级,则使用新的实例进行替换。这一理念的指导下,实现了运行实例的一致,因此在提升发布效率、弹性伸缩、升级回滚方面体现出了无与伦比的优势。

kubernetes是不可变基础设施理念的一个极佳实践平台。Pod作为k8s的最小单元,承担了应用实例这一角色。通过ReplicaSet从而对Pod的副本数进行控制,从而实现Pod的弹性伸缩。而进行更新时,Deployment通过控制两个ReplicaSet的副本数此消彼长,从而进行实例的整体替换,实现升级和回滚操作。

我们进一步思考,我们是否需要将Pod作为一个完全不可变的基础设施实例呢?其实在kubernetes本身,已经提供了一个替换image的功能,来实现Pod不变的情况下,通过更换image字段,实现Container的替换。这样的优势在于无需重新创建Pod,即可实现升级,直接的优势在于免去了重新调度等的时间,使得容器可以快速启动。

从这个思路延伸开来,那么我们其实可以将Pod和Container分为两层来看。将Container作为不可变的基础设施,确保应用实例的完整替换;而将Pod看为可变的基础设施,可以进行动态的改变,亦即可变层。

1.2 关于升级变化的分析

对于应用的升级变化种类,我们来进行一下分类讨论,将其分为以下几类:

升级变化类型说明
规格的变化cpu、内存等资源使用量的修改
配置的变化环境变量、配置文件等的修改
镜像的变化代码修改后镜像更新
健康检查的变化readinessProbe、livenessProbe配置的修改
其他变化调度域、标签修改等其他修改

(滑动查看完整表格)

针对不同的变化类型,我们做过一次抽样调查统计,可以看到下图的一个统计结果。

基于Kubernetes和OpenKruise的方法是什么

在一次升级变化中如果含有多个变化,则统计为多次。

可以看到支持镜像的替换可以覆盖一半左右的的升级变化,但是仍然有相当多的情况下导致不得不重新创建Pod。这点来说,不是特别友好。所以我们做了一个设计,将对于Pod的变化分为了三种Dynamic,Rebuild,Static三种。

修改类型修改类型说明修改举例对应变化类型
Dynamic 动态修改Pod不变,容器无需重建修改了健康检查端口健康检查的变化
Rebuild 原地更新Pod不变,容器需要重新创建更新了镜像、配置文件或者环境变量镜像的变化,配置的变化
Static 静态修改Pod需要重新创建修改了容器规格规格的变化

(滑动查看完整表格)

这样动态修改和原地更新的方式可以覆盖90%以上的升级变化。在Pod不变的情况下带来的收益也是显而易见的。

  1. 减少了调度、网络创建等的时间。

  2. 由于同一个应用的镜像大部分层都是复用的,大大缩短了镜像拉取的时间。

  3. 资源锁定,防止在集群资源紧缺时由于出让资源重新创建进入调度后,导致资源被其他业务抢占而无法运行。

  4. IP不变,对于很多有状态的服务十分友好。

2. Kubernetes与OpenKruise的定制

2.1 kubernetes的定制

那么如何来实现Dynamic和Rebuild更新呢?这里需要对kubernetes进行一下定制。

动态修改定制

liveness和readiness的动态修改支持相对来说较为简单,主要修改点在与prober_manager中增加了UpdatePod函数,用以判断当liveness或者readiness的配置改变时,停止原先的worker,重新启动新的worker。而后将UpdatePod嵌入到kubelet的HandlePodUpdates的流程中即可。

func (m *manager) UpdatePod(pod *v1.Pod) {  m.workerLock.Lock()  defer m.workerLock.Unlock()
 key := probeKey{podUID: pod.UID}  for _, c := range pod.Spec.Containers {    key.containerName = c.Name    {      key.probeType = readiness      worker, ok := m.workers[key]      if ok {        if c.ReadinessProbe == nil {          //readiness置空了,原worker停止          worker.stop()        } else if !reflect.DeepEqual(*worker.spec, *c.ReadinessProbe) {          //readiness配置改变了,原worker停止          worker.stop()        }      }      if c.ReadinessProbe != nil {        if !ok || (ok && !reflect.DeepEqual(*worker.spec, *c.ReadinessProbe)) {          //readiness配置改变了,启动新的worker          w := newWorker(m, readiness, pod, c)          m.workers[key] = w          go w.run()        }      }    }    {      //liveness与readiness相似      ......    }  }}

原地更新定制

kubernetes原生支持了image的修改,对于env和volume的修改是未做支持的。因此我们对env和volume也支持了修改功能,以便其可以进行环境变量和配置文件的替换。这里利用了一个小技巧,就是我们在增加了一个ExcludedHash,用于计算Container内,包含env,volume在内的各项配置。

func HashContainerExcluded(container *v1.Container) uint64 {  copyContainer := container.DeepCopy()  copyContainer.Resources = v1.ResourceRequirements{}  copyContainer.LivenessProbe = &v1.Probe{}  copyContainer.ReadinessProbe = &v1.Probe{}  hash := fnv.New32a()  hashutil.DeepHashObject(hash, copyContainer)  return uint64(hash.Sum32())}

这样当env,volume或者image发生变化时,就可以直接感知到。在SyncPod时,用于在计算computePodActions时,发现容器的相关配置发生了变化,则将该容器进行Rebuild。

func (m *kubeGenericRuntimeManager) computePodActions(pod *v1.Pod, podStatus *kubecontainer.PodStatus) podActions {  ......  for idx, container := range pod.Spec.Containers {    ......    if expectedHash, actualHash, changed := containerExcludedChanged(&container, containerStatus); changed {        // 当env,volume或者image更换时,则重建该容器。      reason = fmt.Sprintf("Container spec exclude resources hash changed (%d vs %d).", actualHash, expectedHash)            restart = true    }    ......    message := reason    if restart {      //将该容器加入到重建的列表中      message = fmt.Sprintf("%s. Container will be killed and recreated.", message)      changes.ContainersToStart = append(changes.ContainersToStart, idx)    }......  return changes}

Pod的生命周期

在Pod从调度完成到创建Running中,会有一个ContaienrCreating的状态用以标识容器在创建中。而原生中当image替换时,先前的一个容器销毁,后一个容器创建过程中,Pod状态会一直处于Running,容易有错误流量导入,用户也无法识别此时容器的状态。

因此我们为原地更新,在ContainerStatus里增加了ContaienrRebuilding的状态,同时在容器创建成功前Pod的Ready Condition置为False,以便表达容器整在重建中,应用在此期间不可用。利用此标识,可以在此期间方便识别Pod状态、隔断流量。

2.2 OpenKruise的定制

OpenKruise是阿里开源的一个项目,提供了一套在Kubernetes核心控制器之外的扩展 workload 管理和实现。其中Advanced StatefulSet,基于原生 StatefulSet 之上的增强版本,默认行为与原生完全一致,在此之外提供了原地升级、并行发布(最大不可用)、发布暂停等功能。

Advanced StatefulSet中的原地升级即与本文中的Rebuild一致,但是原生只支持替换镜像。因此我们在OpenKruise的基础上进行了定制,使其不仅可以支持image的原地更新,也可以支持当env、volume的原地更新以及livenessProbe、readinessProbe的动态更新。这个主要在shouldDoInPlaceUpdate函数中进行一下判断即可。这里就不再做代码展示了。

另外,还有个小坑,就是在pod里为了标识不同的版本,加入了controller-revision-hash值。

[root@xxx ~]# kubectl get pod -n predictor  -o yaml predictor-0 apiVersion: v1kind: Podmetadata:  labels:    controller-revision-hash: predictor-85f9455f6...

一般来说,该值应该只使用hash值作为value就可以了,但是OpenKruise中采用了{sts-name}+{hash}的方式,这带来的一个小问题就是sts-name就要因为label value的长度受到限制了。

感谢各位的阅读,以上就是“基于Kubernetes和OpenKruise的方法是什么”的内容了,经过本文的学习后,相信大家对基于Kubernetes和OpenKruise的方法是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI