image-20220312092321012

目录

实验环境

实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
   k8s version:v1.22.2
   containerd://1.5.5

实验软件

链接:https://pan.baidu.com/s/17XzNnSRYEGEPJf2a-SjgPA?pwd=f7ol
提取码:f7ol
2022.3.12-51.OpenKruise运维增强控制器-实验代码

image-20220312215132059

1、SidecarSet

SidecarSet 支持通过 admission webhook 来自动为集群中创建的符合条件的 Pod 注入 sidecar 容器。除了在 Pod 创建时候注入外,SidecarSet 还提供了为 Pod 原地升级其中已经注入的 sidecar 容器镜像的能力。SidecarSet 将 sidecar 容器的定义和生命周期与业务容器解耦,它主要用于管理无状态的 sidecar 容器,比如监控、日志等 agent。

⚠️ 注意:istios里就是这样干的,给pod里注入一个envop sidercar容器,然后对流量进行拦截。😋

💘 实验:SidercarSet控制器使用测试(测试成功)

  • 比如我们定义一个如下所示的 SidecarSet 资源对象:
# 01-sidecarset.yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: test-sidecarset
spec:
  selector: 
    matchLabels: #去匹配pod的标签
      app: nginx
  updateStrategy:
    type: RollingUpdate
    maxUnavailable: 1
  containers:
  - name: sidecar1
    image: busybox
    command: ["sleep", "999d"]
    volumeMounts:
    - name: log-volume
      mountPath: /var/log
  volumes: # this field will be merged into pod.spec.volumes
  - name: log-volume
    emptyDir: {}
  • 直接创建这个资源对象即可:
$ kubectl apply -f 01-sidecarset.yaml
sidecarset.apps.kruise.io/test-sidecarset created

[root@master1 ~]#kubectl get sidecarset
NAME              MATCHED   UPDATED   READY   AGE
test-sidecarset   0         0         0       30s

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

  • 比如定义如下所示的一个 Pod,该 Pod 中包含 app=nginx 的标签,这样可以和上面的 SidecarSet 对象匹配:
#02-test-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx # 匹配sidercarset中的label selector标签
  name: test-pod
spec:
  containers:
  - name: app
    image: nginx:1.7.9
  • 直接创建上面的资源对象:
$ kubectl apply -f 02-test-pod.yaml  
pod/test-pod created

[root@master1 ~]#kubectl get po test-pod 
NAME       READY   STATUS    RESTARTS   AGE
test-pod   2/2     Running   0          7s

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

[root@master1 ~]#kubectl describe po test-pod
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  45s   default-scheduler  Successfully assigned default/test-pod to node2
  Normal  Pulling    45s   kubelet            Pulling image "busybox"
  Normal  Pulled     44s   kubelet            Successfully pulled image "busybox" in 495.870587ms
  Normal  Created    44s   kubelet            Created container sidecar1
  Normal  Started    44s   kubelet            Started container sidecar1
  Normal  Pulled     44s   kubelet            Container image "nginx:1.7.9" already present on machine
  Normal  Created    44s   kubelet            Created container app
  Normal  Started    44s   kubelet            Started container app

[root@master1 ~]#kubectl get po test-pod  -oyaml
……
spec:
  containers:
  - command:
    - sleep
    - 999d
    env:
    - name: IS_INJECTED
      value: "true"
    image: busybox
    imagePullPolicy: Always
    name: sidecar1
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/log
      name: log-volume
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-k4cqj
      readOnly: true
  - image: nginx:1.7.9
    imagePullPolicy: IfNotPresent
    name: app
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-k4cqj
      readOnly: true
……
  • 现在我们去更新 SidecarSet 中的 sidecar 容器镜像替换成 busybox:1.35.0
[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-sidecarset patched

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

[root@master1 ~]#kubectl get po
NAME       READY   STATUS    RESTARTS      AGE
test-pod   2/2     Running   1 (25s ago)   8m35s


[root@master1 ~]#kubectl describe po test-pod
......
Events:
  Type    Reason     Age                 From               Message
  ----    ------     ----                ----               -------
  Normal  Scheduled  8m28s               default-scheduler  Successfully assigned default/test-pod to node2
  Normal  Pulling    8m28s               kubelet            Pulling image "busybox"
  Normal  Pulled     8m27s               kubelet            Successfully pulled image "busybox" in 495.870587ms
  Normal  Pulled     8m27s               kubelet            Container image "nginx:1.7.9" already present on machine
  Normal  Created    8m27s               kubelet            Created container app
  Normal  Started    8m27s               kubelet            Started container app
  Normal  Killing    49s                 kubelet            Container sidecar1 definition changed, will be restarted
  Normal  Pulling    19s                 kubelet            Pulling image "busybox:1.35.0"
  Normal  Started    1s (x2 over 8m27s)  kubelet            Started container sidecar1
  Normal  Created    1s (x2 over 8m27s)  kubelet            Created container sidecar1
  Normal  Pulled     1s                  kubelet            Successfully pulled image "busybox:1.35.0" in 17.221245188s
  
[root@master1 ~]#kubectl get po test-pod -oyaml|grep busybox
    kruise.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.0
    image: docker.io/library/busybox:1.35.0
    imageID: docker.io/library/busybox@sha256:130df6999605f982ec67e5bee29d3a52614a075e949490f0a41702ee1dd98f3f

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

实验结束。😘

1.同意特性

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

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  selector:
    matchLabels:
      app: sample
  containers:
    # 默认的K8s容器字段
  - name: nginx
    image: nginx:alpine
    volumeMounts:
    - mountPath: /nginx/conf
      name: nginx.conf
    # 扩展的sidecar容器字段
    podInjectPolicy: BeforeAppContainer
    shareVolumePolicy:
      type: disabled | enabled
    transferEnv:
    - sourceContainerName: main
      envName: PROXY_IP
  volumes:
  - Name: nginx.conf
    hostPath: /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 会暂停更新。

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  # ...
  updateStrategy:
    type: RollingUpdate
    maxUnavailable: 20%
    partition: 10
    paused: true

2.金丝雀发布

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

💘 实验:金丝雀发布(测试成功)
  • 比如现在我们有一个3副本的 Pod,也具有 app=nginx 的标签,如下所示
#03-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
spec:
  replicas: 3
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: ngx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
  • 创建后现在就具有4个 app=nginx 标签的 Pod 了,由于都匹配上面创建的 SidecarSet 对象,所以都会被注入一个 sidecar1 的容器,镜像为 busybox
$ kubectl apply -f 03-deployment.yaml
deployment.apps/nginx created

[root@master1 ~]#kubectl get pods -l app=nginx
NAME                    READY   STATUS    RESTARTS       AGE
nginx-6457955f7-bt5fk   2/2     Running   0              36s
nginx-6457955f7-jphvr   2/2     Running   0              36s
nginx-6457955f7-zjvh9   2/2     Running   0              36s
test-pod                2/2     Running   1 (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即可。

# 01-sidecarset.yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: test-sidecarset
spec:
  selector:
    matchLabels:
      app: nginx
  updateStrategy:
    type: RollingUpdate
    maxUnavailable: 1
    selector:
      matchLabels: # 修改1:
        canary.release: "true"    
  containers:
  - name: sidecar1
    image: busybox # 修改2:
    command: ["sleep", "999d"]
    volumeMounts:
    - name: log-volume
      mountPath: /var/log
  volumes: # this field will be merged into pod.spec.volumes
  - name: log-volume
    emptyDir: {}
  • 然后同样需要给 test-pod 添加上 canary.release=true 这个标签:
#直接在02-test-pod文件里添加即可
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: nginx
    canary.release: "true"
  name: test-pod
spec:
  containers:
  - name: app
    image: nginx:1.7.9
  • 更新后可以发现 test-pod 的 sidecar 镜像更新了,其他 Pod 没有变化,这样就实现了 sidecar 的灰度功能:
$ kubectl apply -f 02-test-pod.yaml
pod/test-pod configured
$ kubectl apply -f 01-sidecarset.yaml 
sidecarset.apps.kruise.io/test-sidecarset configured


[root@master1 ~]#kubectl get po -l app=nginx
NAME                    READY   STATUS    RESTARTS      AGE  
nginx-6457955f7-bt5fk   2/2     Running   0             7m34s
nginx-6457955f7-jphvr   2/2     Running   0             7m34s
nginx-6457955f7-zjvh9   2/2     Running   0             7m34s
test-pod                2/2     Running   2 (10s ago)   142m 

[root@master1 ~]#kubectl describe po test-pod
Events:
  Type    Reason   Age                 From     Message
  ----    ------   ----                ----     -------
  Normal  Killing  74s (x2 over 135m)  kubelet  Container sidecar1 definition changed, will be restarted
  Normal  Pulling  44s (x2 over 142m)  kubelet  Pulling image "busybox"
  Normal  Created  43s (x3 over 142m)  kubelet  Created container sidecar1
  Normal  Started  43s (x3 over 142m)  kubelet  Started container sidecar1
  Normal  Pulled   43s                 kubelet  Successfully pulled image "busybox" in 505.082017ms

#符合预期
[root@master1 ~]#kubectl get po test-pod -oyaml|grep busybox
    kruise.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: busybox
    image: docker.io/library/busybox:latest
    imageID: docker.io/library/busybox@sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678  

实验结束。😘

3.热升级

💘 实验:热升级(测试成功)

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

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

  • 编写04-sidecarset-hot.yaml
#04-sidecarset-hot.yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: hotupgrade-sidecarset
spec:
  selector:
    matchLabels:
      app: hotupgrade
  containers:
  - name: sidecar
    image: openkruise/hotupgrade-sample:sidecarv1
    imagePullPolicy: Always
    lifecycle:
      postStart:
        exec:
          command:
          - /bin/sh
          - /migrate.sh #在主容器启动之前,做一些网络方面的迁移
    upgradeStrategy:
      upgradeType: HotUpgrade
      hotUpgradeEmptyImage: 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资源
$ kubectl apply -f 04-sidecarset-hot.yaml 
sidecarset.apps.kruise.io/hotupgrade-sidecarset created

[root@master1 ~]#kubectl get sidecarset
NAME                    MATCHED   UPDATED   READY   AGE
hotupgrade-sidecarset   0         0         0       20s
  • 这里我们以 OpenKruise 的官方示例来进行说明,首先创建上面的 hotupgrade-sidecarset 这个 SidecarSet。然后创建一个如下所示的 CloneSet 对象:
#05-hotupgrade-CloneSet.yaml
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
  labels:
    app: hotupgrade
  name: busybox
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hotupgrade
  template: #下面是pod模板
    metadata:
      labels: 
        app: hotupgrade #这个标签要和上面的sidercarset匹配
    spec:
      containers:
        - name: busybox
          image: openkruise/hotupgrade-sample:busybox
  • 创建完成后,CloneSet 管理的 Pod 已经注入 sidecar-1sidecar-2 两个容器:
#部署
$ kubectl apply -f 05-hotupgrade-CloneSet.yaml 
cloneset.apps.kruise.io/busybox created

[root@master1 ~]#kubectl get po -l app=hotupgrade
NAME            READY   STATUS    RESTARTS   AGE
busybox-695lv   3/3     Running   0          71s

[root@master1 ~]#kubectl describe po busybox-695lv
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  119s  default-scheduler  Successfully assigned default/busybox-695lv to node2
  Normal  Pulling    119s  kubelet            Pulling image "openkruise/hotupgrade-sample:sidecarv1"
  Normal  Pulled     116s  kubelet            Successfully pulled image "openkruise/hotupgrade-sample:sidecarv1" in 2.430148491s
  Normal  Created    116s  kubelet            Created container sidecar-1
  Normal  Started    116s  kubelet            Started container sidecar-1
  Normal  Pulling    111s  kubelet            Pulling image "openkruise/hotupgrade-sample:empty"
  Normal  Pulled     93s   kubelet            Successfully pulled image "openkruise/hotupgrade-sample:empty" in 17.732956857s
  Normal  Created    93s   kubelet            Created container sidecar-2
  Normal  Started    93s   kubelet            Started container sidecar-2
  Normal  Pulling    93s   kubelet            Pulling image "openkruise/hotupgrade-sample:busybox"
  Normal  Pulled     90s   kubelet            Successfully pulled image "openkruise/hotupgrade-sample:busybox" in 2.81313947s
  Normal  Created    90s   kubelet            Created container busybox

[root@master1 ~]#kubectl get po busybox-695lv -oyaml
……
spec:
  containers:
  - env:
    - name: IS_INJECTED
      value: "true"
    - name: SIDECARSET_VERSION
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.annotations['version.sidecarset.kruise.io/sidecar-1']
    - name: SIDECARSET_VERSION_ALT
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-1']
    image: openkruise/hotupgrade-sample:sidecarv1
    imagePullPolicy: Always
    lifecycle:
      postStart:
        exec:
          command:
          - /bin/sh
          - /migrate.sh
    name: sidecar-1
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-n9lpf
      readOnly: true
  - env:
    - name: IS_INJECTED
      value: "true"
    - name: SIDECARSET_VERSION
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.annotations['version.sidecarset.kruise.io/sidecar-2']
    - name: SIDECARSET_VERSION_ALT
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-2']
    image: openkruise/hotupgrade-sample:empty
    imagePullPolicy: Always
    lifecycle:
      postStart:
        exec:
          command:
          - /bin/sh
          - /migrate.sh
    name: sidecar-2
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-n9lpf
      readOnly: true
  - image: openkruise/hotupgrade-sample:busybox
    imagePullPolicy: Always
    name: busybox
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-n9lpf
      readOnly: true
……
  • busybox 主容器每100毫秒会请求一次sidecar(version=v1)服务:
[root@master1 ~]#kubectl logs -f busybox-695lv -c busybox
I0312 11:51:49.100832       1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar)
I0312 11:51:49.212847       1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar)
......
  • 现在我们去升级 sidecar 容器,将镜像修改为 openkruise/hotupgrade-sample:sidecarv2
[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-sidecarset patched
  • 更新后再去观察 pod 的状态,可以看到 sidecar-2 镜像正常更新了:
[root@master1 ~]#kubectl get po -l app=hotupgrade
NAME            READY   STATUS    RESTARTS       AGE  
busybox-695lv   3/3     Running   2 (103s ago)   8m38s

[root@master1 ~]#kubectl describe po busybox-695lv
Events:
  Type    Reason                 Age                    From                   Message
  ----    ------                 ----                   ----                   -------
  Normal  Scheduled              9m10s                  default-scheduler      Successfully assigned default/busybox-695lv to node2
  Normal  Pulling                9m10s                  kubelet                Pulling image "openkruise/hotupgrade-sample:sidecarv1"
  Normal  Pulled                 9m7s                   kubelet                Successfully pulled image "openkruise/hotupgrade-sample:sidecarv1" in 2.430148491s
  Normal  Pulling                9m2s                   kubelet                Pulling image "openkruise/hotupgrade-sample:empty"
  Normal  Pulling                8m44s                  kubelet                Pulling image "openkruise/hotupgrade-sample:busybox"
  Normal  Pulled                 8m44s                  kubelet                Successfully pulled image "openkruise/hotupgrade-sample:empty" in 17.732956857s
  Normal  Started                8m41s                  kubelet                Started container busybox
  Normal  Created                8m41s                  kubelet                Created container busybox
  Normal  Pulled                 8m41s                  kubelet                Successfully pulled image "openkruise/hotupgrade-sample:busybox" in 2.81313947s
  Normal  Killing                2m49s                  kubelet                Container sidecar-2 definition changed, will be restarted
  Normal  Pulling                2m48s                  kubelet                Pulling image "openkruise/hotupgrade-sample:sidecarv2"
  Normal  Pulled                 2m27s                  kubelet                Successfully pulled image "openkruise/hotupgrade-sample:sidecarv2" in 21.791000213s
  Normal  Created                2m27s (x2 over 8m44s)  kubelet                Created container sidecar-2
  Normal  Started                2m26s (x2 over 8m44s)  kubelet                Started container sidecar-2
  Normal  Killing                2m16s                  kubelet                Container sidecar-1 definition changed, will be restarted
  Normal  ResetContainerSucceed  2m16s                  sidecarset-controller  reset sidecar container image empty successfully
  Normal  Pulling                2m15s                  kubelet                Pulling image "openkruise/hotupgrade-sample:empty"
  Normal  Started                2m (x2 over 9m7s)      kubelet                Started container sidecar-1
  Normal  Created                2m (x2 over 9m7s)      kubelet                Created container sidecar-1
  Normal  Pulled                 2m                     kubelet                Successfully pulled image "openkruise/hotupgrade-sample:empty" in 15.229447021s


[root@master1 ~]#kubectl get po busybox-695lv -oyaml
……
spec:
  containers:
  - env:
    - name: IS_INJECTED
      value: "true"
    - name: SIDECARSET_VERSION
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.annotations['version.sidecarset.kruise.io/sidecar-1']
    - name: SIDECARSET_VERSION_ALT
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-1']
    image: openkruise/hotupgrade-sample:empty
    imagePullPolicy: Always
    lifecycle:
      postStart:
        exec:
          command:
          - /bin/sh
          - /migrate.sh
    name: sidecar-1
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-n9lpf
      readOnly: true
  - env:
    - name: IS_INJECTED
      value: "true"
    - name: SIDECARSET_VERSION
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.annotations['version.sidecarset.kruise.io/sidecar-2']
    - name: SIDECARSET_VERSION_ALT
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-2']
    image: openkruise/hotupgrade-sample:sidecarv2
    imagePullPolicy: Always
    lifecycle:
      postStart:
        exec:
          command:
          - /bin/sh
          - /migrate.sh
    name: sidecar-2
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-n9lpf
      readOnly: true
  - image: openkruise/hotupgrade-sample:busybox
    imagePullPolicy: Always
    name: busybox
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-n9lpf
      readOnly: true
……

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

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

image-20220312195352437

🍀 整个热升级示例代码可以参考仓库的实现:https://github.com/openkruise/samples/tree/master/hotupgrade。

实验结束。😘

2、Container Restart

ContainerRecreateRequest 控制器可以帮助用户重启/重建存量 Pod 中一个或多个容器。和 Kruise 提供的原地升级类似,当一个容器重建的时候,Pod 中的其他容器还保持正常运行,重建完成后,Pod 中除了该容器的 restartCount 增加以外不会有什么其他变化。

为要重建容器的 Pod 提交一个 ContainerRecreateRequest 自定义资源(缩写 CRR):

apiVersion: apps.kruise.io/v1alpha1
kind: ContainerRecreateRequest
metadata:
  namespace: pod-namespace
  name: xxx
spec:
  podName: pod-name
  containers:       # 要重建的容器名字列表,至少要有 1 个
  - name: app
  - name: sidecar
  strategy:
    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 对象:

➜ kubectl get nodeimage
NAME      DESIRED   PULLING   SUCCEED   FAILED   AGE
master1   0         0         0         0        10d
node1     0         0         0         0        10d
node2     0         0         0         0        10d
  • 比如我们查看 node1 节点上的 NodeImage 对象:
➜  kruise kubectl get nodeimage node1 -o yaml
apiVersion: apps.kruise.io/v1alpha1
kind: NodeImage
metadata:
  labels:
    beta.kubernetes.io/arch: amd64
    beta.kubernetes.io/os: linux
    kubernetes.io/arch: amd64
    kubernetes.io/hostname: node1
    kubernetes.io/os: linux
  name: node1
spec: {}
status:
  desired: 0
  failed: 0
  pulling: 0
  succeeded: 0
  • 比如我们希望在这个节点上拉去一个 ubuntu:latest 镜像,则可以按照如下所示的去修改 spec:
......
spec:
  images:
    ubuntu:  # 镜像 name
      tags:
      - tag: latest  # 镜像 tag
        pullPolicy:
          ttlSecondsAfterFinished: 300  # [required] 拉取完成(成功或失败)超过 300s 后,将这个任务从 NodeImage 中清除
          timeoutSeconds: 600           # [optional] 每一次拉取的超时时间, 默认为 600
          backoffLimit: 3               # [optional] 拉取的重试次数,默认为 3
          activeDeadlineSeconds: 1200   # [optional] 整个任务的超时时间,无默认值

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

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

ImagePullJob

  • 比如创建如下所示的 ImagePullJob 资源对象:
apiVersion: apps.kruise.io/v1alpha1
kind: ImagePullJob
metadata:
  name: job-with-always
spec:
  image: nginx:1.9.1   # [required] 完整的镜像名 name:tag
  parallelism: 10      # [optional] 最大并发拉取的节点数量, 默认为 1
  selector:            # [optional] 指定节点的 名字列表 或 标签选择器 (只能设置其中一种)
    names:
    - node-1
    - node-2
    matchLabels:
      node-type: xxx
  # podSelector:         # [optional] pod label 选择器来在这些 pod 所在节点上拉取镜像, 与 selector 不能同时设置.
  #  pod-label: xxx
  completionPolicy:
    type: Always                  # [optional] 默认为 Always
    activeDeadlineSeconds: 1200   # [optional] 无默认值, 只对 Alway 类型生效
    ttlSecondsAfterFinished: 300  # [optional] 无默认值, 只对 Alway 类型生效
  pullPolicy:                     # [optional] 默认 backoffLimit=3, timeoutSeconds=600
    backoffLimit: 3
    timeoutSeconds: 300
  pullSecrets: #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 的注解即可:

apiVersion: v1
kind: Pod
  annotations:
    apps.kruise.io/container-launch-priority: Ordered
spec:
  containers:
  - name: sidecar
    # ...
  - name: main
    # ...

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

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

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: main
    # ...
  - name: sidecar
    env:
    - name: KRUISE_CONTAINER_PRIORITY
      value: "1"
    # ...

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

🍀 除了这些常用的增强控制器之外 OpenKruise 还有很多高级的特性,可以前往官网 https://openkruise.io 了解更多信息。

image-20220312214141683

关于我

我的博客主旨:我希望每一个人拿着我的博客都可以做出实验现象,先把实验做出来,然后再结合理论知识更深层次去理解技术点,这样学习起来才有乐趣和动力。并且,我的博客内容步骤是很完整的,也分享源码和实验用到的软件,希望能和大家一起共同进步!

各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人免费帮您解决问题:

  1. 个人微信二维码:x2675263825 (舍得), qq:2675263825。

    image-20211002091450217

  2. 个人博客地址:www.onlyonexl.cn

    image-20211002092057988

  3. 个人微信公众号:云原生架构师实战

    image-20211002141739664

  4. 个人csdn

    https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

    image-20211002092344616

  5. 个人已开源干货😘

    名称链接
    01 实战:打造一款王者云笔记:typora+坚果云+阿里云osshttps://www.jianguoyun.com/p/DXS6qiIQvPWVCRiS0qoE
    02 实战:定制宇宙中最美的typora主题皮肤https://www.jianguoyun.com/p/DeUK9u0QvPWVCRib0qoE
    03 玩转vscodehttps://www.jianguoyun.com/p/DZe8gmsQvPWVCRid0qoE
    04 陈果的幸福哲学课https://www.jianguoyun.com/p/Db0kM7gQvPWVCRj2q6YE

最后

好了,关于OpenKruise运维增强控制器实验就到这里了,感谢大家阅读,最后贴上我女神的photo,祝大家生活快乐,每天都过的有意义哦,我们下期见!

image-20220312214747685

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐