K8s 향상된 워크로드 OpenKruise 운영 및 유지 관리 개선
우리는 이미 OpenKruise의 기본 개념과 일반적으로 사용되는 몇 가지 고급 컨트롤러를 배웠습니다. 다음으로 다른 고급 기능에 대해 계속 배워 보겠습니다.
SidecarSet
SidecarSet는 승인 웹후크를 통해 클러스터에 생성된 적격 Pod에 사이드카 컨테이너를 자동으로 주입하는 것을 지원합니다. 또한 SidecarSet은 Pod가 생성될 때 주입되는 것 외에도 미러링 기능에 대해 주입된 사이드카 컨테이너의 현장 업그레이드도 제공합니다. SidecarSet은 비즈니스 컨테이너에서 사이드카 컨테이너의 정의와 수명 주기를 분리하며 주로 모니터링, 로깅 및 기타 에이전트와 같은 상태 비저장 사이드카 컨테이너를 관리하는 데 사용됩니다.
예를 들어 아래와 같이 SidecarSet 리소스 개체를 정의합니다.
# sidecarset-demo.yaml apiVersion: apps.kruise.io/v1alpha1 kind: SidecarSet metadata: name: scs-demo spec: selector: matchLabels: # 非常重要的属性,会去匹配具有 app=nginx 的 Pod app: nginx updateStrategy: type: RollingUpdate maxUnavailable: 1 containers: - name: sidecar1 image: busybox command: ["sleep", "999d"] volumeMounts: - name: log-volume mountPath: /var/log volumes: # 该属性会被合并到 pod.spec.volumes 去 - name: log-volume emptyDir: {}
이 리소스 개체를 직접 생성합니다.
➜ kubectl get sidecarset NAME MATCHED UPDATED READY AGE scs-demo 0 0 0 6s
위에서 SidecarSet 개체를 정의할 때 매우 중요한 사항이 있다는 점에 유의해야 합니다. 속성: 라벨 선택기는 app=nginx와 함께 Pod를 일치시킨 다음 아래에 정의된 sidecar1 컨테이너를 삽입합니다. 예를 들어 아래와 같이 app=nginx 라벨이 포함된 Pod를 정의합니다. 위의 SidecarSet 객체 일치:
apiVersion: v1 kind: Pod metadata: labels: app: nginx # 匹配 SidecarSet 里面指定的标签 name: test-pod spec: containers: - name: app image: nginx:1.7.9
위의 리소스 객체를 직접 생성합니다:
➜ kubectl get pod test-pod NAME READY STATUSRESTARTS AGE test-pod 2/2 Running 022s
Pod에는 위에 정의된 sidecar1 컨테이너에 자동으로 주입되는 2개의 컨테이너가 있는 것을 볼 수 있습니다.
➜ kubectl get pod test-pod -o yaml apiVersion: v1 kind: Pod metadata: labels: app: nginx name: test-pod namespace: default spec: containers: - command: - sleep - 999d env: - name: IS_INJECTED value: "true" image: busybox imagePullPolicy: Always name: sidecar1 resources: {} volumeMounts: - mountPath: /var/log name: log-volume - image: nginx:1.7.9 imagePullPolicy: IfNotPresent name: app # ...... volumes: - emptyDir: {} name: log-volume # ......
이제 SidecarSet에서 사이드카 컨테이너 이미지를 업데이트하고 busybox:1.35.0으로 교체해 보겠습니다.
➜ kubectl patch sidecarset scs-demo --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value": "busybox:1.35.0"}]' sidecarset.apps.kruise.io/scs-demo patched
업데이트 후 Pod에서 사이드카 컨테이너를 확인하세요.
➜ kubectl get pod test-pod NAME READY STATUSRESTARTSAGE test-pod 2/2 Running 1 (67s ago) 28m ➜ kubectl describe pod test-pod ...... Events: TypeReason AgeFrom Message ---------- ---- ---- ------- # ...... NormalCreated10mkubeletCreated container app NormalStarted10mkubeletStarted container app NormalKilling114s kubeletContainer sidecar1 definition changed, will be restarted NormalPulling84skubeletPulling image "busybox:1.35.0" NormalCreated77s (x2 over 11m)kubeletCreated container sidecar1 NormalStarted77s (x2 over 11m)kubeletStarted container sidecar1 NormalPulled 77skubeletSuccessfully pulled image "busybox:1.35.0" in 6.901558972s (6.901575894s including waiting) ➜ kubectl get pod test-pod -o yaml |grep busybox kruise.io/sidecarset-inplace-update-state: '{"scs-demo":{"revision":"f78z4854d9855xd6478fzx9c84645z2548v24z26455db46bdfzw44v49v98f2cw","updateTimestamp":"2023-04-04T08:05:18Z","lastContainerStatuses":{"sidecar1":{"imageID":"docker.io/library/busybox@sha256:b5d6fe0712636ceb7430189de28819e195e8966372edfc2d9409d79402a0dc16"}}}}' image: busybox:1.35.0 image: docker.io/library/busybox:1.35.0 imageID: docker.io/library/busybox@sha256:223ae047b1065bd069aac01ae3ac8088b3ca4a527827e283b85112f29385fb1b
사이드카 컨테이너가 Pod의 이미지가 busybox:1.35.0으로 업그레이드되었으나 기본 컨테이너에는 영향을 주지 않습니다.
기본 기능
사이드카 삽입은 Pod 생성 단계에서만 발생하며 Pod 사양만 업데이트되며 Pod가 속한 워크로드 템플릿에는 영향을 미치지 않습니다. 기본 k8s 컨테이너 필드 외에도 spec.containers는 주입을 용이하게 하기 위해 다음 필드를 확장합니다.
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: nginxconf # 扩展的 sidecar 容器字段 podInjectPolicy: BeforeAppContainer shareVolumePolicy: # 数据卷共享 type: disabled | enabled transferEnv: # 环境变量共享 - sourceContainerName: main # 会把main容器中的PROXY_IP环境变量注入到当前定义的sidecar容器中 envName: PROXY_IP volumes: - Name: nginxconf hostPath: /data/nginx/conf
- podInjectPolicy는 컨테이너가 pod.spec.containers에 주입되는 위치를 정의합니다.
- BeforeAppContainer: Pod의 원래 컨테이너 앞에 주입됨을 나타냅니다(기본값).
- AfterAppContainer: Pod의 원래 컨테이너 뒤에 주입됨을 나타냅니다.
- 데이터 볼륨 공유.
- 지정된 볼륨 공유: spec.volumes를 사용하여 사이드카 자체에 필요한 볼륨을 정의하세요.
- 모든 볼륨 공유: spec.containers[i].shareVolumePolicy.type =enabled |disable을 사용하여 Pod 애플리케이션 컨테이너의 볼륨 마운트 여부를 제어합니다. 활성화로 구성하면 사이드카에 자주 사용됩니다. 애플리케이션 컨테이너의 모든 볼륨이 마운트됩니다. 마운트 지점은 사이드카와 동일한 경로에 삽입됩니다(사이드카 자체에 선언된 데이터 볼륨 및 마운트 지점 제외).
- 환경 변수 공유: spec.containers[i].transferEnv를 통해 다른 컨테이너에서 환경 변수를 얻을 수 있습니다. sourceContainerName이라는 컨테이너에 있는 envName이라는 환경 변수가 이 컨테이너에 복사됩니다.
SidecarSet은 사이드카 컨테이너의 전체 업그레이드를 지원할 뿐만 아니라 매우 풍부한 업그레이드 및 그레이스케일 전략도 제공합니다. 마찬가지로 SidecarSet 객체의 updateStrategy 속성에서 이전 버전을 유지할 Pod 수 또는 비율을 정의하도록 파티션을 구성할 수 있습니다. 기본값은 0이며, 이는 릴리스 중에 사용할 수 없는 최대 수를 나타냅니다. 프로세스.
- {matched pod}=100, partitinotallow=40,maxUnavailable=10인 경우 컨트롤러는 100-40=60개의 Pod를 새 버전으로 릴리스하지만 Pod가 나올 때마다 동시에 10개의 Pod만 릴리스됩니다. 출시되며, 60개 출시가 완료될 때까지 또 다른 출시작을 찾아볼 예정입니다.
- {matched pod}=100, partitinotallow=80, maxUnavailable=30인 경우 컨트롤러는 maxUnavailable 숫자가 충족되므로 20개의 Pod가 동시에 릴리스됩니다.
paused: true를 설정하여 릴리스를 일시 중지할 수도 있습니다. 이때 새로 생성되고 확장된 포드에 대해서는 주입 기능이 계속 구현되며, 업데이트되지 않은 포드는 업데이트된 버전을 유지합니다. 업데이트가 일시중지됩니다.
apiVersion: apps.kruise.io/v1alpha1 kind: SidecarSet metadata: name: sidecarset spec: # ... updateStrategy: type: RollingUpdate maxUnavailable: 20% partition: 10 paused: true
카나리아 릴리스
카나리아 릴리스가 필요한 비즈니스의 경우 선택기를 통해 최초로 카나리아 그레이스케일이 되어야 하는 포드의 경우 고정된 [canary.release] = true 레이블을 설정하세요. 그런 다음 selector.matchLabels를 통해 Pod를 선택합니다.
예를 들어, 이제 아래와 같이 라벨 app=nginx가 있는 3개 복사본 Pod가 있습니다.
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 get pods -l app=nginx NAMEREADY STATUSRESTARTS AGE nginx-6457955f7-7hnjw 2/2 Running 051s nginx-6457955f7-prkgz 2/2 Running 051s nginx-6457955f7-tbtxk 2/2 Running 051s test-pod2/2 Running 04m2s
现在如果我们想为 test-pod 这个应用来执行灰度策略,将 sidecar 容器镜像更新成 busybox:1.35.0,则可以在 updateStrategy 下面添加 selector.matchLabels 属性 canary.release: "true":
piVersion: apps.kruise.io/v1alpha1 kind: SidecarSet metadata: name: test-sidecarset spec: updateStrategy: type: RollingUpdate selector: matchLabels: canary.release: "true" containers: - name: sidecar1 image: busybox:1.35.0 # ......
然后同样需要给 test-pod 添加上 canary.release=true 这个标签:
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 describe pod test-pod Events: TypeReason AgeFrom Message ---------- ---- ---- ------- NormalKilling7m53skubeletContainer sidecar1 definition changed, will be restarted NormalCreated7m23s (x2 over 8m17s)kubeletCreated container sidecar1 NormalStarted7m23s (x2 over 8m17s)kubeletStarted container sidecar1 NormalPulling7m23skubeletPulling image "busybox" NormalPulled 7m23skubeletSuccessfully pulled image "busybox" in 603.928658ms
热升级
SidecarSet 原地升级会先停止旧版本的容器,然后创建新版本的容器,这种方式适合不影响 Pod 服务可用性的 sidecar 容器,比如说日志收集的 Agent。
但是对于很多代理或运行时的 sidecar 容器,例如 Istio Envoy,这种升级方法就有问题了,Envoy 作为 Pod 中的一个代理容器,代理了所有的流量,如果直接重启,Pod 服务的可用性会受到影响,如果需要单独升级 envoy sidecar,就需要复杂的优雅终止和协调机制,所以我们为这种 sidecar 容器的升级提供了一种新的解决方案。
# hotupgrade-sidecarset.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 执行多次热升级时,将重复性的执行上述三个步骤。
热升级流程
这里我们以 OpenKruise 的官方示例来进行说明,首先创建上面的 hotupgrade-sidecarset 这个 SidecarSet。然后创建一个如下所示的 CloneSet 对象:
# hotupgrade-cloneset.yaml apiVersion: apps.kruise.io/v1alpha1 kind: CloneSet metadata: name: busybox labels: app: hotupgrade spec: replicas: 1 selector: matchLabels: app: hotupgrade template: metadata: labels: app: hotupgrade spec: containers: - name: busybox image: openkruise/hotupgrade-sample:busybox
创建完成后,CloneSet 管理的 Pod 已经注入 sidecar-1 和 sidecar-2 两个容器:
➜ kubectl get sidecarset hotupgrade-sidecarset NAMEMATCHED UPDATED READY AGE hotupgrade-sidecarset 1 1 0 58s ➜ kubectl get pods -l app=hotupgrade NAMEREADY STATUSRESTARTS AGE busybox-nd5bp 3/3 Running 031s ➜ kubectl describe pod busybox-nd5bp Name: busybox-nd5bp Namespace:default Node: node2/10.206.16.10 # ...... Controlled By:CloneSet/busybox Containers: sidecar-1: Container ID: containerd://511aa4b60d36483177e92805653c1b618495e47d8d5de331008259f78b3be89e Image:openkruise/hotupgrade-sample:sidecarv1 Image ID: docker.io/openkruise/hotupgrade-sample@sha256:3d677ca19712b67d2c264374736d71089d21e100eff341f6b4bb0f5288ec6f34 Environment: IS_INJECTED: true SIDECARSET_VERSION: (v1:metadata.annotations['version.sidecarset.kruise.io/sidecar-1']) SIDECARSET_VERSION_ALT: (v1:metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-1']) # ...... sidecar-2: Container ID: containerd://6b0678695ccb977695248e41108606b409ad0c7e3e4fe1ba9b48e839e3c235ef Image:openkruise/hotupgrade-sample:empty Image ID: docker.io/openkruise/hotupgrade-sample@sha256:606be602967c9f91c47e4149af4336c053e26225b717a1b5453ac8fa9a401cc5 Environment: IS_INJECTED: true SIDECARSET_VERSION: (v1:metadata.annotations['version.sidecarset.kruise.io/sidecar-2']) SIDECARSET_VERSION_ALT: (v1:metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-2']) # ...... busybox: Container ID: containerd://da7eebb0161bab37f7de75635e68c5284a973a21f6d3f095bb5e8212ac8ce908 Image:openkruise/hotupgrade-sample:busybox Image ID: docker.io/openkruise/hotupgrade-sample@sha256:08f9ede05850686e1200240e5e376fc76245dd2eb56299060120b8c3dba46dc9 # ...... # ...... Events: TypeReason Age From Message ---------- -------- ------- NormalScheduled50s default-schedulerSuccessfully assigned default/busybox-nd5bp to node2 NormalPulling49s kubeletPulling image "openkruise/hotupgrade-sample:sidecarv1" NormalPulled 41s kubeletSuccessfully pulled image "openkruise/hotupgrade-sample:sidecarv1" in 7.929984849s (7.929998445s including waiting) NormalCreated41s kubeletCreated container sidecar-1 NormalStarted41s kubeletStarted container sidecar-1 NormalPulling36s kubeletPulling image "openkruise/hotupgrade-sample:empty" NormalPulled 29s kubeletSuccessfully pulled image "openkruise/hotupgrade-sample:empty" in 7.434180553s (7.434189239s including waiting) NormalCreated29s kubeletCreated container sidecar-2 NormalStarted29s kubeletStarted container sidecar-2 NormalPulling29s kubeletPulling image "openkruise/hotupgrade-sample:busybox" NormalPulled 22s kubeletSuccessfully pulled image "openkruise/hotupgrade-sample:busybox" in 6.583450981s (6.583456512s including waiting) NormalCreated22s kubeletCreated container busybox NormalStarted22s kubeletStarted container busybox ......
busybox 主容器每 100 毫秒会请求一次 sidecar(versinotallow=v1)服务:
➜ kubectl logs -f busybox-nd5bp -c busybox I0404 09:12:26.513128 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) I0404 09:12:26.623496 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) I0404 09:12:26.733958 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) ......
现在我们去升级 sidecar 容器,将镜像修改为 openkruise/hotupgrade-sample:sidecarv2:
➜ kubectl patch sidecarset hotupgrade-sidecarset --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value": "openkruise/hotupgrade-sample:sidecarv2"}]'
更新后再去观察 pod 的状态,可以看到 sidecar-2 镜像正常更新了:
➜ kubectl get pods -l app=hotupgrade NAMEREADY STATUSRESTARTSAGE busybox-nd5bp 3/3 Running 2 (45s ago) 23m ➜ kubectl describe pods busybox-nd5bp ...... Events: ...... NormalPulling33skubeletPulling image "openkruise/hotupgrade-sample:sidecarv2" NormalKilling33skubeletContainer sidecar-2 definition changed, will be restarted NormalStarted25s (x2 over 22m)kubeletStarted container sidecar-2 NormalCreated25s (x2 over 22m)kubeletCreated container sidecar-2 NormalPulled 25skubeletSuccessfully pulled image "openkruise/hotupgrade-sample:sidecarv2" in 8.169453753s (8.16946743s including waiting) NormalKilling14skubeletContainer sidecar-1 definition changed, will be restarted NormalResetContainerSucceed14ssidecarset-controllerreset sidecar container image empty successfully NormalPulling14skubeletPulling image "openkruise/hotupgrade-sample:empty" NormalCreated12s (x2 over 22m)kubeletCreated container sidecar-1 NormalStarted12s (x2 over 22m)kubeletStarted container sidecar-1 NormalPulled 12skubeletSuccessfully pulled image "openkruise/hotupgrade-sample:empty" in 1.766097364s (1.766109087s including waiting)
并且在更新过程中观察 busybox 容器仍然会不断请求 sidecar 服务,但是并没有失败的请求出现:
➜ kubectl logs -f busybox-nd5bp -c busybox I0306 11:08:47.587727 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) I0404 09:14:28.588973 1 main.go:39] request sidecar server success, and response(body=This is version(v2) sidecar) # ......
整个热升级示例代码可以参考仓库的实现:https://github.com/openkruise/samples/tree/master/hotupgrade。
Container Restart
ContainerRecreateRequest 控制器可以帮助用户重启/重建存量 Pod 中一个或多个容器。和 Kruise 提供的原地升级类似,当一个容器重建的时候,Pod 中的其他容器还保持正常运行,重建完成后,Pod 中除了该容器的 restartCount 增加以外不会有什么其他变化。
不过需要注意之前临时写到旧容器 rootfs 中的文件会丢失,但是 volume mount 挂载卷中的数据都还存在。这个功能依赖于 kruise-daemon 组件来停止 Pod 容器。
为要重建容器的 Pod 提交一个 ContainerRecreateRequest 自定义资源(缩写 CRR):
# crr-demo.yaml apiVersion: apps.kruise.io/v1alpha1 kind: ContainerRecreateRequest metadata: name: crr-dmo 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 工作负载创建的 Pod 注入 readinessGate,也就是说只有这些 Pod 才能在 CRR 重建时使用 unreadyGracePeriodSeconds。
当用户创建了一个 CRR,Kruise webhook 会把当时容器的 containerID/restartCount 记录到 spec.containers[x].statusContext 之中。 在 kruise-daemon 执行的过程中,如果它发现实际容器当前的 containerID 与 statusContext 不一致或 restartCount 已经变大,则认为容器已经被重建成功了(比如可能发生了一次原地升级)。
容器重启请求
一般情况下,kruise-daemon 会执行 preStop hook 后把容器停掉,然后 kubelet 感知到容器退出,则会新建一个容器并启动。最后 kruise-daemon 看到新容器已经启动成功超过 minStartedSeconds 时间后,会上报这个容器的 phase 状态为 Succeeded。
如果容器重建和原地升级操作同时触发了:
- 如果 kubelet 根据原地升级要求已经停止或重建了容器,kruise-daemon 会判断容器重建已经完成。
- 如果 kruise-daemon 先停了容器,Kubelet 会继续执行原地升级,即创建一个新版本容器并启动。
- 如果针对一个 Pod 提交了多个 ContainerRecreateRequest 资源,会按时间先后一个个执行。
ImagePullJob
NodeImage 和 ImagePullJob 是从 Kruise v0.8.0 版本开始提供的 CRD。Kruise 会自动为每个节点创建一个 NodeImage,它包含了哪些镜像需要在这个 Node 上做预热,比如我们这里 3 个节点,则会自动创建 3 个 NodeImage 对象:
➜ kubectl get nodeimage NAMEDESIRED PULLING SUCCEED FAILED AGE master1 0 0 0 05d node1 0 0 0 05d node2 0 0 0 05d
比如我们查看 node1 节点上的 NodeImage 对象:
➜ kubectl get nodeimage node1 -o yaml apiVersion: apps.kruise.io/v1alpha1 kind: NodeImage metadata: 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 后任务会被清除。
➜ kubectl describe nodeimage node1 Name: node1 Namespace: # ...... Spec: Images: Ubuntu: Tags: Created At:2023-04-04T09:29:18Z Pull Policy: Active Deadline Seconds: 1200 Backoff Limit: 3 Timeout Seconds: 600 Ttl Seconds After Finished:300 Tag: latest Status: Desired:1 Failed: 0 Image Statuses: Ubuntu: Tags: Completion Time:2023-04-04T09:29:28Z Phase:Succeeded Progress: 100 Start Time: 2023-04-04T09:29:18Z Tag:latest Pulling:0 Succeeded:1 Events: TypeReasonAge From Message ------------------ ------- NormalPullImageSucceed11s kruise-daemon-imagepullerImage ubuntu:latest, ecalpsedTime 10.066193581s
我们可以在 node1 节点上查看到这个镜像已经被拉取下来了:
ubuntu@node1:~$ sudo ctr -n k8s.io i ls|grep ubuntu docker.io/library/ubuntu:latestapplication/vnd.oci.image.index.v1+json sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21 28.2 MiBlinux/amd64,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390xio.cri-containerd.image=managed docker.io/library/ubuntu@sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21 application/vnd.oci.image.index.v1+json sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21 28.2 MiBlinux/amd64,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390xio.cri-containerd.image=managed ubuntu@node1:~$
此外用户可以创建 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: - node1 - node2 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, timeoutSecnotallow=600 backoffLimit: 3 timeoutSeconds: 300 pullSecrets: - secret-name1 - secret-name2
我们可以在 selector 字段中指定节点的名字列表或标签选择器 (只能设置其中一种),如果没有设置 selector 则会选择所有节点做预热。或者可以配置 podSelector 来在这些 pod 所在节点上拉取镜像,podSelector 与 selector 不能同时设置。
同时,ImagePullJob 有两种 completionPolicy 类型:
- Always:表示这个 job 是一次性预热,不管成功、失败都会结束。
- activeDeadlineSeconds:整个 job 的 deadline 结束时间。
- ttlSecondsAfterFinished:结束后超过这个时间,自动清理删除 job。
- Never:表示这个 job 是长期运行、不会结束,并且会每天都会在匹配的节点上重新预热一次指定的镜像。
同样如果你要预热的镜像来自私有仓库,则可以通过 pullSecrets 来指定仓库的 Secret 信息。
如果这个镜像来自一个私有仓库,则可以通过 pullSecrets 来指定仓库的 Secret 信息。
# ... spec: pullSecrets: - secret-name1 - secret-name2
因为 ImagePullJob 是一种 namespaced-scope 资源,所以这些 Secret 必须存在 ImagePullJob 所在的 namespace 中。然后你只需要在 pullSecrets 字段中写上这些 secret 的名字即可。
容器启动顺序
Container Launch Priority 提供了控制一个 Pod 中容器启动顺序的方法。通常来说 Pod 容器的启动和退出顺序是由 Kubelet 管理的,Kubernetes 曾经有一个 KEP 计划在 container 中增加一个 type 字段来标识不同类型容器的启停优先级,但是由于sig-node 考虑到对现有代码架构的改动太大,所以将该提案拒绝了。
这个功能作用在 Pod 对象上,不管它的 owner 是什么类型的,因此可以适用于 Deployment、CloneSet 以及其他的工作负载。
比如我们可以设置按照容器顺序启动,只需要在 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,权重高的容器,会保证在权重低的容器之前启动,但是需要注意相同权重的容器不保证启动顺序。
资源分发
在对 Secret、ConfigMap 等命名空间级别资源进行跨 namespace 分发及同步的场景中,原生 Kubernetes 目前只支持用户手动分发与同步,十分地不方便。比如:
- 当用户需要使用 SidecarSet 的 imagePullSecrets 能力时,要先重复地在相关 namespaces 中创建对应的 Secret,并且需要确保这些 Secret 配置的正确性和一致性。
- 当用户想要采用 ConfigMap 来配置一些通用的环境变量时,往往需要在多个 namespaces 做 ConfigMap 的下发,并且后续的修改往往也要求多 namespaces 之间保持同步。
- 在多个命名空间中的 Ingress 对象需要使用同一个 Secret 对象
面对这些需要跨命名空间进行资源分发和多次同步的场景,OpenKruise 设计了一个新的 CRD - ResourceDistribution,可以更便捷的自动化分发和同步这些资源。
ResourceDistribution 目前支持 Secret 和 ConfigMap 两类资源的分发和同步。
ResourceDistribution 是全局的 CRD,其主要由 resource 和 targets 两个字段构成,其中 resource 字段用于描述用户所要分发的资源,targets 字段用于描述用户所要分发的目标命名空间。
apiVersion: apps.kruise.io/v1alpha1 kind: ResourceDistribution metadata: name: sample spec: resource: ... ... targets: ... ...
其中 resource 字段必须是一个完整、正确的资源描述,如下所示:
apiVersion: apps.kruise.io/v1alpha1 kind: ResourceDistribution metadata: name: sample spec: resource: apiVersion: v1 kind: ConfigMap metadata: name: game-demo data: game.properties: | enemy.types=aliens,monsters player.maximum-lives=5 player_initial_lives: "3" ui_properties_file_name: user-interface.properties user-interface.properties: | color.good=purple color.bad=yellow allow.textmode=true targets: ... ...
用户可以先在本地某个命名空间中创建相应资源并进行测试,确认资源配置正确后再拷贝过来。
targets 字段目前支持四种规则来描述用户所要分发的目标命名空间,包括 allNamespaces、includedNamespaces、namespaceLabelSelector 以及 excludedNamespaces:
- allNamespaces: bool 值,如果为 true,则分发至所有命名空间。
- includedNamespaces: 通过 Name 来匹配目标命名空间。
- namespaceLabelSelector:通过 LabelSelector 来匹配目标命名空间。
- excludedNamespaces: 通过 Name 来排除某些不想分发的命名空间。
allNamespaces、includedNamespaces、namespaceLabelSelector 之间是**或(OR)**的关系,而 excludedNamespaces 一旦被配置,则会显式地排除掉这些命名空间。另外,targets 还将自动忽略 kube-system 和 kube-public 两个命名空间。
一个配置正确的 targets 字段如下所示:
apiVersion: apps.kruise.io/v1alpha1 kind: ResourceDistribution metadata: name: sample spec: resource: ... ... targets: includedNamespaces: list: - name: ns-1 - name: ns-4 namespaceLabelSelector: matchLabels: group: test excludedNamespaces: list: - name: ns-3
该配置表示该 ResourceDistribution 的目标命名空间一定会包含 ns-1 和 ns-4,并且 Labels 满足 namespaceLabelSelector 的命名空间也会被包含进目标命名空间,但是,即使 ns-3 即使满足 namespaceLabelSelector 也不会被包含,因为它已经在 excludedNamespaces 中被显式地排除了。
如果同步的资源需要更新则可以去更新 resource 字段,更新后会自动地对所有目标命名空间中的资源进行同步更新。每一次更新资源时,ResourceDistribution 都会计算新版本资源的哈希值,并记录到资源的 Annotations 之中,当 ResourceDistribution 发现新版本的资源与目前资源的哈希值不同时,才会对资源进行更新。
apiVersion: v1 kind: ConfigMap metadata: name: demo annotations: kruise.io/resourcedistribution.resource.from: sample kruise.io/resourcedistribution.resource.distributed.timestamp: 2021-09-06 08:44:52.7861421 +0000 UTC m=+12896.810364601 kruise.io/resourcedistribution.resource.hashcode: 0821a13321b2c76b5bd63341a0d97fb46bfdbb2f914e2ad6b613d10632fa4b63 ... ...
当然非常不建议用户绕过 ResourceDistribution 直接对资源进行修改,除非用户知道自己在做什么。
- 因为直接修改资源后,资源的哈希值不会被自动计算,因此,下次 resource 字段被修改后,ResourceDistribution 可能将用户对这些资源的直接修改覆盖掉。
- ResourceDistribution 通过 kruise.io/resourcedistribution.resource.from 来判断资源是否由该 ResourceDistribution 分发,如果该 Annotation 被修改或删除,则被修改的资源会被 ResourceDistribution 当成冲突资源,并且无法通过 ResourceDistribution 进行同步更新。
除了这些增强控制器之外 OpenKruise 还有很多高级的特性,可以前往官网 https://openkruise.io 了解更多信息。
위 내용은 K8s 향상된 워크로드 OpenKruise 운영 및 유지 관리 개선의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











컨테이너 기술을 기반으로 한 경량 가상화 플랫폼인 Docker는 다양한 시나리오에서 널리 사용되었습니다. 프로덕션 환경에서는 컨테이너의 고가용성과 자동 장애 복구가 중요합니다. 이 문서에서는 특정 코드 예제를 포함하여 컨테이너 오류 복구 및 자동 다시 시작을 위해 Docker를 사용하는 방법을 소개합니다. 1. 컨테이너 자동 재시작 구성 Docker에서는 컨테이너 실행 시 --restart 옵션을 사용하여 컨테이너 자동 재시작 기능을 활성화할 수 있습니다. 일반적인 옵션은 다음과 같습니다. no: 자동으로 다시 시작하지 않습니다. 조용한

화웨이 공식 소식에 따르면, '개발자를 위한 모든 것'이라는 주제로 Open Atomic 개발자 컨퍼런스가 12월 16일부터 17일까지 이틀 동안 우시에서 열렸습니다. 이번 컨퍼런스는 Open Atomic Open Source Foundation인 화웨이가 주도했습니다. Inspur., DaoCloud, Xieyun, Qingyun, Hurricane Engine은 물론 OpenSDV Open Source Alliance, openEuler 커뮤니티, OpenCloudOS 커뮤니티 및 기타 회원 단위가 공동으로 AtomHub Trusted Mirror Center 구축을 시작했습니다. 이는 공식 테스트를 위해 공개되었습니다. AtomHub는 공동 구축, 공동 거버넌스, 공유의 개념을 고수하며 오픈 소스 조직과 개발자에게 중립적이고 개방적이며 공동 구축된 신뢰할 수 있는 오픈 소스 컨테이너 미러 센터를 제공하는 것을 목표로 합니다. DockerHub와 같은 이미지 웨어하우스의 불안정성과 통제불가능성을 고려하여

Windows 11 또는 10에 RedHatPodman 설치 명령 프롬프트 또는 Powershell을 사용하여 Windows 시스템에 RedHatPodman을 설치하려면 아래 단계를 따르십시오. 1단계: 시스템 요구 사항 확인 먼저 Windows 시스템이 최신 업데이트로 실행되고 있는지 확인해야 합니다. Podman 요구 사항을 실행하기 위한 요구 사항을 충족할 수 있습니다. Windows 11 또는 Windows 10 버전 1709(빌드 16299) 이상을 사용해야 하며 WSL2(Linux 2용 Windows 하위 시스템) 및 VM 기능을 활성화해야 합니다. 아직 활성화되지 않은 경우 2단계 명령을 사용할 수 있습니다. 이것을 실행합니다

C++에서 STL 컨테이너를 정렬하는 방법: sort() 함수를 사용하여 std::Vector와 같은 컨테이너를 제자리에 정렬합니다. 순서가 지정된 컨테이너 std::set 및 std::map을 사용하면 삽입 시 요소가 자동으로 정렬됩니다. 사용자 정의 정렬 순서의 경우 문자열 벡터를 알파벳순으로 정렬하는 것과 같은 사용자 정의 비교기 클래스를 사용할 수 있습니다.

C++STL에서 가장 일반적인 컨테이너 유형은 Vector, List, Deque, Set, Map, Stack 및 Queue입니다. 이러한 컨테이너는 동적 배열, 이중 연결 목록, 키 및 값 기반 연관 컨테이너와 같은 다양한 데이터 스토리지 요구 사항에 대한 솔루션을 제공합니다. 실제로 STL 컨테이너를 사용하여 학생 성적을 저장하는 등 데이터를 효율적으로 구성하고 액세스할 수 있습니다.

안녕하세요, 저는 정 형제입니다. WeChat의 미니 프로그램은 매우 좋은 경험이며, 간단하고 빠르게 사용할 수 있습니다. 저는 요즘 미니 프로그램 사용법을 배우고 있으며 여러분의 참고를 위해 Python을 미니 프로그램의 백엔드로 사용하는 세 가지 방법을 요약했습니다. 방법 1. WeChat 클라우드 호스팅 [1]. 장점: 서버 구매 불필요, 도메인 이름 등록 불필요, 사용량에 따른 과금, DevOps 자동화, 보안 인증, 운영 및 유지 관리 경험이 없는 사람들에게 적합합니다. 단점: 자체 구축 서버를 구축하는 비용에 비해 비용이 확실히 약간 높습니다. 같은 모델과 마찬가지로 자동변속기 차량도 수동변속기 차량보다 가격이 더 비쌉니다. 소위 클라우드 호스팅은 Docker 컨테이너만 있으면 github, gitlab, gitee 중 하나를 사용할 수 있습니다.

Go 언어의 마이크로서비스 아키텍처와 컨테이너 기술을 배워보세요. 클라우드 컴퓨팅과 빅데이터의 급속한 발전으로 소프트웨어 개발 분야에서 마이크로서비스 아키텍처와 컨테이너 기술이 점점 더 대중화되고 있습니다. 오픈 소스이자 효율적인 프로그래밍 언어인 Go 언어는 강력한 동시성과 간결한 구문으로 인해 널리 주목을 받고 있습니다. 이 글에서는 Go 언어 마이크로서비스 아키텍처와 컨테이너 기술을 학습하는 관련 지식과 방법을 소개합니다. 먼저 마이크로서비스 아키텍처를 이해해 봅시다. 마이크로서비스 아키텍처는 애플리케이션을 일련의 더 작고 독립적인 서비스로 분할하여 애플리케이션을 구축하는 방법입니다.

서블릿 컨테이너는 서블릿 실행 환경을 제공하는 애플리케이션으로, 서블릿 라이프사이클을 관리하고 보안, 트랜잭션 등 필요한 WEB 서비스를 제공하는 역할을 담당합니다. 서블릿 컨테이너에는 다양한 종류가 있으며 그 중 가장 일반적인 것은 Tomcat과 Jetty입니다. 서블릿 컨테이너의 주요 기능은 라이프사이클 관리입니다. 서블릿 컨테이너는 시작, 초기화, 서비스 및 파기를 포함하여 서블릿의 라이프사이클을 관리하는 역할을 담당합니다. 웹 서비스: 서블릿 컨테이너는 보안, 트랜잭션 등과 같은 웹 서비스를 제공합니다. 리소스 관리: 서블릿 컨테이너는 서블릿, jsP, html 페이지 등과 같은 리소스를 관리합니다. 클래스 로딩: 서블릿 컨테이너는 추가를 담당합니다.
