Skip to content

OpenKruise运维增强控制器

OpenKruise运维增强控制器

目录

[TOC]

实验环境

bash
实验环境:1、win10,vmwrokstation虚机;2、k8s集群:3台centos7.61810虚机,1个master节点,2个node节点k8sversion:v1.22.2containerd:apiVersion:apps.kruise.io/v1alpha1kind:SidecarSetmetadata:name:test-sidecarsetspec:selector:matchLabels:#去匹配pod的标签app:nginxupdateStrategy:type:RollingUpdatemaxUnavailable:1containers:- name:sidecar1image:busyboxcommand:["sleep","999d"]volumeMounts:- name:log-volumemountPath:/var/logvolumes:# this field will be merged into pod.spec.volumes- name:log-volumeemptyDir:{}
  • 直接创建这个资源对象即可:
bash
$kubectlapply-f01-sidecarset.yamlsidecarset.apps.kruise.io/test-sidecarsetcreated[root@master1 ~]#kubectl get sidecarsetNAMEMATCHEDUPDATEDREADYAGEtest-sidecarset00030s

需要注意上面我们在定义 SidecarSet 对象的时候里面有一个非常重要的属性就是 label selector,会去匹配具有 app=nginx的 Pod,然后向其中注入下面定义的 sidecar1这个容器。

  • 比如定义如下所示的一个 Pod,该 Pod 中包含 app=nginx的标签,这样可以和上面的 SidecarSet 对象匹配:
yaml
#02-test-pod.yamlapiVersion:v1kind:Podmetadata:labels:app:nginx# 匹配sidercarset中的label selector标签name:test-podspec:containers:- name:appimage:nginx:1.7.9
  • 直接创建上面的资源对象:
bash
$kubectlapply-f02-test-pod.yamlpod/test-podcreated[root@master1 ~]#kubectl get po test-pod NAMEREADYSTATUSRESTARTSAGEtest-pod2/2Running07s

可以看到该 Pod 中有2个容器,被自动注入了上面定义的 sidecar1容器:

bash
[root@master1 ~]#kubectl describe po test-podEvents:TypeReasonAgeFromMessage-------------------------NormalScheduled45sdefault-schedulerSuccessfullyassigneddefault/test-podtonode2NormalPulling45skubeletPullingimage"busybox"NormalPulled44skubeletSuccessfullypulledimage"busybox"in495.870587msNormalCreated44skubeletCreatedcontainersidecar1NormalStarted44skubeletStartedcontainersidecar1NormalPulled44skubeletContainerimage"nginx:1.7.9"alreadypresentonmachineNormalCreated44skubeletCreatedcontainerappNormalStarted44skubeletStartedcontainerapp[root@master1 ~]#kubectl get po test-pod -oyaml……spec:containers:-command:-sleep-999denv:-name:IS_INJECTEDvalue:"true"image:busyboximagePullPolicy:Alwaysname:sidecar1resources:{}terminationMessagePath:/dev/termination-logterminationMessagePolicy:FilevolumeMounts:-mountPath:/var/logname:log-volume-mountPath:/var/run/secrets/kubernetes.io/serviceaccountname:kube-api-access-k4cqjreadOnly:true-image:nginx:1.7.9imagePullPolicy:IfNotPresentname:appresources:{}terminationMessagePath:/dev/termination-logterminationMessagePolicy:FilevolumeMounts:-mountPath:/var/run/secrets/kubernetes.io/serviceaccountname:kube-api-access-k4cqjreadOnly:true……
  • 现在我们去更新 SidecarSet 中的 sidecar 容器镜像替换成 busybox:1.35.0
bash
[root@master1 ~]# kubectl patch sidecarset test-sidecarset --type='json'-p='[{"op":"replace","path":"/spec/containers/0/image","value":"busybox:1.35.0"}]'sidecarset.apps.kruise.io/test-sidecarsetpatched

更新后再去查看 Pod 中的 sidecar 容器:

bash
[root@master1 ~]#kubectl get poNAMEREADYSTATUSRESTARTSAGEtest-pod2/2Running1(25s ago) 8m35s[root@master1 ~]#kubectl describe po test-pod......Events:TypeReasonAgeFromMessage-------------------------NormalScheduled8m28sdefault-schedulerSuccessfullyassigneddefault/test-podtonode2NormalPulling8m28skubeletPullingimage"busybox"NormalPulled8m27skubeletSuccessfullypulledimage"busybox"in495.870587msNormalPulled8m27skubeletContainerimage"nginx:1.7.9"alreadypresentonmachineNormalCreated8m27skubeletCreatedcontainerappNormalStarted8m27skubeletStartedcontainerappNormalKilling49skubeletContainersidecar1definitionchanged,willberestartedNormalPulling19skubeletPullingimage"busybox:1.35.0"NormalStarted1s(x2 over8m27s) kubelet Started container sidecar1NormalCreated1s(x2 over8m27s) kubelet Created container sidecar1NormalPulled1skubeletSuccessfullypulledimage"busybox:1.35.0"in17.221245188s[root@master1 ~]#kubectl get po test-pod -oyaml|grepbusyboxkruise.io/sidecarset-inplace-update-state:'{"test-sidecarset":{"revision":"f78z4854d9855xd6478fzx9c84645z2548v24z26455db46bdfzw44v49v98f2cw","updateTimestamp":"2022-03-12T01:55:56Z","lastContainerStatuses":{"sidecar1":{"imageID":"docker.io/library/busybox@sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678"}}}}'image:busybox:1.35.0image:docker.io/library/busybox:1.35.0imageID:docker.io/library/busybox@sha256:130df6999605f982ec67e5bee29d3a52614a075e949490f0a41702ee1dd98f3f

可以看到 Pod 中的 sidecar 容器镜像被原地升级成 busybox:1.35.0了, 对主容器没有产生任何影响。

实验结束。😘

1.同意特性

需要注意的是 sidecar 的注入只会发生在 Pod 创建阶段,并且只有 Pod spec 会被更新,不会影响 Pod 所属的 workload template 模板spec.containers除了默认的 k8s container 字段,还扩展了如下一些字段,来方便注入:

yaml
apiVersion:apps.kruise.io/v1alpha1kind:SidecarSetmetadata:name:sidecarsetspec:selector:matchLabels:app:samplecontainers:# 默认的K8s容器字段- name:nginximage:nginx:alpinevolumeMounts:- mountPath:/nginx/confname:nginx.conf# 扩展的sidecar容器字段podInjectPolicy:BeforeAppContainershareVolumePolicy:type:disabled |enabledtransferEnv:- sourceContainerName:mainenvName:PROXY_IPvolumes:- Name:nginx.confhostPath:/data/nginx/conf
  • podInjectPolicy:定义了容器 注入到pod.spec.containers中的位置

    • BeforeAppContainer:表示注入到 pod 原 containers 的前面((默认) )
    • AfterAppContainer: 表示注入到 pod 原 containers 的后面
  • 数据卷共享

    • 共享指定卷:通过 spec.volumes来定义 sidecar 自身需要的 volume
    • 共享所有卷:通过 spec.containers[i].shareVolumePolicy.type =enabled |disabled来控制是否挂载 pod 应用容器的卷,常用于日志收集等 sidecar,配置为 enabled后会把应用容器中所有挂载点注入 sidecar 同一路经下(sidecar 中本身就有声明的数据卷和挂载点除外)
  • 环境变量共享:可以通过 spec.containers[i].transferEnv来从别的容器获取环境变量,会把名为 sourceContainerName容器中名为 envName的环境变量拷贝到本容器

🍀 SidecarSet 不仅支持 sidecar 容器的原地升级,而且提供了非常丰富的升级、灰度策略。同样在 SidecarSet 对象中 updateStrategy属性下面也可以配置 partition来定义保留旧版本 Pod 的数量或百分比,默认为 0;同样还可以配置的有 maxUnavailable属性,表示在发布过程中的最大不可用数量。

  • {matched pod}=100,partition=50,maxUnavailable=10,控制器会发布 50 个 Pod 到新版本,但是同一时间只会发布 10 个 Pod,每发布好一个 Pod 才会再找一个发布,直到 50 个发布完成。
  • {matched pod}=100,partition=80,maxUnavailable=30,控制器会发布 20 个 Pod 到新版本,因为满足 maxUnavailable 数量,所以这 20 个 Pod 会同时发布。

🍀 同样也可以设置 paused:true来暂停发布,此时对于新创建的、扩容的 pod 依旧会实现注入能力,已经更新的 pod 会保持更新后的版本不动,还没有更新的 pod 会暂停更新。

yaml
apiVersion:apps.kruise.io/v1alpha1kind:SidecarSetmetadata:name:sidecarsetspec:# ...updateStrategy:type:RollingUpdatemaxUnavailable:20%partition:10paused:true

2.金丝雀发布

对于有金丝雀发布需求的业务,可以通过 selector来实现,对于需要率先金丝雀灰度的 pod 打上固定的 [canary.release] =true的标签,再通过 selector.matchLabels来选中该 pod 即可。

💘 实验:金丝雀发布

  • 比如现在我们有一个3副本的 Pod,也具有 app=nginx的标签,如下所示
yaml
#03-deployment.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:nginxnamespace:defaultspec:replicas:3revisionHistoryLimit:3selector:matchLabels:app:nginxtemplate:metadata:labels:app:nginxspec:containers:- name:ngximage:nginx:1.7.9ports:- containerPort:80
  • 创建后现在就具有4个 app=nginx标签的 Pod 了,由于都匹配上面创建的 SidecarSet 对象,所以都会被注入一个 sidecar1的容器,镜像为 busybox
bash
$kubectlapply-f03-deployment.yamldeployment.apps/nginxcreated[root@master1 ~]#kubectl get pods -l app=nginxNAMEREADYSTATUSRESTARTSAGEnginx-6457955f7-bt5fk2/2Running036snginx-6457955f7-jphvr2/2Running036snginx-6457955f7-zjvh92/2Running036stest-pod2/2Running1(127m ago) 135m
  • 现在如果我们想为 test-pod这个应用来执行灰度策略,将 sidecar 容器镜像更新成 busybox:1.35.0,则可以在 updateStrategy下面添加 selector.matchLabels属性 canary.release:"true"

注意:我这里就直接在01-sidecarset.yaml上面直接修改了。另外,我之前原本test-pod里的busybox镜像就已经是busybox:1.35.0了,本次yaml里写成busybox即可。

yaml
# 01-sidecarset.yamlapiVersion:apps.kruise.io/v1alpha1kind:SidecarSetmetadata:name:test-sidecarsetspec:selector:matchLabels:app:nginxupdateStrategy:type:RollingUpdatemaxUnavailable:1selector:matchLabels:# 修改1:canary.release:"true"containers:- name:sidecar1image:busybox# 修改2:command:["sleep","999d"]volumeMounts:- name:log-volumemountPath:/var/logvolumes:# this field will be merged into pod.spec.volumes- name:log-volumeemptyDir:{}
  • 然后同样需要给 test-pod 添加上 canary.release=true这个标签:
yaml
#直接在02-test-pod文件里添加即可apiVersion:v1kind:Podmetadata:labels:app:nginxcanary.release:"true"name:test-podspec:containers:- name:appimage:nginx:1.7.9
  • 更新后可以发现 test-pod 的 sidecar 镜像更新了,其他 Pod 没有变化,这样就实现了 sidecar 的灰度功能:
bash
$kubectlapply-f02-test-pod.yamlpod/test-podconfigured$kubectlapply-f01-sidecarset.yamlsidecarset.apps.kruise.io/test-sidecarsetconfigured[root@master1 ~]#kubectl get po -l app=nginxNAMEREADYSTATUSRESTARTSAGEnginx-6457955f7-bt5fk2/2Running07m34snginx-6457955f7-jphvr2/2Running07m34snginx-6457955f7-zjvh92/2Running07m34stest-pod2/2Running2(10s ago) 142m [root@master1 ~]#kubectl describe po test-podEvents:TypeReasonAgeFromMessage-------------------------NormalKilling74s(x2 over135m) kubelet Container sidecar1 definition changed,will be restartedNormalPulling44s(x2 over142m) kubelet Pulling image "busybox"NormalCreated43s(x3 over142m) kubelet Created container sidecar1NormalStarted43s(x3 over142m) kubelet Started container sidecar1NormalPulled43skubeletSuccessfullypulledimage"busybox"in505.082017ms#符合预期[root@master1 ~]#kubectl get po test-pod -oyaml|grepbusyboxkruise.io/sidecarset-inplace-update-state:'{"test-sidecarset":{"revision":"f78z4854d9855xd6478fzx9c84645z2548v24z26455db46bdfzw44v49v98f2cw","updateTimestamp":"2022-03-12T01:55:56Z","lastContainerStatuses":{"sidecar1":{"imageID":"docker.io/library/busybox@sha256:130df6999605f982ec67e5bee29d3a52614a075e949490f0a41702ee1dd98f3f"}}}}'image:busyboximage:docker.io/library/busybox:latestimageID:docker.io/library/busybox@sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678

实验结束。😘

3.热升级

💘 实验:热升级

SidecarSet 原地升级会先停止旧版本的容器,然后创建新版本的容器,这种方式适合不影响 Pod 服务可用性的 sidecar 容器,比如说日志收集的 Agent。

但是对于很多代理或运行时的 sidecar 容器,例如 Istio Envoy,这种升级方法就有问题了,Envoy 作为 Pod 中的一个代理容器,代理了所有的流量,如果直接重启,Pod 服务的可用性会受到影响,如果需要单独升级 envoy sidecar,就需要复杂的优雅终止和协调机制,所以我们为这种 sidecar 容器的升级提供了一种新的解决方案。

  • 编写04-sidecarset-hot.yaml
yaml
#04-sidecarset-hot.yamlapiVersion:apps.kruise.io/v1alpha1kind:SidecarSetmetadata:name:hotupgrade-sidecarsetspec:selector:matchLabels:app:hotupgradecontainers:- name:sidecarimage:openkruise/hotupgrade-sample:sidecarv1imagePullPolicy:Alwayslifecycle:postStart:exec:command:- /bin/sh- /migrate.sh#在主容器启动之前,做一些网络方面的迁移upgradeStrategy:upgradeType:HotUpgradehotUpgradeEmptyImage:openkruise/hotupgrade-sample:empty
  • upgradeType:HotUpgrade代表该 sidecar 容器的类型是热升级方案
  • hotUpgradeEmptyImage:当热升级 sidecar 容器时,业务必须要提供一个 empty 容器用于热升级过程中的容器切换,empty 容器同 sidecar 容器具有相同的配置(除了镜像地址),例如:command、lifecycle、probe 等,但是它不做任何工作。
  • lifecycle.postStart:在 postStart 这个 hook 中完成热升级过程中的状态迁移,该脚本需要由业务根据自身的特点自行实现,例如:nginx 热升级需要完成 Listen FD 共享以及 reload 操作。

整体来说热升级特性总共包含以下两个过程:

  • Pod 创建时,注入热升级容器
  • 原地升级时,完成热升级流程

注入热升级容器

Pod 创建时,SidecarSet Webhook 将会注入两个容器:

  • {sidecarContainer.name}-1:如下图所示 envoy-1,这个容器代表正在实际工作的 sidecar 容器,例如:envoy:1.16.0
  • {sidecarContainer.name}-2:如下图所示 envoy-2,这个容器是业务配置的 hotUpgradeEmptyImage 容器,例如:empty:1.0,用于后面的热升级机制

热升级流程

热升级流程主要分为三个步骤:

  • Upgrade:将 empty 容器升级为当前最新的 sidecar 容器,例如:envoy-2.Image =envoy:1.17.0
  • Migration:lifecycle.postStart完成热升级流程中的状态迁移,当迁移完成后退出
  • Reset:状态迁移完成后,热升级流程将设置 envoy-1 容器为 empty 镜像,例如:envoy-1.Image =empty:1.0

上述三个步骤完成了热升级中的全部流程,当对 Pod 执行多次热升级时,将重复性的执行上述三个步骤。

empty这个空容器的目的就是占位。

  • 部署上面04-sidecarset-hot.yaml资源
bash
$kubectlapply-f04-sidecarset-hot.yamlsidecarset.apps.kruise.io/hotupgrade-sidecarsetcreated[root@master1 ~]#kubectl get sidecarsetNAMEMATCHEDUPDATEDREADYAGEhotupgrade-sidecarset00020s
  • 这里我们以 OpenKruise 的官方示例来进行说明,首先创建上面的 hotupgrade-sidecarset这个 SidecarSet。然后创建一个如下所示的 CloneSet 对象:
yaml
#05-hotupgrade-CloneSet.yamlapiVersion:apps.kruise.io/v1alpha1kind:CloneSetmetadata:labels:app:hotupgradename:busyboxspec:replicas:1selector:matchLabels:app:hotupgradetemplate:#下面是pod模板metadata:labels:app:hotupgrade#这个标签要和上面的sidercarset匹配spec:containers:- name:busyboximage:openkruise/hotupgrade-sample:busybox
  • 创建完成后,CloneSet 管理的 Pod 已经注入 sidecar-1sidecar-2两个容器:
bash
#部署$kubectlapply-f05-hotupgrade-CloneSet.yamlcloneset.apps.kruise.io/busyboxcreated[root@master1 ~]#kubectl get po -l app=hotupgradeNAMEREADYSTATUSRESTARTSAGEbusybox-695lv3/3Running071s[root@master1 ~]#kubectl describe po busybox-695lvEvents:TypeReasonAgeFromMessage-------------------------NormalScheduled119sdefault-schedulerSuccessfullyassigneddefault/busybox-695lvtonode2NormalPulling119skubeletPullingimage"openkruise/hotupgrade-sample:sidecarv1"NormalPulled116skubeletSuccessfullypulledimage"openkruise/hotupgrade-sample:sidecarv1"in2.430148491sNormalCreated116skubeletCreatedcontainersidecar-1NormalStarted116skubeletStartedcontainersidecar-1NormalPulling111skubeletPullingimage"openkruise/hotupgrade-sample:empty"NormalPulled93skubeletSuccessfullypulledimage"openkruise/hotupgrade-sample:empty"in17.732956857sNormalCreated93skubeletCreatedcontainersidecar-2NormalStarted93skubeletStartedcontainersidecar-2NormalPulling93skubeletPullingimage"openkruise/hotupgrade-sample:busybox"NormalPulled90skubeletSuccessfullypulledimage"openkruise/hotupgrade-sample:busybox"in2.81313947sNormalCreated90skubeletCreatedcontainerbusybox[root@master1 ~]#kubectl get po busybox-695lv -oyaml……spec:containers:-env:-name:IS_INJECTEDvalue:"true"-name:SIDECARSET_VERSIONvalueFrom:fieldRef:apiVersion:v1fieldPath:metadata.annotations['version.sidecarset.kruise.io/sidecar-1']-name:SIDECARSET_VERSION_ALTvalueFrom:fieldRef:apiVersion:v1fieldPath:metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-1']image:openkruise/hotupgrade-sample:sidecarv1imagePullPolicy:Alwayslifecycle:postStart:exec:command:-/bin/sh-/migrate.shname:sidecar-1resources:{}terminationMessagePath:/dev/termination-logterminationMessagePolicy:FilevolumeMounts:-mountPath:/var/run/secrets/kubernetes.io/serviceaccountname:kube-api-access-n9lpfreadOnly:true-env:-name:IS_INJECTEDvalue:"true"-name:SIDECARSET_VERSIONvalueFrom:fieldRef:apiVersion:v1fieldPath:metadata.annotations['version.sidecarset.kruise.io/sidecar-2']-name:SIDECARSET_VERSION_ALTvalueFrom:fieldRef:apiVersion:v1fieldPath:metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-2']image:openkruise/hotupgrade-sample:emptyimagePullPolicy:Alwayslifecycle:postStart:exec:command:-/bin/sh-/migrate.shname:sidecar-2resources:{}terminationMessagePath:/dev/termination-logterminationMessagePolicy:FilevolumeMounts:-mountPath:/var/run/secrets/kubernetes.io/serviceaccountname:kube-api-access-n9lpfreadOnly:true-image:openkruise/hotupgrade-sample:busyboximagePullPolicy:Alwaysname:busyboxresources:{}terminationMessagePath:/dev/termination-logterminationMessagePolicy:FilevolumeMounts:-mountPath:/var/run/secrets/kubernetes.io/serviceaccountname:kube-api-access-n9lpfreadOnly:true……
  • busybox 主容器每100毫秒会请求一次sidecar(version=v1)服务:
bash
[root@master1 ~]#kubectl logs -f busybox-695lv -c busyboxI031211:51:49.1008321main.go:39]requestsidecarserversuccess,andresponse(body=Thisisversion(v1) sidecar)I031211:51:49.2128471main.go:39]requestsidecarserversuccess,andresponse(body=Thisisversion(v1) sidecar)......
  • 现在我们去升级 sidecar 容器,将镜像修改为 openkruise/hotupgrade-sample:sidecarv2
bash
[root@master1 ~]#kubectl patch sidecarset hotupgrade-sidecarset --type='json'-p='[{"op":"replace","path":"/spec/containers/0/image","value":"openkruise/hotupgrade-sample:sidecarv2"}]'sidecarset.apps.kruise.io/hotupgrade-sidecarsetpatched
  • 更新后再去观察 pod 的状态,可以看到 sidecar-2 镜像正常更新了:
bash
[root@master1 ~]#kubectl get po -l app=hotupgradeNAMEREADYSTATUSRESTARTSAGEbusybox-695lv3/3Running2(103s ago) 8m38s[root@master1 ~]#kubectl describe po busybox-695lvEvents:TypeReasonAgeFromMessage-------------------------NormalScheduled9m10sdefault-schedulerSuccessfullyassigneddefault/busybox-695lvtonode2NormalPulling9m10skubeletPullingimage"openkruise/hotupgrade-sample:sidecarv1"NormalPulled9m7skubeletSuccessfullypulledimage"openkruise/hotupgrade-sample:sidecarv1"in2.430148491sNormalPulling9m2skubeletPullingimage"openkruise/hotupgrade-sample:empty"NormalPulling8m44skubeletPullingimage"openkruise/hotupgrade-sample:busybox"NormalPulled8m44skubeletSuccessfullypulledimage"openkruise/hotupgrade-sample:empty"in17.732956857sNormalStarted8m41skubeletStartedcontainerbusyboxNormalCreated8m41skubeletCreatedcontainerbusyboxNormalPulled8m41skubeletSuccessfullypulledimage"openkruise/hotupgrade-sample:busybox"in2.81313947sNormalKilling2m49skubeletContainersidecar-2definitionchanged,willberestartedNormalPulling2m48skubeletPullingimage"openkruise/hotupgrade-sample:sidecarv2"NormalPulled2m27skubeletSuccessfullypulledimage"openkruise/hotupgrade-sample:sidecarv2"in21.791000213sNormalCreated2m27s(x2 over8m44s) kubelet Created container sidecar-2NormalStarted2m26s(x2 over8m44s) kubelet Started container sidecar-2NormalKilling2m16skubeletContainersidecar-1definitionchanged,willberestartedNormalResetContainerSucceed2m16ssidecarset-controllerresetsidecarcontainerimageemptysuccessfullyNormalPulling2m15skubeletPullingimage"openkruise/hotupgrade-sample:empty"NormalStarted2m(x2 over9m7s) kubelet Started container sidecar-1NormalCreated2m(x2 over9m7s) kubelet Created container sidecar-1NormalPulled2mkubeletSuccessfullypulledimage"openkruise/hotupgrade-sample:empty"in15.229447021s[root@master1 ~]#kubectl get po busybox-695lv -oyaml……spec:containers:-env:-name:IS_INJECTEDvalue:"true"-name:SIDECARSET_VERSIONvalueFrom:fieldRef:apiVersion:v1fieldPath:metadata.annotations['version.sidecarset.kruise.io/sidecar-1']-name:SIDECARSET_VERSION_ALTvalueFrom:fieldRef:apiVersion:v1fieldPath:metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-1']image:openkruise/hotupgrade-sample:emptyimagePullPolicy:Alwayslifecycle:postStart:exec:command:-/bin/sh-/migrate.shname:sidecar-1resources:{}terminationMessagePath:/dev/termination-logterminationMessagePolicy:FilevolumeMounts:-mountPath:/var/run/secrets/kubernetes.io/serviceaccountname:kube-api-access-n9lpfreadOnly:true-env:-name:IS_INJECTEDvalue:"true"-name:SIDECARSET_VERSIONvalueFrom:fieldRef:apiVersion:v1fieldPath:metadata.annotations['version.sidecarset.kruise.io/sidecar-2']-name:SIDECARSET_VERSION_ALTvalueFrom:fieldRef:apiVersion:v1fieldPath:metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-2']image:openkruise/hotupgrade-sample:sidecarv2imagePullPolicy:Alwayslifecycle:postStart:exec:command:-/bin/sh-/migrate.shname:sidecar-2resources:{}terminationMessagePath:/dev/termination-logterminationMessagePolicy:FilevolumeMounts:-mountPath:/var/run/secrets/kubernetes.io/serviceaccountname:kube-api-access-n9lpfreadOnly:true-image:openkruise/hotupgrade-sample:busyboximagePullPolicy:Alwaysname:busyboxresources:{}terminationMessagePath:/dev/termination-logterminationMessagePolicy:FilevolumeMounts:-mountPath:/var/run/secrets/kubernetes.io/serviceaccountname:kube-api-access-n9lpfreadOnly:true……

并且在更新过程中观察 busybox 容器仍然会不断请求 sidecar 服务,但是并没有失败的请求出现:

bash
[root@master1 ~]#kubectl logs -f busybox-695lv -c busybox

🍀 整个热升级示例代码可以参考仓库的实现:https:kind:ContainerRecreateRequestmetadata:namespace:pod-namespacename:xxxspec:podName:pod-namecontainers:# 要重建的容器名字列表,至少要有 1 个- name:app- name:sidecarstrategy:failurePolicy:Fail# 'Fail'或 'Ignore',表示一旦有某个容器停止或重建失败, CRR 立即结束orderedRecreate:false# 'true'表示要等前一个容器重建完成了,再开始重建下一个terminationGracePeriodSeconds:30# 等待容器优雅退出的时间,不填默认用 Pod 中定义的unreadyGracePeriodSeconds:3# 在重建之前先把 Pod 设为 not ready,并等待这段时间后再开始执行重建minStartedSeconds:10# 重建后新容器至少保持运行这段时间,才认为该容器重建成功activeDeadlineSeconds:300# 如果 CRR 执行超过这个时间,则直接标记为结束(未结束的容器标记为失败)ttlSecondsAfterFinished:1800# CRR 结束后,过了这段时间自动被删除掉

一般来说,列表中的容器会一个个被停止,但可能同时在被重建和启动,除非 orderedRecreate被设置为 true。 unreadyGracePeriodSeconds功能依赖于 KruisePodReadinessGate这个 feature-gate,后者会在每个 Pod 创建的时候注入一个 readinessGate,否则,默认只会给 Kruise workload 创建的 Pod 注入 readinessGate,也就是说只有这些 Pod 才能在 CRR 重建时使用 unreadyGracePeriodSeconds

3、ImagePullJob

NodeImageImagePullJob是从 Kruise v0.8.0 版本开始提供的 CRD。Kruise 会自动为每个 Node 创建一个 NodeImage,它包含了哪些镜像需要在这个 Node 上做预热,比如我们这里3个节点,则会自动创建3个 NodeImage 对象:

bash
kubectlgetnodeimageNAMEDESIREDPULLINGSUCCEEDFAILEDAGEmaster1000010dnode1000010dnode2000010d
  • 比如我们查看 node1 节点上的 NodeImage 对象:
bash
kruisekubectlgetnodeimagenode1-oyamlapiVersion:apps.kruise.io/v1alpha1kind:NodeImagemetadata:labels:beta.kubernetes.io/arch:amd64beta.kubernetes.io/os:linuxkubernetes.io/arch:amd64kubernetes.io/hostname:node1kubernetes.io/os:linuxname:node1spec:{}status:desired:0failed:0pulling:0succeeded:0
  • 比如我们希望在这个节点上拉去一个 ubuntu:latest镜像,则可以按照如下所示的去修改 spec:
yaml
......spec:images:ubuntu:# 镜像 nametags:- tag:latest# 镜像 tagpullPolicy:ttlSecondsAfterFinished:300# [required] 拉取完成(成功或失败)超过 300s 后,将这个任务从 NodeImage 中清除timeoutSeconds:600# [optional] 每一次拉取的超时时间,默认为 600backoffLimit:3# [optional] 拉取的重试次数,默认为 3activeDeadlineSeconds:1200# [optional] 整个任务的超时时间,无默认值

更新后我们可以从 status 中看到拉取进度以及结果,并且你会发现拉取完成 600s 后任务会被清除。

🍀 此外用户可以创建 ImagePullJob对象,来指定一个镜像要在哪些节点上做预热。

  • 比如创建如下所示的 ImagePullJob资源对象:
yaml
apiVersion:apps.kruise.io/v1alpha1kind:ImagePullJobmetadata:name:job-with-alwaysspec:image:nginx:1.9.1# [required] 完整的镜像名 name:tagparallelism:10# [optional] 最大并发拉取的节点数量,默认为 1selector:# [optional] 指定节点的 名字列表 或 标签选择器 (只能设置其中一种)names:- node-1- node-2matchLabels:node-type:xxx# podSelector:# [optional] pod label 选择器来在这些 pod 所在节点上拉取镜像,与 selector 不能同时设置.# pod-label:xxxcompletionPolicy:type:Always# [optional] 默认为 AlwaysactiveDeadlineSeconds:1200# [optional] 无默认值,只对 Alway 类型生效ttlSecondsAfterFinished:300# [optional] 无默认值,只对 Alway 类型生效pullPolicy:# [optional] 默认 backoffLimit=3,timeoutSeconds=600backoffLimit:3timeoutSeconds:300pullSecrets:#secret是命名空间级别的,当然ImagePullJob也是命名空间级别的。- secret-name1- secret-name2

我们可以在 selector字段中指定节点的名字列表或标签选择器 (只能设置其中一种),如果没有设置 selector 则会选择所有节点做预热。或者可以配置 podSelector来在这些 pod 所在节点上拉取镜像,podSelector 与 selector 不能同时设置。

同时,ImagePullJob 有两种 completionPolicy类型:

  • Always:表示这个 job 是一次性预热,不管成功、失败都会结束
  • activeDeadlineSeconds:整个 job 的 deadline 结束时间
  • ttlSecondsAfterFinished:结束后超过这个时间,自动清理删除 job
  • Never:表示这个 job 是长期运行、不会结束,并且会每天都会在匹配的节点上重新预热一次指定的镜像

同样如果你要预热的镜像来自私有仓库,则可以通过 pullSecrets来指定仓库的 Secret 信息。

4、容器启动顺序

Container Launch Priority提供了控制一个 Pod 中容器启动顺序的方法。通常来说 Pod 容器的启动和退出顺序是由 Kubelet 管理的,Kubernetes 曾经有一个 KEP计划在 container 中增加一个 type 字段来标识不同类型容器的启停优先级,但是由于sig-node 考虑到对现有代码架构的改动太大,所以将该提案拒绝了。

这个功能作用在 Pod 对象上,不管它的 owner 是什么类型的,因此可以适用于 Deployment、CloneSet 以及其他的 workload 种类。

🍀 比如我们可以设置按照容器顺序启动,只需要在 Pod 中定义一个 apps.kruise.io/container-launch-priority的注解即可:

yaml
apiVersion:v1kind:Podannotations:apps.kruise.io/container-launch-priority:Orderedspec:containers:- name:sidecar# ...- name:main# ...

Kruise 会保证前面的容器(sidecar)会在后面容器(main)之前启动。

🍀 此外我们还可以按自定义顺序启动,但是需要在 Pod 容器中添加 KRUISE_CONTAINER_PRIORITY这个环境变量:

yaml
apiVersion:v1kind:Podspec:containers:- name:main# ...- name:sidecarenv:- name:KRUISE_CONTAINER_PRIORITYvalue:"1"# ...

该环境变量值的范围在 [-2147483647,2147483647],不写默认是 0,权重高的容器,会保证在权重低的容器之前启动,但是需要注意相同权重的容器不保证启动顺序。

🍀 除了这些常用的增强控制器之外 OpenKruise 还有很多高级的特性,可以前往官网 https:

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

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

上次更新时间:

最近更新