OpenKruise部署与CloneSet
OpenKruise部署与CloneSet
目录
[TOC]
实验环境
实验环境: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-controller
、sidecarset-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 仓库:
[root@master1 ~]# helm repo add openkruise https:"openkruise"hasbeenaddedtoyourrepositories[root@master1 ~]#helm repo updateHangtightwhilewegrabthelatestfromyourchartrepositories...……...Successfullygotanupdatefromthe"openkruise"chartrepositoryUpdateComplete.⎈HappyHelming!⎈
- 我这边下载下
kruise
软件包(下载到本地,方便以后做实验)
[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
- 然后执行下面的命令安装最新版本的应用:
➜helmupgrade--installkruiseopenkruise/kruise--version1.0.1
该 charts 在模板中默认定义了命名空间为 kruise-system
,所以在安装的时候可以不用指定。
如果你的环境访问 DockerHub 官方镜像较慢,则可以使用下面的命令将镜像替换成阿里云的镜像:
➜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
组件:
[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呢,拉取比较费时间。。。;
- 如果不想使用默认的参数进行安装,也可以自定义配置,可配置的 values 值可以参考 charts 文档 https:apiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:name:cs-demonamespace:defaultspec:replicas:3selector:matchLabels:app:cstemplate:metadata:labels:app:csspec:containers:- name:nginximage:nginx:alpineimagePullPolicy:IfNotPresentports:- containerPort:80
- 直接创建上面的这个 CloneSet 对象:
$kubectlapply-f01-cloneset-demo.yamlcloneset.apps.kruise.io/cs-democreated[root@master1 ~]#kubectl get cloneset cs-demoNAMEDESIREDUPDATEDUPDATED_READYREADYTOTALAGEcs-demo3333346s[root@master1 ~]#kubectl describe cloneset cs-demoName:cs-demoNamespace:defaultLabels:<none>Annotations:<none>APIVersion:apps.kruise.io/v1alpha1Kind:CloneSet……UpdatedReplicas:3Events:TypeReasonAgeFromMessage-------------------------NormalSuccessfulCreate69scloneset-controllersucceedtocreatepodcs-demo-4wz2zNormalSuccessfulCreate69scloneset-controllersucceedtocreatepodcs-demo-cg8btNormalSuccessfulCreate69scloneset-controllersucceedtocreatepodcs-demo-mdqwn[root@master1 ~]#
该对象创建完成后我们可以通过 kubectl describe
命令查看对应的 Events 信息,可以发现 cloneset-controller
是直接创建的 Pod,这个和原生的 Deployment 就有一些区别了,Deployment 是通过 ReplicaSet 去创建的 Pod,所以从这里也可以看出来 CloneSet 是直接管理 Pod 的,3个副本的 Pod 此时也创建成功了:
[root@master1 ~]#kubectl get po -l app=csNAMEREADYSTATUSRESTARTSAGEcs-demo-4wz2z1/1Running02m30scs-demo-cg8bt1/1Running02m30scs-demo-mdqwn1/1Running02m30s[root@master1 ~]#kubectl get po cs-demo-4wz2z -oyaml……ownerReferences:-apiVersion:apps.kruise.io/v1alpha1blockOwnerDeletion:truecontroller:truekind:CloneSet#从这里也可以看到pod是由cloneset-controller直接创建出来的!name:cs-demouid:c07bf739-1f89-4ac6-9c1f-401b96350920……
CloneSet 虽然在使用上和 Deployment 比较类似,但还是有非常多比 Deployment 更高级的功能,下面我们来详细介绍下。
- 注意:这个
volumeClaimTemplates
之前不是在statefuleset控制器
里出现的吗,这里怎么也出现在了deployment控制里
了呢。-->可能还是有一些比较变态的业务场景,它需要只针对这1/2个pod做配置,也就是把这容器当成了传统的业务虚机来看待使用。
实验结束。😘
1.扩缩容
💘 实验:使用CloneSet控制器扩缩容
CloneSet 在扩容的时候可以通过 ScaleStrategy.MaxUnavailable
来限制扩容的步长,这样可以对服务应用的影响最小,可以设置一个绝对值或百分比,如果不设置该值,则表示不限制。
- 比如我们在上面的资源清单中添加如下所示数据:
apiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:name:cs-demospec:minReadySeconds:60scaleStrategy:maxUnavailable:1replicas:5......
上面我们配置 scaleStrategy.maxUnavailable
为1,结合 minReadySeconds
参数,表示在扩容时,只有当上一个扩容出的 Pod 已经 Ready 超过一分钟后,CloneSet 才会执行创建下一个 Pod。
本次测试完整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个配置选项:maxUnavailable
和maxSurge
。而CloneSet
这里只有一个maxUnavailable
。
- 部署
$kubectlapply-f02-cloneset-increase-demo.yamlcloneset.apps.kruise.io/cs-democonfigured
- 比如这里我们扩容成5个副本,更新上面对象后查看 CloneSet 的事件:
[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分钟左右:
[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 名字。
- 如下所示:
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如下:
# 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 会被移除,其余会保留下来:
$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,使用
podsToDelete
或apps.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].env
或 spec.template.spec.containers[x].resources
,都是会回退为重建升级。
注意:[root@master1 ~]#kubectl explain pod.spec.containers
💘 实验:使用CloneSet控制器升级
- 比如我们将上面的应用升级方式设置为
InPlaceIfPossible
,只需要在资源清单中添加spec.updateStrategy.type:InPlaceIfPossible
即可:
apiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:name:cs-demospec:updateStrategy:type:InPlaceIfPossible......
完整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:
$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 上设置并发度:
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如下:
# 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:
$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