Skip to content

OpenKruise部署与CloneSet

OpenKruise部署与CloneSet

目录

[TOC]

实验环境

bash
实验环境:1、win10,vmwrokstation虚机;2、k8s集群:3台centos7.61810虚机,1个master节点,2个node节点k8sversion:v1.22.2containerd:advancedcronjobs.apps.kruise.io2021-09-16T06:02:36Zbroadcastjobs.apps.kruise.io2021-09-16T06:02:36Zclonesets.apps.kruise.io2021-09-16T06:02:36Zcontainerrecreaterequests.apps.kruise.io2021-09-16T06:02:36Zdaemonsets.apps.kruise.io2021-09-16T06:02:36Zimagepulljobs.apps.kruise.io2021-09-16T06:02:36Znodeimages.apps.kruise.io2021-09-16T06:02:36Zpodunavailablebudgets.policy.kruise.io2021-09-16T06:02:36Zresourcedistributions.apps.kruise.io2021-09-16T06:02:36Zsidecarsets.apps.kruise.io2021-09-16T06:02:36Zstatefulsets.apps.kruise.io2021-09-16T06:02:36Zuniteddeployments.apps.kruise.io2021-09-16T06:02:37Zworkloadspreads.apps.kruise.io2021-09-16T06:02:37Z

其中 Kruise-manager是一个运行控制器和 webhook 的中心组件,它通过 Deployment 部署在 kruise-system命名空间中。 从逻辑上来看,如 cloneset-controllersidecarset-controller这些的控制器都是独立运行的,不过为了减少复杂度,它们都被打包在一个独立的二进制文件、并运行在 kruise-controller-manager-xxx这个 Pod 中。除了控制器之外,kruise-controller-manager-xxx中还包含了针对 Kruise CRD 以及 Pod 资源的 admission webhook。Kruise-manager会创建一些 webhook configurations 来配置哪些资源需要感知处理、以及提供一个 Service 来给 kube-apiserver 调用。

webhook它会去做一些校验。

从 v0.8.0 版本开始提供了一个新的 Kruise-daemon组件,它通过 DaemonSet 部署到每个节点上,提供镜像预热、容器重启等功能。

3、OpenKruise安装

💘 实验:OpenKruise安装

这里我们同样还是使用 Helm 方式来进行安装,需要注意从 v1.0.0 开始,OpenKruise 要求在 Kubernetes >=1.16 以上版本的集群中安装和使用。

  • 首先添加 charts 仓库:
bash
[root@master1 ~]# helm repo add openkruise https:"openkruise"hasbeenaddedtoyourrepositories[root@master1 ~]#helm repo updateHangtightwhilewegrabthelatestfromyourchartrepositories...……...Successfullygotanupdatefromthe"openkruise"chartrepositoryUpdateComplete.⎈HappyHelming!⎈
  • 我这边下载下kruise软件包(下载到本地,方便以后做实验)
bash
[root@master1 ~]#helm fetch openkruise/kruiseError:Get"https:[root@master1 ~]#helm fetch openkruise/kruise[root@master1 ~]#ls -lh kruise-1.0.1.tgz-rw-r--r--1rootroot37KMar1020:08kruise-1.0.1.tgz[root@master1 ~]#tar xf kruise-1.0.1.tgz [root@master1 ~]#ls kruiseChart.yamlREADME.mdcitemplatesvalues.yaml
  • 然后执行下面的命令安装最新版本的应用:
bash
helmupgrade--installkruiseopenkruise/kruise--version1.0.1

该 charts 在模板中默认定义了命名空间为 kruise-system,所以在安装的时候可以不用指定。

如果你的环境访问 DockerHub 官方镜像较慢,则可以使用下面的命令将镜像替换成阿里云的镜像:

bash
helmupgrade--installkruiseopenkruise/kruise--setmanager.image.repository=openkruise-registry.cn-hangzhou.cr.aliyuncs.com/openkruise/kruise-manager--version1.0.1

注意:我当时安装了第三次才安装成功,多安装几次就OK的。

注意:我们本次是直接使用默认的values.yaml文件里的参数的

  • 应用部署完成后会在 kruise-system命名空间下面运行2个 kruise-manager的 Pod,同样它们之间采用 leader-election的方式选主,同一时间只有一个提供服务,达到高可用的目的。此外还会以 DaemonSet 的形式启动 kruise-daemon组件:
bash
[root@master1 ~]#kubectl get po -nkruise-system NAMEREADYSTATUSRESTARTSAGEkruise-controller-manager-f5c9b55c5-jf4p21/1Running016mkruise-controller-manager-f5c9b55c5-lbnnz1/1Running016mkruise-daemon-26kvd1/1Running016mkruise-daemon-44ww61/1Running016mkruise-daemon-kwszd1/1Running016m

注意:这里pod启动感觉好慢啊。。。;-->原因是因为这个kruise-manager:v1.0.1镜像要77M呢,拉取比较费时间。。。;

该对象创建完成后我们可以通过 kubectl describe命令查看对应的 Events 信息,可以发现 cloneset-controller是直接创建的 Pod,这个和原生的 Deployment 就有一些区别了,Deployment 是通过 ReplicaSet 去创建的 Pod,所以从这里也可以看出来 CloneSet 是直接管理 Pod 的,3个副本的 Pod 此时也创建成功了:

CloneSet 虽然在使用上和 Deployment 比较类似,但还是有非常多比 Deployment 更高级的功能,下面我们来详细介绍下。

实验结束。😘

1.扩缩容

💘 实验:使用CloneSet控制器扩缩容

CloneSet 在扩容的时候可以通过 ScaleStrategy.MaxUnavailable来限制扩容的步长,这样可以对服务应用的影响最小,可以设置一个绝对值或百分比,如果不设置该值,则表示不限制。

  • 比如我们在上面的资源清单中添加如下所示数据:
yaml
apiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:name:cs-demospec:minReadySeconds:60scaleStrategy:maxUnavailable:1replicas:5......

上面我们配置 scaleStrategy.maxUnavailable为1,结合 minReadySeconds参数,表示在扩容时,只有当上一个扩容出的 Pod 已经 Ready 超过一分钟后,CloneSet 才会执行创建下一个 Pod。

本次测试完整yaml如下:

yaml
# 02-cloneset-increase-demo.yamlapiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:name:cs-demonamespace:defaultspec:minReadySeconds:30#本次我们设置为30s即可scaleStrategy:maxUnavailable:1replicas:5selector:matchLabels:app:cstemplate:metadata:labels:app:csspec:containers:- name:nginximage:nginx:alpineimagePullPolicy:IfNotPresentports:- containerPort:80

注意:deployment控制器滚动升级策略这里有2个配置选项:maxUnavailablemaxSurge。而CloneSet这里只有一个maxUnavailable

  • 部署
bash
$kubectlapply-f02-cloneset-increase-demo.yamlcloneset.apps.kruise.io/cs-democonfigured
  • 比如这里我们扩容成5个副本,更新上面对象后查看 CloneSet 的事件:
bash
[root@master1 ~]#kubectl describe cloneset cs-demo......Events:TypeReasonAgeFromMessage-------------------------NormalSuccessfulCreate9hcloneset-controllersucceedtocreatepodcs-demo-4wz2zNormalSuccessfulCreate9hcloneset-controllersucceedtocreatepodcs-demo-cg8btNormalSuccessfulCreate9hcloneset-controllersucceedtocreatepodcs-demo-mdqwnWarningScaleUpLimited33scloneset-controllerscaleUpislimitedbecauseofscaleStrategy.maxUnavailable,limit:1NormalSuccessfulCreate32scloneset-controllersucceedtocreatepodcs-demo-w4zlsWarningScaleUpLimited29s(x5 over32s) cloneset-controller scaleUp is limited because of scaleStrategy.maxUnavailable,limit:0 NormalSuccessfulCreate0scloneset-controllersucceedtocreatepodcs-demo-h6cfn

可以看到第一时间扩容了一个 Pod,由于我们配置了 minReadySeconds:60,也就是新扩容的 Pod 创建成功超过1分钟后才会扩容另外一个 Pod,上面的 Events 信息也能表现出来,查看 Pod 的 AGE也能看出来扩容的2个 Pod 之间间隔了1分钟左右:

bash
[root@master1 ~]#kubectl get po -l app=csNAMEREADYSTATUSRESTARTSAGEcs-demo-4wz2z1/1Running09hcs-demo-cg8bt1/1Running09hcs-demo-h6cfn1/1Running056scs-demo-mdqwn1/1Running09hcs-demo-w4zls1/1Running088s

当 CloneSet 被缩容时,我们还可以指定一些 Pod 来删除,这对于 StatefulSet 或者 Deployment 来说是无法实现的。StatefulSet 是根据序号来删除 Pod,而 Deployment/ReplicaSet 目前只能根据控制器里定义的排序来删除。而 CloneSet 允许用户在缩小 replicas 数量的同时,指定想要删除的 Pod 名字。

  • 如下所示:
yaml
apiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:name:cs-demospec:minReadySeconds:60scaleStrategy:maxUnavailable:1podsToDelete:- cs-demo-79rcxreplicas:4......

更新上面的资源对象后,会将应用缩到4个 Pod,如果在 podsToDelete列表中指定了 Pod 名字,则控制器会优先删除这些 Pod,对于已经被删除的 Pod,控制器会自动从 podsToDelete列表中清理掉

完整yaml如下:

yaml
# 03-cloneset-decrease-demo.yamlapiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:name:cs-demonamespace:defaultspec:minReadySeconds:30#本次我们设置为30s即可scaleStrategy:maxUnavailable:1podsToDelete:- cs-demo-4wz2z#指定要删除的podreplicas:4#replicas修改为4selector:matchLabels:app:cstemplate:metadata:labels:app:csspec:containers:- name:nginximage:nginx:alpineimagePullPolicy:IfNotPresentports:- containerPort:80
  • 比如我们更新上面的资源对象后 cs-demo-79rcx这个 Pod 会被移除,其余会保留下来:
bash
$kubectlapply-f03-cloneset-decrease-demo.yamlcloneset.apps.kruise.io/cs-democonfigured[root@master1 ~]#kubectl get po -l app=csNAMEREADYSTATUSRESTARTSAGEcs-demo-cg8bt1/1Running010hcs-demo-h6cfn1/1Running04m45scs-demo-mdqwn1/1Running010hcs-demo-w4zls1/1Running05m17s[root@master1 ~]#kubectl describe cloneset cs-demo Events:TypeReasonAgeFromMessage-------------------------NormalSuccessfulCreate10hcloneset-controllersucceedtocreatepodcs-demo-4wz2zNormalSuccessfulCreate10hcloneset-controllersucceedtocreatepodcs-demo-cg8btNormalSuccessfulCreate10hcloneset-controllersucceedtocreatepodcs-demo-mdqwnWarningScaleUpLimited5m36scloneset-controllerscaleUpislimitedbecauseofscaleStrategy.maxUnavailable,limit:1NormalSuccessfulCreate5m35scloneset-controllersucceedtocreatepodcs-demo-w4zlsWarningScaleUpLimited5m32s(x5 over5m35s) cloneset-controller scaleUp is limited because of scaleStrategy.maxUnavailable,limit:0 NormalSuccessfulCreate5m3scloneset-controllersucceedtocreatepodcs-demo-h6cfnNormalSuccessfulDelete42scloneset-controllersucceedtodeletepodcs-demo-4wz2z
  • 如果你只把 Pod 名字加到 podsToDelete,但没有修改 replicas 数量,那么控制器会先把指定的 Pod 删掉,然后再扩一个新的 Pod。

  • 另一种直接删除 Pod 的方式是在要删除的 Pod 上打 apps.kruise.io/specified-delete:true标签。

相比于手动直接删除 Pod,使用 podsToDeleteapps.kruise.io/specified-delete:true方式会有 CloneSet 的 maxUnavailable/maxSurge来保护删除, 并且会触发 PreparingDelete生命周期的钩子(这里关于生命周期可以看下官方文档,里面有中文,也很详细)。

实验结束。😘

2.升级

pod原地升级概念

  • ReCreate:删除旧 Pod 和它的 PVC,然后用新版本重新创建出来,这是默认的方式。 (注意:deployment这里默认的升级策略是滚动升级,但这里CloneSet默认的策略是ReCreate。)
  • InPlaceIfPossible:会优先尝试原地升级 Pod,如果不行再采用重建升级
  • InPlaceOnly:只允许采用原地升级,因此,用户只能修改上一条中的限制字段,如果尝试修改其他字段会被拒绝

注意:原地升级是只针对你单独一个容器的;

🍀 这里有一个重要概念:原地升级,这也是 OpenKruise 提供的核心功能之一,当我们要升级一个 Pod 中镜像的时候,下图展示了重建升级原地升级的区别:

重建升级时我们需要删除旧 Pod、创建新 Pod:

  • Pod 名字和 uid 发生变化,因为它们是完全不同的两个 Pod 对象(比如 Deployment 升级)
  • Pod 名字可能不变、但 uid 变化,因为它们是不同的 Pod 对象,只是复用了同一个名字(比如 StatefulSet 升级)
  • Pod 所在 Node 名字可能发生变化,因为新 Pod 很可能不会调度到之前所在的 Node 节点
  • Pod IP 发生变化,因为新 Pod 很大可能性是不会被分配到之前的 IP 地址

但是对于原地升级,我们仍然复用同一个 Pod 对象,只是修改它里面的字段:

  • 可以避免如_调度_、分配 IP、_挂载盘_等额外的操作和代价
  • 更快的镜像拉取,因为会复用已有旧镜像的大部分 layer 层,只需要拉取新镜像变化的一些 layer
  • 当一个容器在原地升级时,Pod 中的其他容器不会受到影响,仍然维持运行

所以显然如果能用原地升级方式来升级我们的工作负载,对在线应用的影响是最小的。上面我们提到 CloneSet 升级类型支持 InPlaceIfPossible,这意味着 Kruise 会尽量对 Pod 采取原地升级,如果不能则退化到重建升级,以下的改动会被允许执行原地升级:

  • 更新 workload 中的 spec.template.metadata.*,比如 labels/annotations,Kruise 只会将 metadata 中的改动更新到存量 Pod 上。
  • 更新 workload 中的 spec.template.spec.containers[x].image,Kruise 会原地升级 Pod 中这些容器的镜像,而不会重建整个 Pod。
  • 从 Kruise v1.0 版本开始,更新 spec.template.metadata.labels/annotations并且 container 中有配置 env from 这些改动的 labels/anntations,Kruise 会原地升级这些容器来生效新的 env 值。

否则,其他字段的改动,比如 spec.template.spec.containers[x].envspec.template.spec.containers[x].resources,都是会回退为重建升级。

注意:[root@master1 ~]#kubectl explain pod.spec.containers

💘 实验:使用CloneSet控制器升级

  • 比如我们将上面的应用升级方式设置为 InPlaceIfPossible,只需要在资源清单中添加 spec.updateStrategy.type:InPlaceIfPossible即可:
yaml
apiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:name:cs-demospec:updateStrategy:type:InPlaceIfPossible......

完整yaml如下:

yaml
# 04-cloneset-InPlaceIfPossible-demo.yamlapiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:name:cs-demonamespace:defaultspec:minReadySeconds:30updateStrategy:type:InPlaceIfPossible#修改1:指定为最大限度的原地升级的方式scaleStrategy:maxUnavailable:1replicas:4selector:matchLabels:app:cstemplate:metadata:labels:app:csspec:containers:- name:nginximage:nginx:1.7.9#修改2:把镜像版本替换了imagePullPolicy:IfNotPresentports:- containerPort:80

更新后可以发现 Pod 的状态并没有发生什么大的变化,名称、IP 都一样,唯一变化的是镜像 tag:

bash
$kubectlapply-f04-cloneset-InPlaceIfPossible-demo.yamlcloneset.apps.kruise.io/cs-democonfigured[root@master1 ~]#kubectl get po -l app=csNAMEREADYSTATUSRESTARTSAGEcs-demo-cg8bt1/1Running013hcs-demo-h6cfn1/1Running1(63s ago) 3h32mcs-demo-mdqwn1/1Running013hcs-demo-w4zls1/1Running1(31s ago) 3h33m[root@master1 ~]#kubectl get po -l app=csNAMEREADYSTATUSRESTARTSAGEcs-demo-cg8bt1/1Running1(3s ago) 13hcs-demo-h6cfn1/1Running1(101s ago) 3h33mcs-demo-mdqwn1/1Running1(36s ago) 13hcs-demo-w4zls1/1Running1(69s ago) 3h33m[root@master1 ~]#kubectl describe cloneset cs-demo ……Events:TypeReasonAgeFromMessage-------------------------NormalSuccessfulUpdatePodInPlace2m30scloneset-controllersuccessfullyupdatepodcs-demo-h6cfnin-place(revisioncs-demo-7cb9c88699) NormalSuccessfulUpdatePodInPlace118scloneset-controllersuccessfullyupdatepodcs-demo-w4zlsin-place(revisioncs-demo-7cb9c88699) NormalSuccessfulUpdatePodInPlace85scloneset-controllersuccessfullyupdatepodcs-demo-mdqwnin-place(revisioncs-demo-7cb9c88699) NormalSuccessfulUpdatePodInPlace52scloneset-controllersuccessfullyupdatepodcs-demo-cg8btin-place(revisioncs-demo-7cb9c88699) [root@master1 ~]#kubectl describe po cs-demo-cg8bt......Events:TypeReasonAgeFromMessage-------------------------NormalCreated3m10s(x2 over13h) kubelet Created container nginxNormalStarted3m10s(x2 over13h) kubelet Started container nginxNormalKilling3m10skubeletContainernginxdefinitionchanged,willberestartedNormalPulled3m10skubeletContainerimage"nginx:1.7.9"alreadypresentonmachine[root@master1 ~]#kubectl get po cs-demo-cg8bt -oyaml|grepimageapps.kruise.io/inplace-update-state:'{"revision":"cs-demo-7cb9c88699","updateTimestamp":"2022-03-11T03:20:45Z","lastContainerStatuses":{"nginx":{"imageID":"docker.io/library/nginx@sha256:eb05700fe7baa6890b74278e39b66b2ed1326831f9ec3ed4bdc6361a4ac2f333"}}}'-image:nginx:1.7.9imagePullPolicy:IfNotPresentimage:docker.io/library/nginx:1.7.9imageID:sha256:35d28df486f6150fa3174367499d1eb01f22f5a410afe4b9581ac0e0e58b3eaf

这就是原地升级的效果。

🍀 原地升级整体工作流程如下图所示:

注意:conditions是在pod的status里面的选项;

注意:kubectl explain cloneset.spec.updateStrategy.type

🍀 ``PreDownloadImageForInPlaceUpdate这个 feature-gate

如果你在安装或升级 Kruise 的时候启用了 PreDownloadImageForInPlaceUpdate这个 feature-gate,CloneSet 控制器会自动在所有旧版本 pod 所在节点上预热你正在灰度发布的新版本镜像,这对于应用发布加速很有帮助。

默认情况下 CloneSet 每个新镜像预热时的并发度都是 1,也就是一个个节点拉镜像,如果需要调整,你可以在 CloneSet annotation 上设置并发度:

yaml
apiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:annotations:apps.kruise.io/image-predownload-parallelism:"5"

注意,为了避免大部分不必要的镜像拉取,目前只针对 replicas >3的 CloneSet 做自动预热。

🍀 CloneSet 还支持分批进行灰度

此外 CloneSet 还支持分批进行灰度,在 updateStrategy属性中可以配置 partition参数,该参数可以用来保留旧版本 Pod 的数量或百分比,默认为0:

  • 如果是数字,控制器会将 (replicas - partition)数量的 Pod 更新到最新版本
  • 如果是百分比,控制器会将 (replicas *(100% - partition))数量的 Pod 更新到最新版本

比如,我们将上面示例中的的 image 更新为 nginx:latest并且设置 partition=2:

完整yaml如下:

yaml
# 05-cloneset-updateStrategy-partition.yamlapiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:name:cs-demonamespace:defaultspec:minReadySeconds:30updateStrategy:type:InPlaceIfPossiblepartition:2#修改1:保留旧版本pod的数量scaleStrategy:maxUnavailable:1replicas:4selector:matchLabels:app:cstemplate:metadata:labels:app:csspec:containers:- name:nginximage:nginx:latest#修改2:把镜像版本替换了imagePullPolicy:IfNotPresentports:- containerPort:80

更新后,过一会查看可以发现只升级了2个 Pod:

bash
$kubectlapply-f05-cloneset-updateStrategy-partition.yamlcloneset.apps.kruise.io/cs-democonfigured[root@master1 ~]#kubectl get pods -l app=cs-Lcontroller-revision-hashNAMEREADYSTATUSRESTARTSAGECONTROLLER-REVISION-HASHcs-demo-cg8bt1/1Running2(3m3s ago) 16h cs-demo-7c4d79f5bc #能够看到这个控制器的hash值发生了变化cs-demo-h6cfn1/1Running1(3h13m ago) 6h44m cs-demo-7cb9c88699cs-demo-mdqwn1/1Running2(2m29s ago) 16h cs-demo-7c4d79f5bccs-demo-w4zls1/1Running1(3h12m ago) 6h45m cs-demo-7cb9c88699[root@master1 ~]#kubectl describe po cs-demo-cg8btEvents:TypeReasonAgeFromMessage-------------------------NormalKilling10m(x2 over3h18m) kubelet Container nginx definition changed,will be restartedNormalPulled10mkubeletContainerimage"nginx:latest"alreadypresentonmachineNormalCreated10m(x3 over16h) kubelet Created container nginxNormalStarted10m(x3 over16h) kubelet Started container nginx
  • 此外 CloneSet 还支持一些更高级的用法,比如可以定义优先级策略来控制 Pod 发布的优先级规则,还可以定义策略来将一类 Pod 打散到整个发布过程中,也可以暂停 Pod 发布等操作。

kubectl explain cloneset.spec.updateStrategy

https:

版权:此文章版权归 One 所有,如有转载,请注明出处!

链接:可点击右上角分享此页面复制文章链接

上次更新时间:

最近更新