Skip to main content

One post tagged with "release"

View All Tags

· 阅读需要 1 分钟
Mingshan Zhao

OpenKruise( https://github.com/openkruise/kruise )是阿里云开源的云原生应用自动化管理套件,也是当前托管在 Cloud Native Computing Foundation (CNCF) 下的孵化项目。它来自阿里巴巴多年来容器化、云原生的技术沉淀,是阿里内部生产环境大规模应用的基于 Kubernetes 之上的标准扩展组件,也是紧贴上游社区标准、适应互联网规模化场景的技术理念与最佳实践。

OpenKruise 在 2023.3.31 发布了最新的 v1.4 版本(ChangeLog),新增 Job Sidecar Terminator 重磅功能,本文以下对新版本做整体的概览介绍。

1. 重要更新

  • 为了方便大家使用 Kruise 增强能力,默认打开了一些稳定的能力,如下:ResourcesDeletionProtection, WorkloadSpread, PodUnavailableBudgetDeleteGate, InPlaceUpdateEnvFromMetadata, StatefulSetAutoDeletePVC, PodProbeMarkerGate。上述能力大部分是需要特别配置才会生效的,所以默认打开一般不会对存量集群造成影响,如果有一些特性不想使用,可以在升级时关闭。
  • Kruise-Manager leader 选举方式从 configmaps 迁移为 configmapsleases,为后面迁移到 leases 方式做准备,另外,这是官方提供的平滑升级的方式,不会对存量的集群造成影响。

2. Sidecar容器管理能力:Job Sidecar Terminator

在 Kubernetes 中对于 Job 类型 Workload,人们通常希望当主容器完成任务并退出后,Pod 进入已完成状态。然而,当这些 Pod 拥有 Long-Running Sidecar 容器时,由于 Sidecar 容器在主容器退出后无法自行退出,导致 Pod 一直无法进入已完成状态。面对这个问题,社区的常见解决方案一般都需要对 Main 和 Sidecar 进行改造,两者通过 Volume 共享来实现 Main 容器退出之后,Sidecar 容器完成退出的效果。

社区的解决方案可以解决这个问题,但是需要对容器进行改造,尤其对于社区通用的 Sidecar 容器,改造和维护的成本太高了。

为此,我们在 Kruise 中加入了一个名为 SidecarTerminator 的控制器,专门用于在此类场景下,监听主容器的完成状态,并选择合适的时机终止掉 Pod 中的 sidecar 容器,并且无需对 Main 和 Sidecar 容器进行侵入式改造。

运行在普通节点的 Pod

对于运行于普通节点的 Pod(常规Kubelet),使用该特性非常简单,用户只需要在要在目标 sidecar 容器中添加一个特殊的 env 对其进行标识,控制器会在恰当的时机利用 Kruise Daemon 提供的 CRR 的能力,将这些 sidecar 容器终止:

kind: Job
spec:
template:
spec:
containers:
- name: sidecar
env:
- name: KRUISE_TERMINATE_SIDECAR_WHEN_JOB_EXIT
value: "true"
- name: main
...

运行在虚拟节点的 Pod

对于一些提供 Serverless 容器的平台,例如 ECI 或者 Fargate, 其 Pods 只能运行于 Virtual-Kubelet 之类的虚拟节点。 然而,Kruise Daemon 无法部署和工作在这些虚拟节点之上,导致无法使用 CRR 能力将容器终止。 但幸运地是,我们可以借助原生 Kubernetes 提供的 Pod 原地升级机制来达到同样的目的:只需要构造一个特殊镜像,这个镜像的唯一作用就是当被拉起后,会快速地主动退出,这样一来,只需要在退出 sidecar 时,将原本的 sidecar 镜像替换为快速退出镜像,即可达到退出 sidecar 的目的。

步骤一: 准备一个快速退出镜像

  • 该镜像只需要具备非常简单的逻辑:当其被拉起后,直接退出,且退出码为 0。
  • 该镜像需要兼容原 sidecar 镜像的 commands 和 args,以防容器被拉起时报错。

步骤二: 配置你的 sidecar 容器

kind: Job
spec:
template:
spec:
containers:
- name: sidecar
env:
- name: KRUISE_TERMINATE_SIDECAR_WHEN_JOB_EXIT_WITH_IMAGE
value: "example/quick-exit:v1.0.0"
- name: main
...

使用你自己准备的快速退出镜像来替换上述 "example/quick-exit:v1.0.0".

注意事项

  • sidecar 容器必须能够响应 SIGTERM 信号,并且当收到此信号时,entrypoint 进程需要退出(即 sidecar 容器需要退出),并且退出码应当为 0。
  • 该特性适用于任意 Job 类型 Workload 所管理的 Pod,只要他们的 RestartPolicy 为 Never/OnFailure 即可。
  • 具有环境变量 KRUISE_TERMINATE_SIDECAR_WHEN_JOB_EXIT 的容器将被视为 sidecar 容器,其他容器将被视为主容器,当所有主容器完成后,sidecar 容器才会被终止:
    • 在 Never 重启策略下,主容器一旦退出,将被视为"已完成"。
    • 在 OnFailure 重启策略下,主容器退出代码必须为0,才会被视为"已完成"。
  • 且运行在普通节点方式下,KRUISE_TERMINATE_SIDECAR_WHEN_JOB_EXIT 的优先级高于KRUISE_TERMINATE_SIDECAR_WHEN_JOB_EXIT_WITH_IMAGE

3. 增强版本的工作负载

CloneSet 优化性能 :新增 FeatureGate CloneSetEventHandlerOptimization

当前,无论是 Pod 的状态变化还是 Metadata 变化,Pod Update 事件都会触发 CloneSet reconcile 逻辑。CloneSet Reconcile 默认配置了三个 worker,对于集群规模较小的场景,这种情况并不会造成问题。

但对于集群规模较大或 Pod Update 事件较多的情况,这些无效的 reconcile 将会阻塞真正的 CloneSet reconcile,进而导致 CloneSet 的滚动升级等变更延迟。为了解决这个问题,可以打开 feature-gate CloneSetEventHandlerOptimization 来减少一些不必要的 reconcile 入队。

CloneSet 新增 disablePVCReuse 字段

如果一个 Pod 被外部直接调用删除或驱逐时,这个 Pod 关联的 PVCs 还都存在;并且 CloneSet controller 发现数量不足重新扩容时,新扩出来的 Pod 会复用原 Pod 的 instance-id 并关联原来的 PVCs。

然而,如果 Pod 所在的 Node 出现异常,复用可能会导致新 Pod 启动失败,详情参考 issue 1099。为了解决这个问题,您可以设置字段 DisablePVCReuse=true,当 Pod 被驱逐或者删除后,与 Pod 相关的 PVCs 将被自动删除,不再被复用。

apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
spec:
...
replicas: 4
scaleStrategy:
disablePVCReuse: true

CloneSet 增加 PreNormal 生命周期钩子

CloneSet 已经支持了PreparingUpdate、PreparingDelete 两种生命周期钩子,用于应用的优雅下线,详情参考社区文档。为了支持优雅上线的场景,本次新增加了 PreNormal 状态,具体如下:

apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
spec:
# define with finalizer
lifecycle:
preNormal:
finalizersHandler:
- example.io/unready-blocker

# or define with label
# lifecycle:
# preNormal:
# labelsHandler:
# example.io/block-unready: "true"

当 CloneSet 创建一个 Pod(包括正常扩容和重建升级)时:

  • 如果 Pod 满足了 PreNormal hook 的定义,才会被认为是 Available,并且才会进入 Normal 状态 这对于一些 Pod 创建时的后置检查很有用,比如你可以检查 Pod 是否已经挂载到 SLB 后端,从而避免滚动升级时,旧实例销毁后,新实例挂载失败导致的流量损失。

4. 高级的应用运维能力

容器重启新增 forceRecreate 字段

当创建 CRR 资源时,如果容器正在启动过程中,CRR 将不会再重启容器。如果您想要强制重启容器,可以使用以下字段开启:

apiVersion: apps.kruise.io/v1alpha1
kind: ContainerRecreateRequest
spec:
...
strategy:
forceRecreate: true

镜像预热支持 Attach metadata into cri interface

当 Kubelet 创建 Pod 时,Kubelet 将会 attach metadata 到 container runtime cri 接口。镜像仓库可以根据这些 metadata 信息来确定拉镜像的来源业务,如果发生了仓库过载、压力过大的情况,可以对具体的业务进行降级处理。OpenKruise 镜像预热同样支持类似的能力,如下:

apiVersion: apps.kruise.io/v1alpha1
kind: ImagePullJob
spec:
...
image: nginx:1.9.1
sandboxConfig:
annotations:
io.kubernetes.image.metrics.tags: "cluster=cn-shanghai"
labels:
io.kubernetes.image.app: "foo"

社区参与

非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 OpenKruise 开源社区。 你是否已经有一些希望与我们社区交流的内容呢? 可以在我们的社区双周会上分享你的声音,或通过以下渠道参与讨论:

  • 加入社区 Slack channel (English)
  • 加入社区钉钉群:搜索群号 23330762 (Chinese)
  • 加入社区微信群(新):添加用户 openkruise 并让机器人拉你入群 (Chinese)

· 阅读需要 1 分钟
Mingshan Zhao

云原生应用自动化管理套件、CNCF Sandbox 项目 -- OpenKruise,近期发布了 v1.3 版本。

OpenKruise 是针对 Kubernetes 的增强能力套件,聚焦于云原生应用的部署、升级、运维、稳定性防护等领域。 所有的功能都通过 CRD 等标准方式扩展,可以适用于 1.16 以上版本的任意 Kubernetes 集群。单条 helm 命令即可完成 Kruise 的一键部署,无需更多配置。

版本解析

在版本v1.3中,OpenKruise提供了新的CRD资源 PodProbeMarker,改善了大规模集群的一些性能问题,Advanced DaemonSet支持镜像预热, 以及 CloneSet、WorkloadSpread、Advanced CronJob、SidecarSet一些新的特性。

1. 新增 CRD 和 Controller:PodProbeMarker

Kubernetes提供了三种默认的Pod生命周期管理:

  • Readiness Probe 用来判断业务容器是否已经准备好响应用户请求,如果检查失败,会将该Pod从Service Endpoints中剔除。
  • Liveness Probe 用来判断容器的健康状态,如果检查失败,kubelet将会重启该容器。
  • Startup Probe 用来判断容器是否启动完成,如果定义了该Probe,那么Readiness Probe与Liveness Probe将会在它成功之后再执行。

所以Kubernetes中提供的Probe能力都已经限定了特定的语义以及相关的行为。除此之外,其实还是存在自定义Probe语义以及相关行为的需求,例如:

  • GameServer定义 Idle Probe 用来判断该Pod当前是否存在游戏对局,如果没有,从成本优化的角度,可以将该Pod缩容掉。
  • K8S Operator定义 main-secondary Probe 来判断当前Pod的角色(main or secondary),升级的时候,可以优先升级 secondary,进而达到升级过程只有一次选主的行为,降低升级过程中服务抖动时间。

OpenKruise提供了自定义Probe的能力,并将结果返回到Pod Status中,用户可以根据该结果决定后续的行为。

PodProbeMarker配置如下:

apiVersion: apps.kruise.io/v1alpha1
kind: PodProbeMarker
metadata:
name: game-server-probe
namespace: ns
spec:
selector:
matchLabels:
app: game-server
probes:
- name: Idle
containerName: game-server
probe:
exec:
command:
- /home/game/idle.sh
initialDelaySeconds: 10
timeoutSeconds: 3
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
markerPolicy:
- state: Succeeded
labels:
gameserver-idle: 'true'
annotations:
controller.kubernetes.io/pod-deletion-cost: '-10'
- state: Failed
labels:
gameserver-idle: 'false'
annotations:
controller.kubernetes.io/pod-deletion-cost: '10'
podConditionType: game.io/idle

PodProbeMarker结果可以通过Pod对象查看:

apiVersion: v1
kind: Pod
metadata:
labels:
app: game-server
gameserver-idle: 'true'
annotations:
controller.kubernetes.io/pod-deletion-cost: '-10'
name: game-server-58cb9f5688-7sbd8
namespace: ns
spec:
...
status:
conditions:
# podConditionType
- type: game.io/idle
# Probe State 'Succeeded' indicates 'True', and 'Failed' indicates 'False'
status: "True"
lastProbeTime: "2022-09-09T07:13:04Z"
lastTransitionTime: "2022-09-09T07:13:04Z"
# If the probe fails to execute, the message is stderr
message: ""

2. 性能优化:大规模集群性能显著提升

  • #1026 引入了延迟入队机制,大幅优化了在大规模应用集群下 kruise-manager 拉起时的 CloneSet 控制器工作队列堆积问题,在理想情况下初始化时间减少了 80% 以上。
  • #1027 优化 PodUnavailableBudget 控制器 Event Handler 逻辑,减少无关 Pod 入队数量。
  • #1011 通过缓存机制,优化了大规模集群下 Advanced DaemonSet 重复模拟 Pod 调度计算的 CPU、Memory 消耗。
  • #1015, #1068 大幅降低了大规模集群下的运行时内存消耗。弥补了 v1.1 版本中 Disable DeepCopy 的一些疏漏点,减少 expressions 类型 label selector 的转换消耗。

3. SidecarSet 支持注入特定的历史版本

SidecarSet 通过 ControllerRevision 记录了关于 containersvolumesinitContainersimagePullSecretspatchPodMetadata 等字段的历史版本,并允许用户在 Pod 创建时选择特定的历史版本进行注入。 基于这一特性,用户可以规避在 SidecarSet 灰度发布时,因Deployment 等 Workload 扩容、升级等操作带来的 SidecarSet 发布风险。如果不选择注入版本,SidecarSet 将对重建 Pod 默认全都注入最新版本 Sidecar。

SidecarSet 相关 ControllerRevision 资源被放置在了与 Kruise-Manager 相同的命名空间中,用户可以使用 kubectl get controllerrevisions -n kruise-system -l kruise.io/sidecarset-name=your-sidecarset-name 来查看。 此外,用户还可以通过 SidecarSet 的 status.latestRevision 字段看到当前版本对应的 ControllerRevision 名称,以方便自行记录。

通过 ControllerRevision 名称指定注入的 Sidecar 版本

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
name: sidecarset
spec:
...
injectionStrategy:
revision:
revisionName: specific-controllerRevision-name

通过自定义版本标识指定注入的 Sidecar 版本

用户可以通过在发版时,同时给 SidecarSet 打上 apps.kruise.io/sidecarset-custom-version=your-version-id 来标记每一个历史版本,SidecarSet 会将这个 label 向下带入到对应的 ControllerRevision 对象,以便用户进行筛选,并且允许用户在选择注入历史版本时,使用该 your-version-id 来进行描述。

假设用户只想灰度 10% 的 Pods 到 version-2,并且对于新创建的 Pod 希望都注入更加稳定的 version-1 版本来控制灰度风险:

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
name: sidecarset
labels:
apps.kruise.io/sidecarset-custom-version: version-2
spec:
...
updateStrategy:
partition: 90%
injectionStrategy:
revision:
customVersion: version-1

4. SidecarSet 支持注入 Pod Annotations

SidecarSet支持注入Pod Annotations,配置如下:

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
spec:
containers:
...
patchPodMetadata:
- annotations:
oom-score: '{"log-agent": 1}'
custom.example.com/sidecar-configuration: '{"command": "/home/admin/bin/start.sh", "log-level": "3"}'
patchPolicy: MergePatchJson
- annotations:
apps.kruise.io/container-launch-priority: Ordered
patchPolicy: Overwrite | Retain

patchPolicy为注入的策略,如下:

  • Retain: 默认策略,如果Pod中存在 annotation[key]=value ,则保留Pod原有的value。只有当 Pod中不存在 annotation[key] 时,才注入 annotations[key]=value。
  • Overwrite: 与 Retain 对应,当 Pod 中存在 annotation[key]=value,将被强制覆盖为 value2。
  • MergePatchJson: 与 Overwrite 对应,annotations value为 json 字符串。如果 Pod 不存在该 annotations[key],则直接注入。如果存在,则进行 json value合并。 例如:Pod中存在 annotations[oom-score]='{"main": 2}',注入后将 value json合并为 annotations[oom-score]='{"log-agent": 1, "main": 2}'。

注意: patchPolicy为Overwrite和MergePatchJson时,SidecarSet原地升级 Sidecar Container时,能够同步更新该 annotations。但是,如果只修改annotations则不能生效,只能搭配Sidecar容器镜像一起原地升级。 patchPolicy为Retain时,SidecarSet原地升级 Sidecar Container时,将不会同步更新该 annotations。

上述配置后,sidecarSet在注入sidecar container时,会注入Pod annotations,如下:

apiVersion: v1
kind: Pod
metadata:
annotations:
apps.kruise.io/container-launch-priority: Ordered
oom-score: '{"log-agent": 1, "main": 2}'
custom.example.com/sidecar-configuration: '{"command": "/home/admin/bin/start.sh", "log-level": "3"}'
name: test-pod
spec:
containers:
...

注意: SidecarSet从安全的考虑不应该注入或修改除 sidecar container 之外的 Pod 字段,所以如果想要使用该能力,首先需要配置 SidecarSet_PatchPodMetadata_WhiteList 白名单 或通过 Feature-gate SidecarSetPatchPodMetadataDefaultsAllowed=true 关闭白名单校验。

5. Advanced DaemonSet 支持镜像预热

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

默认情况下 DaemonSet 每个新镜像预热时的并发度都是 1,也就是一个个节点拉镜像。 如果需要调整,你可以通过 apps.kruise.io/image-predownload-parallelism annotation 来设置并发度。

apiVersion: apps.kruise.io/v1alpha1
kind: DaemonSet
metadata:
annotations:
apps.kruise.io/image-predownload-parallelism: "10"

6. CloneSet 扩缩容与 PreparingDelete

默认情况下,CloneSet 将处于 PreparingDelete 状态的 Pod 视为正常,意味着这些 Pod 仍然被计算在 replicas 数量中。

在这种情况下:

  • 如果你将 replicasN 改为 N-1,当一个要删除的 Pod 还在 PreparingDelete 状态中时,你重新将 replicas 改为 N,CloneSet 会将这个 Pod 重新置为 Normal 状态。
  • 如果你将 replicasN 改为 N-1 的同时在 podsToDelete 中设置了一个 Pod,当这个 Pod 还在 PreparingDelete 状态中时,你重新将 replicas 改为 N,CloneSet 会等到这个 Pod 真正进入 terminating 之后再扩容一个 Pod 出来。
  • 如果你在不改变 replicas 的时候指定删除一个 Pod,当这个 Pod 还在 PreparingDelete 状态中时,CloneSet 会等到这个 Pod 真正进入 terminating 之后再扩容一个 Pod 出来。

从 Kruise v1.3.0 版本开始,你可以在 CloneSet 中设置一个 apps.kruise.io/cloneset-scaling-exclude-preparing-delete: "true" 标签,它标志着这个 CloneSet 不会将 PreparingDelete 状态的 Pod 计算在 replicas 数量中。

在这种情况下:

  • 如果你将 replicasN 改为 N-1,当一个要删除的 Pod 还在 PreparingDelete 状态中时,你重新将 replicas 改为 N,CloneSet 会将这个 Pod 重新置为 Normal 状态。
  • 如果你将 replicasN 改为 N-1 的同时在 podsToDelete 中设置了一个 Pod,当这个 Pod 还在 PreparingDelete 状态中时,你重新将 replicas 改为 N,CloneSet 会立即创建一个新 Pod。
  • 如果你在不改变 replicas 的时候指定删除一个 Pod,当这个 Pod 还在 PreparingDelete 状态中时,CloneSet 会立即创建一个新 Pod。

7. Advanced CronJob Time zones

默认情况下,所有 AdvancedCronJob schedule 调度时,都是基于 kruise-controller-manager 容器本地的时区所计算的。

不过,在 v1.3.0 版本中我们引入了 spec.timeZone 字段,你可以将它设置为任意合法时区的名字。例如,设置 spec.timeZone: "Asia/Shanghai" 则 Kruise 会根据国内的时区计算 schedule 任务触发时间。

Go 标准库中内置了时区数据库,作为在容器的系统环境中没有外置数据库时的 fallback 选择。

8. 其他改动

你可以通过 Github release 页面,来查看更多的改动以及它们的作者与提交记录。

社区参与

非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 OpenKruise 开源社区。 你是否已经有一些希望与我们社区交流的内容呢? 可以在我们的社区双周会上分享你的声音,或通过以下渠道参与讨论:

  • 加入社区 Slack channel (English)
  • 加入社区钉钉群:搜索群号 23330762 (Chinese)
  • 加入社区微信群(新):添加用户 openkruise 并让机器人拉你入群 (Chinese)

· 阅读需要 1 分钟
Siyu Wang

云原生应用自动化管理套件、CNCF Sandbox 项目 -- OpenKruise,近期发布了 v1.2 版本。

OpenKruise 是针对 Kubernetes 的增强能力套件,聚焦于云原生应用的部署、升级、运维、稳定性防护等领域。 所有的功能都通过 CRD 等标准方式扩展,可以适用于 1.16 以上版本的任意 Kubernetes 集群。单条 helm 命令即可完成 Kruise 的一键部署,无需更多配置。

版本解析

在 v1.2 版本中,OpenKruise 提供了一个名为 PersistentPodState 的新 CRD 和控制器,在 CloneSet status 和 lifecycle hook 中新增字段, 并对 PodUnavailableBudget 做了多重优化。

1. 新增 CRD 和 Controller:PersistentPodState

随着云原生的发展,越来越多的公司开始将有状态服务(如:Etcd、MQ)进行 Kubernetes 部署。K8s StatefulSet 是管理有状态服务的工作负载,它在很多方面考虑了有状态服务的部署特征。 然而,StatefulSet 只能保持有限的Pod状态,如:Pod Name 有序且不变,PVC 持久化,并不能满足其它 Pod 状态的保持需求,例如:固定 IP 调度,优先调度到之前部署的 Node 等。典型案例有:

  • 服务发现中间件服务对部署之后的 Pod IP 异常敏感,要求 IP 不能随意改变

  • 数据库服务将数据持久化到宿主机磁盘,所属 Node 改变将导致数据丢失

针对上述描述,Kruise 通过自定义 PersistentPodState CRD,能够保持 Pod 其它相关状态,例如:“固定 IP 调度”。

一个 PersistentPodState 资源对象 YAML 如下:

apiVersion: apps.kruise.io/v1alpha1
kind: PersistentPodState
metadata:
name: echoserver
namespace: echoserver
spec:
targetRef:
# 原生k8s 或 kruise StatefulSet
# 只支持 StatefulSet 类型
apiVersion: apps.kruise.io/v1beta1
kind: StatefulSet
name: echoserver
# required node affinity,如下:Pod重建后将强制部署到同Zone
requiredPersistentTopology:
nodeTopologyKeys:
- failure-domain.beta.kubernetes.io/zone[,other node labels]
# preferred node affinity,如下:Pod重建后将尽量部署到同Node
preferredPersistentTopology:
- preference:
nodeTopologyKeys:
- kubernetes.io/hostname[,other node labels]
# int, [1 - 100]
weight: 100

“固定 IP 调度”应该是比较常见的有状态服务的 K8s 部署要求,它的含义不是“指定 Pod IP 部署”,而是要求 Pod 在第一次部署之后,业务发布或机器驱逐等常规性运维操作都不会导致 Pod IP 发生变化。 达到上述效果,首先就需要 K8s 网络组件能够支持Pod IP保留以及尽量保持IP不变的能力,本文将 flannel 网络组件中的 Host-local 插件做了一些代码改造, 使之能够达到同 Node 下保持 Pod IP 不变的效果,相关原理就不在此陈述,代码请参考:host-local

“固定 IP 调度”好像有网络组件支持就好了,这跟 PersistentPodState 有什么关系呢?因为,网络组件实现"Pod IP 保持不变"都有一定的限制, 例如:flannel 只能支持同 Node 保持 Pod IP 不变。但是,K8s 调度的最大特性就是“不确定性”,所以“如何保证 Pod 重建后调度到同 Node 上”就是 PersistentPodState 解决的问题。

另外,你可以通过在 StatefulSet 或 Advanced StatefulSet 上面新增如下的 annotations,来让 Kruise 自动为你的 StatefulSet 创建 PersistentPodState 对象,从而避免了手动创建所有 PersistentPodState 的负担。

apiVersion: apps.kruise.io/v1alpha1
kind: StatefulSet
metadata:
annotations:
# 自动生成PersistentPodState对象
kruise.io/auto-generate-persistent-pod-state: "true"
# preferred node affinity,如下:Pod重建后将尽量部署到同Node
kruise.io/preferred-persistent-topology: kubernetes.io/hostname[,other node labels]
# required node affinity,如下:Pod重建后将强制部署到同Zone
kruise.io/required-persistent-topology: failure-domain.beta.kubernetes.io/zone[,other node labels]

2. CloneSet 针对百分比形式 partition 计算逻辑变化,新增 status 字段

过去,CloneSet 通过 “向上取整” 的方式来计算它的 partition 数值(当它是百分比形式的数值时),这意味着即使你将 partition 设置为一个小于 100% 的百分比,CloneSet 也有可能不会升级任何一个 Pod 到新版本。 比如,对于一个 replicas=8partition=90% 的 CloneSet 对象,它所计算出的实际 partition 数值是 8(来自 8 * 90% 向上取整), 因此它暂时不会执行升级动作。 这有时候会为用户来带困惑,尤其是对于使用了一些 rollout 滚动升级组件的场景,比如 Kruise Rollout 或 Argo。

因此,从 v1.2 版本开始,CloneSet 会保证在 partition 是小于 100% 的百分比数值时,至少有 1 个 Pod 会被升级,除非 CloneSet 处于 replicas <= 1 的情况。

不过,这样会导致用户难以理解其中的计算逻辑,同时又需要在 partition 升级的时候知道期望升级的 Pod 数量,来判断该批次升级是否完成。

所以我们另外又在 CloneSet status 中新增了 expectedUpdatedReplicas 字段,它可以很直接地展示基于当前的 partition 数值,期望有多少 Pod 会被升级。 对于用户来说,只需要比对 status.updatedReplicas >= status.expectedUpdatedReplicas 以及另外的 updatedReadyReplicas 来判断当前发布阶段是否达到完成状态。

apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
spec:
replicas: 8
updateStrategy:
partition: 90%
status:
replicas: 8
expectedUpdatedReplicas: 1
updatedReplicas: 1
updatedReadyReplicas: 1

3. 在 lifecycle hook 阶段设置 Pod not-ready

Kruise 在早先的版本中提供了 lifecycle hook 功能,其中 CloneSet 和 Advanced StatefulSet 都支持了 PreDelete、InPlaceUpdate 两种 hook, Advanced DaemonSet 目前只支持 PreDelete hook。

过去,这些 hook 只会将当前的操作卡住,并允许用户在 Pod 删除之前或者原地升级的前后来做一些自定义的事情(比如将 Pod 从服务端点中摘除)。 但是,Pod 在这些阶段中很可能还处于 Ready 状态,此时将它从一些自定义的 service 实现中摘除, 其实一定程度上有点违背 Kubernetes 的常理,一般来说它只会将处于 NotReady 状态的 Pod 从服务端点中摘除。

因此,这个版本我们在 lifecycle hook 中新增了 markPodNotReady 字段,它控制了 Pod 在处于 hook 阶段的时候是否会被强制设为 NotReady 状态。

type LifecycleStateType string

// Lifecycle contains the hooks for Pod lifecycle.
type Lifecycle struct
// PreDelete is the hook before Pod to be deleted.
PreDelete *LifecycleHook `json:"preDelete,omitempty"`
// InPlaceUpdate is the hook before Pod to update and after Pod has been updated.
InPlaceUpdate *LifecycleHook `json:"inPlaceUpdate,omitempty"`
}

type LifecycleHook struct {
LabelsHandler map[string]string `json:"labelsHandler,omitempty"`
FinalizersHandler []string `json:"finalizersHandler,omitempty"`

/********************** FEATURE STATE: 1.2.0 ************************/
// MarkPodNotReady = true means:
// - Pod will be set to 'NotReady' at preparingDelete/preparingUpdate state.
// - Pod will be restored to 'Ready' at Updated state if it was set to 'NotReady' at preparingUpdate state.
// Default to false.
MarkPodNotReady bool `json:"markPodNotReady,omitempty"`
/*********************************************************************/
}

对于配置了 markPodNotReady: true 的 PreDelete hook,它会在 PreparingDelete 阶段的时候将 Pod 设置为 NotReady, 并且这种 Pod 在我们重新调大 replicas 数值的时候无法重新回到 normal 状态。

对于配置了 markPodNotReady: true 的 InPlaceUpdate hook,它会在 PreparingUpdate 阶段将 Pod 设置为 NotReady, 并在 Updated 阶段将强制 NotReady 的状态去掉。

4. PodUnavailableBudget 支持自定义 workload 与性能优化

Kubernetes 自身提供了 PodDisruptionBudget 来帮助用户保护高可用的应用,但它只能防护 eviction 驱逐一种场景。 对于多种多样的不可用操作,PodUnavailableBudget 能够更加全面地防护应用的高可用和 SLA,它不仅能够防护 Pod 驱逐,还支持其他如删除、原地升级等会导致 Pod 不可用的操作。

过去,PodUnavailableBudget 仅仅支持一些特定的 workload,比如 CloneSet、Deployment 等,但它不能够识别用户自己定义的一些未知工作负载。

从 v1.2 版本开始,PodUnavailableBudget 支持了保护任意自定义工作负载的 Pod,只要这些工作负载声明了 scale subresource 子资源。

在 CRD 中,scale 子资源的声明方式如下:

    subresources:
scale:
labelSelectorPath: .status.labelSelector
specReplicasPath: .spec.replicas
statusReplicasPath: .status.replicas

不过,如果你的项目是通过 kubebuilder 或 operator-sdk 生成的,那么只需要在你的 workload 定义结构上加一行注解并重新 make manifests 即可:

// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.labelSelector

另外,PodUnavailableBudget 还通过关闭 client list 时候的默认 DeepCopy 操作,来提升了在大规模集群中的运行时性能。

5. 其他改动

你可以通过 Github release 页面,来查看更多的改动以及它们的作者与提交记录。

社区参与

非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 OpenKruise 开源社区。 你是否已经有一些希望与我们社区交流的内容呢? 可以在我们的社区双周会上分享你的声音,或通过以下渠道参与讨论:

  • 加入社区 Slack channel (English)
  • 加入社区钉钉群:搜索群号 23330762 (Chinese)
  • 加入社区微信群(新):添加用户 openkruise 并让机器人拉你入群 (Chinese)

· 阅读需要 1 分钟
Siyu Wang

云原生应用自动化管理套件、CNCF Sandbox 项目 -- OpenKruise,近期发布了 v1.1 版本。

OpenKruise 是针对 Kubernetes 的增强能力套件,聚焦于云原生应用的部署、升级、运维、稳定性防护等领域。 所有的功能都通过 CRD 等标准方式扩展,可以适用于 1.16 以上版本的任意 Kubernetes 集群。单条 helm 命令即可完成 Kruise 的一键部署,无需更多配置。

版本解析

在 v1.1 版本中,OpenKruise 对不少已有功能做了扩展与增强,并且优化了在大规模集群中的运行性能。以下对 v1.1 的部分功能做简要介绍。

值得注意的是,OpenKruise v1.1 已经将 Kubernetes 代码依赖版本升级到 v1.22,这意味着用户可以在 CloneSet 等工作负载的 pod template 模板中使用 up to v1.22 的新字段等, 但用户安装使用 OpenKruise 所兼容的 Kubernetes 集群版本仍然保持在 >= v1.16。

1. 原地升级支持容器顺序优先级

去年底发布的 v1.0 版本,OpenKruise 引入了容器启动顺序控制功能, 它支持为一个 Pod 中的多个容器定义不同的权重关系,并在 Pod 创建时按照权重来控制不同容器的启动顺序。

在 v1.0 中,这个功能仅仅能够作用于每个 Pod 的创建阶段。当创建完成后,如果对 Pod 中多个容器做原地升级,则这些容器都会被同时执行升级操作。

最近一段时间,社区与 LinkedIn 等公司做过一些交流,获得了更多用户使用场景的输入。 在一些场景下,Pod 中多个容器存在关联关系,例如业务容器升级的同时,Pod 中其他一些容器也需要升级配置从而关联到这个新版本; 或是多个容器避免并行升级,从而保证如日志采集类的 sidecar 容器不会丢失业务容器中的日志等。

因此,在 v1.1 版本中 OpenKruise 支持了按容器优先级顺序的原地升级。 在实际使用过程中,用户无需配置任何额外参数,只要 Pod 在创建时已经带有了容器启动优先级,则不仅在 Pod 创建阶段,会保证高优先级容器先于低优先级容器启动; 并且在单次原地升级中,如果同时升级了多个容器,会先升级高优先级容器,等待它升级启动完成后,再升级低优先级容器。

这里的原地升级,包括修改 image 镜像升级与修改 env from metadata 的环境变量升级,详见原地升级介绍

总结来说

  • 对于不存在容器启动顺序的 Pod,在多容器原地升级时没有顺序保证。
  • 对于存在容器启动顺序的 Pod:
    • 如果本次原地升级的多个容器具有不同的启动顺序,会按启动顺序来控制原地升级的先后顺序。
    • 如果本地原地升级的多个容器的启动顺序相同,则原地升级时没有顺序保证。

例如,一个包含两个不同启动顺序容器的 CloneSet 如下:

apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
...
spec:
replicas: 1
template:
metadata:
annotations:
app-config: "... config v1 ..."
spec:
containers:
- name: sidecar
env:
- name: KRUISE_CONTAINER_PRIORITY
value: "10"
- name: APP_CONFIG
valueFrom:
fieldRef:
fieldPath: metadata.annotations['app-config']
- name: main
image: main-image:v1
updateStrategy:
type: InPlaceIfPossible

当我们更新 CloneSet,将其中 app-config annotation 和 main 容器的镜像修改后, 意味着 sidecar 与 main 容器都需要被更新,Kruise 会先原地升级 Pod 来将其中 sidecar 容器重建来生效新的 env from annotation。

接下来,我们可以在已升级的 Pod 中看到 apps.kruise.io/inplace-update-state annotation 和它的值:

{
"revision": "{CLONESET_NAME}-{HASH}", // 本次原地升级的目标 revision 名字
"updateTimestamp": "2022-03-22T09:06:55Z", // 整个原地升级的初次开始时间
"nextContainerImages": {"main": "main-image:v2"}, // 后续批次中还需要升级的容器镜像
// "nextContainerRefMetadata": {...}, // 后续批次中还需要升级的容器 env from labels/annotations
"preCheckBeforeNext": {"containersRequiredReady": ["sidecar"]}, // pre-check 检查项,符合要求后才能原地升级后续批次的容器
"containerBatchesRecord":[
{"timestamp":"2022-03-22T09:06:55Z","containers":["sidecar"]} // 已更新的首个批次容器(它仅仅表明容器的 spec 已经被更新,例如 pod.spec.containers 中的 image 或是 labels/annotations,但并不代表 node 上真实的容器已经升级完成了)
]
}

当 sidecar 容器升级成功之后,Kruise 会接着再升级 main 容器。最终你会在 Pod 中看到如下的 apps.kruise.io/inplace-update-state annotation:

{
"revision": "{CLONESET_NAME}-{HASH}",
"updateTimestamp": "2022-03-22T09:06:55Z",
"lastContainerStatuses":{"main":{"imageID":"THE IMAGE ID OF OLD MAIN CONTAINER"}},
"containerBatchesRecord":[
{"timestamp":"2022-03-22T09:06:55Z","containers":["sidecar"]},
{"timestamp":"2022-03-22T09:07:20Z","containers":["main"]}
]
}

通常来说,用户只需要关注其中 containerBatchesRecord 来确保容器是被分为多批升级的。 如果这个 Pod 在原地升级的过程中卡住了,你可以检查 nextContainerImages/nextContainerRefMetadata 字段,以及 preCheckBeforeNext 中前一次升级的容器是否已经升级成功并 ready 了。

2. StatefulSetAutoDeletePVC 功能

从 Kubernetes v1.23 开始,原生的 StatefulSet 加入了 StatefulSetAutoDeletePVC 功能,即根据给定策略来选择保留或自动删除 StatefulSet 创建的 PVC 对象参考文档

因此,v1.1 版本的 Advanced StatefulSet 从上游同步了这个功能,允许用户通过 .spec.persistentVolumeClaimRetentionPolicy 字段来指定这个自动清理策略。 这需要你在安装或升级 Kruise 的时候,启用 StatefulSetAutoDeletePVC feature-gate 功能。

apiVersion: apps.kruise.io/v1beta1
kind: StatefulSet
spec:
...
persistentVolumeClaimRetentionPolicy: # optional
whenDeleted: Retain | Delete
whenScaled: Retain | Delete

其中,两个策略字段包括:

  • whenDeleted:当 Advanced StatefulSet 被删除时,对 PVC 的保留/删除策略。
  • whenScaled:当 Advanced StatefulSet 发生缩容时,对缩容 Pod 关联 PVC 的保留/删除策略。

每个策略都可以配置以下两种值:

  • Retain(默认值):它的行为与过去 StatefulSet 一样,在 Pod 删除时对它关联的 PVC 做保留。
  • Delete:当 Pod 删除时,自动删除它所关联的 PVC 对象。

除此之外,还有几个注意点:

  1. StatefulSetAutoDeletePVC 功能只会清理由 volumeClaimTemplate 中定义和创建的 PVC,而不会清理用户自己创建或关联到 StatefulSet Pod 中的 PVC。
  2. 上述清理只发生在 Advanced StatefulSet 被删除或主动缩容的情况下。例如 node 故障导致的 Pod 驱逐重建等,仍然会复用已有的 PVC。

3. Advanced DaemonSet 重构并支持生命周期钩子

早先版本的 Advanced DaemonSet 实现与上游控制器差异较大,例如对于 not-ready 和 unschedulable 的节点需要额外配置字段来选择是否处理,这对于我们的用户来说都增加了使用成本和负担。

在 v1.1 版本中,我们对 Advanced DaemonSet 做了一次小重构,将它与上游控制器重新做了对齐。 因此,Advanced DaemonSet 的所有默认行为会与原生 DaemonSet 基本一致,用户可以像使用 Advanced StatefulSet 一样,通过修改 apiVersion 就能很方便地将一个原生 DaemonSet 修改为 Advanced DaemonSet 来使用。

另外,我们还为 Advanced DaemonSet 增加了生命周期钩子,首先支持 preDelete hook,来允许用户在 daemon Pod 被删除前执行一些自定义的逻辑。

apiVersion: apps.kruise.io/v1alpha1
kind: DaemonSet
spec:
...
# define with label
lifecycle:
preDelete:
labelsHandler:
example.io/block-deleting: "true"

当 DaemonSet 删除一个 Pod 时(包括缩容和重建升级):

  • 如果没有定义 lifecycle hook 或者 Pod 不符合 preDelete 条件,则直接删除。
  • 否则,会先将 Pod 更新为 PreparingDelete 状态,并等待用户自定义的 controller 将 Pod 中关联的 label/finalizer 去除,再执行 Pod 删除。

4. Disable DeepCopy 性能优化

默认情况下,我们在使用 controller-runtime 来编写 Operator/Controller 时, 使用其中 sigs.k8s.io/controller-runtime/pkg/client Client 客户端来 get/list 查询对象(typed),都是从内存 Informer 中获取并返回,这是大部分人都知道的。

但很多人不知道的是,在这些 get/list 操作背后,controller-runtime 会将从 Informer 中查到的所有对象做一次 deep copy 深拷贝后再返回。

这个设计的初衷,是避免开发者错误地将 Informer 中的对象直接篡改。在深拷贝之后,无论开发者对 get/list 返回的对象做了任何修改,都不会影响到 Informer 中的对象,后者只会从 kube-apiserver 的 ListWatch 请求中同步。

但是在一些很大规模的集群中,OpenKruise 中各个控制器同时在运行,同时每个控制器还存在多个 worker 执行 Reconcile,可能会带来大量的 deep copy 操作。 例如集群中有大量应用的 CloneSet,而其中一些 CloneSet 下管理的 Pod 数量非常多,则每个 worker 在 Reconcile 的时候都会 list 查询一个 CloneSet 下的所有 Pod 对象,再加上多个 worker 并行操作, 可能造成 kruise-manager 瞬时的 CPU 和 Memory 压力陡增,甚至在内存配额不足的情况下有发生 OOM 的风险。

在上游的 controller-runtime 中,我在去年已经提交合并了 DisableDeepCopy 功能,包含在 controller-runtime v0.10 及以上的版本。 它允许开发者指定某些特定的资源类型,在做 get/list 查询时不执行深拷贝,而是直接返回 Informer 中的对象指针。

例如下述代码,在 main.go 中初始化 Manager 时,为 cache 加入参数即可配置 Pod 等资源类型不做深拷贝。

    mgr, err := ctrl.NewManager(cfg, ctrl.Options{
...
NewCache: cache.BuilderWithOptions(cache.Options{
UnsafeDisableDeepCopyByObject: map[client.Object]bool{
&v1.Pod{}: true,
},
}),
})

但在 Kruise v1.1 版本中,我们没有选择直接使用这个功能,而是将 Delegating Client 重新做了封装, 从而使得开发者可以在任意做 list 查询的地方通过 DisableDeepCopy ListOption 来指定单次的 list 操作不做深拷贝。

    if err := r.List(context.TODO(), &podList, client.InNamespace("default"), utilclient.DisableDeepCopy); err != nil {
return nil, nil, err
}

这样做的好处是使用上更加灵活,避免为整个资源类型关闭深拷贝后,众多社区贡献者在参与开发的过程中如果没有注意到则可能会错误修改 Informer 中的对象。

5. 其他改动

你可以通过 Github release 页面,来查看更多的改动以及它们的作者与提交记录。

社区参与

非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 OpenKruise 开源社区。 你是否已经有一些希望与我们社区交流的内容呢? 可以在我们的社区双周会上分享你的声音,或通过以下渠道参与讨论:

  • 加入社区 Slack channel (English)
  • 加入社区钉钉群:搜索群号 23330762 (Chinese)
  • 加入社区微信群(新):添加用户 openkruise 并让机器人拉你入群 (Chinese)

· 阅读需要 1 分钟
Siyu Wang

云原生应用自动化管理套件、CNCF Sandbox 项目 -- OpenKruise,近期发布了 v1.0 大版本。

OpenKruise 是针对 Kubernetes 的增强能力套件,聚焦于云原生应用的部署、升级、运维、稳定性防护等领域。所有的功能都通过 CRD 等标准方式扩展,可以适用于 1.16 以上版本的任意 Kubernetes 集群。单条 helm 命令即可完成 Kruise 的一键部署,无需更多配置。

openkruise-features|center|450x400

总得来看,目前 OpenKruise 提供的能力分为几个领域:

  • 应用工作负载:面向无状态、有状态、daemon 等多种类型应用的高级部署发布策略,例如原地升级、灰度流式发布等。
  • Sidecar 容器管理:支持独立定义 sidecar 容器,完成动态注入、独立原地升级、热升级等功能。
  • 增强运维能力:包括容器原地重启、镜像预拉取、容器启动顺序保障等。
  • 应用分区管理:管理应用在多个分区(可用区、不同机型等)上的部署比例、顺序、优先级等。
  • 应用安全防护:帮助应用在 Kubernetes 之上获得更高的安全性保障与可用性防护。

版本解析

在 v1.0 大版本中,OpenKruise 带来了多种新的特性,同时也对不少已有功能做了增强与优化。

首先要说的是,从 v1.0 开始 OpenKruise 将 CRD/WehhookConfiguration 等资源配置的版本从 v1beta1 升级到 v1,因此可以支持 Kubernetes v1.22 及以上版本的集群,但同时也要求 Kubernetes 的版本不能低于 v1.16

以下对 v1.0 的部分功能做简要介绍,详细的 ChangeLog 列表请查看 OpenKruise Github 上的 release 说明以及官网文档。

1. 支持环境变量原地升级

Author: @FillZpp

OpenKruise 从早期版本开始就支持了 “原地升级” 功能,主要应用于 CloneSet 与 Advanced StatefulSet 两种工作负载上。简单来说,原地升级使得应用在升级的过程中,不需要删除、新建 Pod 对象,而是通过对 Pod 中容器配置的修改来达到升级的目的。

inplace-update-comparation|center|450x400

如上图所示,原地升级过程中只修改了 Pod 中的字段,因此:

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

然而,OpenKruise 过去只能对 Pod 中 image 字段的更新做原地升级,对于其他字段仍然只能采用与 Deployment 相似的重建升级。一直以来,我们收到很多用户反馈,希望支持对 env 等更多字段的原地升级 -- 由于受到 kube-apiserver 的限制,这是很难做到的。

经过我们的不懈努力,OpenKruise 终于在 v1.0 版本中,支持了通过 Downward API 的方式支持了 env 环境变量的原地升级。例如对以下CloneSet YAML,用户将配置定义在 annotation 中并关联到对应 env 中。后续在修改配置时,只需要更新 annotation value 中的值,Kruise 就会对 Pod 中所有 env 里引用了这个 annotation 的容器触发原地重建,从而生效这个新的 value 配置。

apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
...
spec:
replicas: 1
template:
metadata:
annotations:
app-config: "... the real env value ..."
spec:
containers:
- name: app
env:
- name: APP_CONFIG
valueFrom:
fieldRef:
fieldPath: metadata.annotations['app-config']
updateStrategy:
type: InPlaceIfPossible

与此同时,我们在这个版本中也去除了过去对镜像原地升级的imageID限制,即支持相同imageID的两个镜像替换升级。

具体使用方式请参考文档

2. 配置跨命名空间分发

Author: @veophi

在对 Secret、ConfigMap 等 namespace-scoped 资源进行跨 namespace 分发及同步的场景中,原生 kubernetes 目前只支持用户 one-by-one 地进行手动分发与同步,十分地不方便。

典型的案例有:

  • 当用户需要使用 SidecarSet 的 imagePullSecrets 能力时,要先重复地在相关 namespaces 中创建对应的 Secret,并且需要确保这些 Secret 配置的正确性和一致性。
  • 当用户想要采用 ConfigMap 来配置一些通用的环境变量时,往往需要在多个 namespaces 做 ConfigMap 的下发,并且后续的修改往往也要求多 namespaces 之间保持同步。

因此,面对这些需要跨 namespaces 进行资源分发和多次同步的场景,我们期望一种更便捷的分发和同步工具来自动化地去做这件事,为此我们设计并实现了一个新的CRD --- ResourceDistribution

ResourceDistribution 目前支持 SecretConfigMap 两类资源的分发和同步。

apiVersion: apps.kruise.io/v1alpha1
kind: ResourceDistribution
metadata:
name: sample
spec:
resource:
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
...
targets:
namespaceLabelSelector:
...
# or includedNamespaces, excludedNamespaces

如上述 YAML 所示,ResourceDistribution是一类 cluster-scoped 的 CRD,其主要由 resourcetargets 两个字段构成,其中 resource 字段用于描述用户所要分发的资源,targets 字段用于描述用户所要分发的目标命名空间。

具体使用方式请参考文档

3. 容器启动顺序控制

Author: @Concurrensee

对于 Kubernetes 的一个 Pod,其中的多个容器可能存在依赖关系,比如 容器B 中应用进程的运行依赖于 容器A 中的应用。因此,多个容器之间存在顺序关系的需求:

  • 容器A 先启动,启动成功后才可以启动 容器B
  • 容器B 先退出,退出完成后才可以停止 容器A

通常来说 Pod 容器的启动和退出顺序是由 Kubelet 管理的。Kubernetes 曾经有一个 KEP 计划在 container 中增加一个 type 字段来标识不同类型容器的启停优先级。但是由于 sig-node 考虑到对现有代码架构的改动太大,目前这个 KEP 已经被拒绝了。

因此,OpenKruise 在 v1.0 中提供了名为 Container Launch Priority 的功能,用于控制一个 Pod 中多个容器的强制启动顺序:

  1. 对于任意一个 Pod 对象,只需要在 annotations 中定义 apps.kruise.io/container-launch-priority: Ordered,则 Kruise 会按照 Pod 中 containers 容器列表的顺序来保证其中容器的串行启动。
  2. 如果要自定义 containers 中多个容器的启动顺序,则在容器 env 中添加 KRUISE_CONTAINER_PRIORITY 环境变量,value 值是范围在 [-2147483647, 2147483647] 的整数。一个容器的 priority 值越大,会保证越先启动。

具体使用方式请参考文档

4. kubectl-kruise 命令行工具

Author: @hantmac

过去 OpenKruise 是通过 kruise-api、client-java 等仓库提供了 Go、Java 等语言的 Kruise API 定义以及客户端封装,可供用户在自己的应用程序中引入使用。但仍然有不少用户在测试环境下需要灵活地用命令行操作 workload 资源。

然而原生 kubectl 工具提供的 rolloutset image 等命令只能适用于原生的 workload 类型,如 Deployment、StatefulSet,并不能识别 OpenKruise 中扩展的 workload 类型。

因此,OpenKruise 最新提供了 kubectl-kruise 命令行工具,它是 kubectl 的标准插件,提供了许多适用于 OpenKruise workload 的功能。

# rollout undo cloneset
$ kubectl kruise rollout undo cloneset/nginx

# rollout status advanced statefulset
$ kubectl kruise rollout status statefulsets.apps.kruise.io/sts-demo

# set image of a cloneset
$ kubectl kruise set image cloneset/nginx busybox=busybox nginx=nginx:1.9.1

具体使用方式请参考文档

5. 其余部分功能改进与优化

CloneSet:

  • 通过 scaleStrategy.maxUnavailable 策略支持流式扩容
  • Stable revision 判断逻辑变化,当所有 Pod 版本与 updateRevision 一致时则标记为 currentRevision

WorkloadSpread:

  • 支持接管存量 Pod 到匹配的 subset 分组中
  • 优化 webhook 在 Pod 注入时的更新与重试逻辑

Advanced DaemonSet:

  • 支持对 Daemon Pod 做原地升级
  • 引入 progressive annotation 来选择是否按 partition 限制 Pod 创建

SidecarSet:

  • 解决 SidecarSet 过滤屏蔽 inactive Pod
  • transferenv 中新增 SourceContainerNameFromEnvNames 字段,来解决 container name 不一致与大量 env 情况下的冗余问题

PodUnavailableBudget:

  • 新增 “跳过保护” 标识
  • PodUnavailableBudget controller 关注 workload 工作负载的 replicas 变化

NodeImage:

  • 加入 --nodeimage-creation-delay 参数,并默认等待新增 Node ready 一段时间后同步创建 NodeImage

UnitedDeployment:

  • 解决 NodeSelectorTerms 为 nil 情况下 Pod NodeSelectorTerms 长度为 0 的问题

Other optimization:

  • kruise-daemon 采用 protobuf 协议操作 Pod 资源
  • 暴露 cache resync 为命令行参数,并在 chart 中设置默认值为 0
  • 解决 certs 更新时的 http checker 刷新问题
  • 去除对 forked controller-tools 的依赖,改为使用原生 controller-tools 配合 markers 注解

社区参与

非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 OpenKruise 开源社区。 你是否已经有一些希望与我们社区交流的内容呢? 可以在我们的社区双周会上分享你的声音,或通过以下渠道参与讨论:

  • 加入社区 Slack channel (English)
  • 加入社区钉钉群:搜索群号 23330762 (Chinese)
  • 加入社区微信群:添加用户 openkruise 并让机器人拉你入群 (Chinese)

· 阅读需要 1 分钟
Siyu Wang

本文将带你一览 v0.10.0 的新变化,其中新增的 WorkloadSpread、PodUnavailableBudget 等大颗粒特性后续还将有专文详细介绍其设计实现原理。

WorkloadSpread:旁路的应用弹性拓扑管理能力

在应用部署运维的场景下,有着多种多样的拓扑打散以及弹性的诉求。其中最常见、最基本的,就是按某种或几种拓扑水平打散,比如:

  • 应用部署需要按 node 维度打散,避免堆叠(提高容灾能力)
  • 应用部署需要按 AZ(available zone)维度打散(提高容灾能力)

这些基本的诉求,通过 Kubernetes 原生提供的 pod affinity、topology spread constraints 等能力目前都能够满足了。但在实际的生产场景下,还有着太多更加复杂的分区与弹性需求,以下举一些实际的例子:

  • 按 zone 打散时,需要指定在不同 zone 中部署的比例数,比如某个应用在 zone a、b、c 中部署的 Pod 数量比例为 1 : 1 : 2 等(由于一些现实的原因比如该应用在多个 zone 中的流量不均衡等)
  • 存在多个 zone 或不同机型的拓扑,应用扩容时,优先部署到某个 zone 或机型上,当资源不足时再部署到另一个 zone 或机型上(往后以此类推);应用缩容时,要按反向顺序,优先缩容后面 zone 或机型上的 Pod(往前以此类推)
  • 存在多个基础的节点池和弹性的节点池,应用部署时需要固定数量或比例的 Pod 部署在基础节点池,其余的都扩到弹性节点池

对于这些例子,过去一般只能将一个应用拆分为多个 Workload(比如 Deployment)来部署,才能解决应用在不同拓扑下采用不同比例数量、扩缩容优先级、资源感知、弹性选择等场景的基本问题,但还是需要 PaaS 层深度定制化,来支持对一个应用多个 Workload 的精细化管理。

针对这些问题,在 Kruise v0.10.0 版本中新增了 WorkloadSpread 资源,目前它支持配合 Deployment、ReplicaSet、CloneSet 这些 Workload 类型,来管理它们下属 Pod 的分区与弹性拓扑。 以下是一个简化的例子:

apiVersion: apps.kruise.io/v1alpha1
kind: WorkloadSpread
metadata:
name: workloadspread-demo
spec:
targetRef:
apiVersion: apps/v1 | apps.kruise.io/v1alpha1
kind: Deployment | CloneSet
name: workload-xxx
subsets:
- name: subset-a
requiredNodeSelectorTerm:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- zone-a
maxReplicas: 10 | 30%
- name: subset-b
requiredNodeSelectorTerm:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- zone-b

创建这个 WorkloadSpread 可以通过 targetRef 关联到一个 Workload 对象上,然后这个 Workload 在扩容 pod 的过程中,Pod 会被 Kruise 按上述策略注入对应的拓扑规则。这是一种旁路的注入和管理方式,本身不会干涉 Workload 对 Pod 的扩缩容、发布管理。

注意:WorkloadSpread 对 Pod 的缩容的优先级控制是通过 Pod Deletion Cost 来实现的:

  • 如果 Workload 类型是 CloneSet,则已经支持了这个 feature,可以实现缩容优先级
  • 如果 Workload 类型是 Deployment/ReplicaSet,则要求 Kubernetes version >= 1.21,且在 1.21 中要在 kube-controller-manager 上开启 PodDeletionCost 这个 feature-gate

使用 WorkloadSpread 功能,需要在 安装/升级 Kruise v0.10.0 的时候打开 WorkloadSpread 这个 feature-gate。

PodUnavailableBudget:应用可用性防护

在诸多 Voluntary Disruption 场景中 Kubernetes 原生提供的 Pod Disruption Budget(PDB) 通过限制同时中断的 Pod 数量,来保证应用的高可用性。

但还有很多场景中,即便有 PDB 防护依然将会导致业务中断、服务降级,比如:

  • 应用 owner 通过 Deployment 正在进行版本升级,与此同时集群管理员由于机器资源利用率过低正在进行 node 缩容
  • 中间件团队利用 SidecarSet 正在原地升级集群中的sidecar版本(例如:ServiceMesh envoy),同时HPA正在对同一批应用进行缩容
  • 应用 owner 和中间件团队利用 CloneSet、SidecarSet 原地升级的能力,正在对同一批 Pod 进行升级

这其实很好理解 -- PDB 只能防控通过 Eviction API 来触发的 Pod 驱逐(例如 kubectl drain驱逐node上面的所有Pod),但是对于 Pod 删除、原地升级 等很多操作是无法防护的。

在 Kruise v0.10.0 版本中新增的 PodUnavailableBudget(PUB)功能,则是对原生 PDB 的强化扩展。它包含了 PDB 自身的能力,并在此基础上增加了对更多 Voluntary Disruption 操作的防护,包括但不限于 Pod 删除、原地升级 等。

apiVersion: apps.kruise.io/v1alpha1
kind: PodUnavailableBudget
metadata:
name: web-server-pub
namespace: web
spec:
targetRef:
apiVersion: apps/v1 | apps.kruise.io/v1alpha1
kind: Deployment | CloneSet | StatefulSet | ...
name: web-server
# selector 与 targetRef 二选一配置
# selector:
# matchLabels:
# app: web-server
# 保证的最大不可用数量
maxUnavailable: 60%
# 保证的最小可用数量
# minAvailable: 40%

使用 PodUnavailableBudget 功能,需要在 安装/升级 Kruise v0.10.0 的时候打开feature-gate(两个可以选择打开一个,也可以都打开):

  • PodUnavailableBudgetDeleteGate:拦截防护 Pod 删除、驱逐 等操作
  • PodUnavailableBudgetUpdateGate:拦截防护 Pod 原地升级 等更新操作

CloneSet 支持按拓扑规则缩容

在 CloneSet 缩容(调小 replicas 数量)的时候,选择哪些 Pod 删除是有一套固定算法排序的:

  1. 未调度 < 已调度
  2. PodPending < PodUnknown < PodRunning
  3. Not ready < ready
  4. 较小 pod-deletion cost < 较大 pod-deletion cost
  5. 较大打散权重 < 较小
  6. 处于 Ready 时间较短 < 较长
  7. 容器重启次数较多 < 较少
  8. 创建时间较短 < 较长

其中,“4” 是在 Kruise v0.9.0 中开始提供的特性,用于支持用户指定删除顺序(WorkloadSpread 就是利用这个功能实现缩容优先级);而 “5” 则是当前 v0.10.0 提供的特性,即在缩容的时候会参考应用的拓扑打散来排序

  • 如果应用配置了 topology spread constraints,则 CloneSet 缩容时会按照其中的 topology 维度打散来选择 Pod 删除(比如尽量打平多个 zone 上部署 Pod 的数量)
  • 如果应用没有配置 topology spread constraints,则默认情况下 CloneSet 缩容时会按照 node 节点维度打散来选择 Pod 删除(尽量减少同 node 上的堆叠数量)

Advanced StatefulSet 支持流式扩容

为了避免在一个新 Advanced StatefulSet 创建后有大量失败的 pod 被创建出来,从 Kruise v0.10.0 版本开始引入了在 scale strategy 中的 maxUnavailable 策略:

apiVersion: apps.kruise.io/v1beta1
kind: StatefulSet
spec:
# ...
replicas: 100
scaleStrategy:
maxUnavailable: 10% # percentage or absolute number

当这个字段被设置之后,Advanced StatefulSet 会保证创建 pod 之后不可用 pod 数量不超过这个限制值。 比如说,上面这个 StatefulSet 一开始只会一次性创建 10 个 pod。在此之后,每当一个 pod 变为 running、ready 状态后,才会再创建一个新 pod 出来。

注意:这个功能只允许在 podManagementPolicy 是 Parallel 的 StatefulSet 中使用。

More

更多版本变化,请参考 release pageChangeLog

· 阅读需要 1 分钟
Siyu Wang

OpenKruise 在 2021.5.20 发布了最新的 v0.9.0 版本,新增了 Pod 容器重启、资源级联删除防护等重磅功能,本文以下对新版本做整体的概览介绍。

Pod 容器重启/重建

“重启” 是一个很朴素的需求,即使日常运维的诉求,也是技术领域较为常见的 “恢复手段”。而在原生的 Kubernetes 中,并没有提供任何对容器粒度的操作能力,Pod 作为最小操作单元也只有创建、删除两种操作方式。

有的同学可能会问,在云原生时代,为什么用户还要关注容器重启这种运维操作呢?在理想的 serverless 模式下,业务只需要关心服务自身就好吧?

这来自于云原生架构和过去传统基础基础设施的差异性。在传统的物理机、虚拟机时代,一台机器上往往会部署和运行多个应用的实例,并且机器和应用的生命周期是不同的;在这种情况下,应用实例的重启可能仅仅是一条 systemctl 或 supervisor 之类的指令,而无需将整个机器重启。然而,在容器与云原生模式下,应用的生命周期是和 Pod 容器绑定的;即常规情况下,一个容器只运行一个应用进程,一个 Pod 也只提供一个应用实例的服务。

基于上述的限制,目前原生 Kubernetes 之下是没有 API 来为上层业务提供容器(应用)重启能力的。而 Kruise v0.9.0 版本提供了一种单 Pod 维度的容器重启能力,兼容 1.16 及以上版本的标准 Kubernetes 集群。在安装或升级 Kruise 之后,只需要创建 ContainerRecreateRequest(简称 CRR) 对象来指定重启,最简单的 YAML 如下:

apiVersion: apps.kruise.io/v1alpha1
kind: ContainerRecreateRequest
metadata:
namespace: pod-namespace
name: xxx
spec:
podName: pod-name
containers:
- name: app
- name: sidecar

其中,namespace 需要与要操作的 Pod 在同一个命名空间,name 可自选。spec 中 podName 是 Pod 名字,containers 列表则可以指定 Pod 中一个或多个容器名来执行重启。

除了上述必选字段外,CRR 还提供了多种可选的重启策略:

spec:
# ...
strategy:
failurePolicy: Fail
orderedRecreate: false
terminationGracePeriodSeconds: 30
unreadyGracePeriodSeconds: 3
minStartedSeconds: 10
activeDeadlineSeconds: 300
ttlSecondsAfterFinished: 1800
  • failurePolicy: Fail 或 Ignore,默认 Fail;表示一旦有某个容器停止或重建失败,CRR 立即结束
  • orderedRecreate: 默认 false;true 表示列表有多个容器时,等前一个容器重建完成了,再开始重建下一个
  • terminationGracePeriodSeconds: 等待容器优雅退出的时间,不填默认用 Pod 中定义的时间
  • unreadyGracePeriodSeconds: 在重建之前先把 Pod 设为 not ready,并等待这段时间后再开始执行重建
    • 注:该功能依赖于 KruisePodReadinessGate 这个 feature-gate 要打开,后者会在每个 Pod 创建的时候注入一个 readinessGate。 否则,默认只会给 Kruise workload 创建的 Pod 注入 readinessGate,也就是说只有这些 Pod 才能在 CRR 重建时使用 unreadyGracePeriodSeconds
  • minStartedSeconds: 重建后新容器至少保持运行这段时间,才认为该容器重建成功
  • activeDeadlineSeconds: 如果 CRR 执行超过这个时间,则直接标记为结束(未完成的容器标记为失败)
  • ttlSecondsAfterFinished: CRR 结束后,过了这段时间自动被删除掉

实现原理:当用户创建了 CRR 后,经过了 kruise-manager 中心端的初步处理,会被 Pod 所在节点上的 kruise-daemon 收到并开始执行。执行的过程如下:

  1. 如果 Pod 容器定义了 preStop,kruise-daemon 会先走 CRI 运行时 exec 到容器中执行 preStop
  2. 如果没有 preStop 或执行完成,kruise-daemon 调用 CRI 接口将容器停止
  3. kubelet 感知到容器退出,则会新建一个 “序号” 递增的新容器,并开始启动(以及执行 postStart)
  4. kruise-daemon 感知到新容器启动成功,上报 CRR 重启完成

ContainerRecreateRequest

上述的容器 “序号” 其实就对应了 Pod status 中 kubelet 上报的 restartCount。因此,在容器重启后会看到 Pod 的 restartCount 增加。另外,因为容器发生了重建,之前临时写到旧容器 rootfs 中的文件会丢失,但是 volume mount 挂载卷中的数据仍然存在。

级联删除防护

Kubernetes 的面向终态自动化是一把 “双刃剑”,它既为应用带来了声明式的部署能力,同时也潜在地会将一些误操作行为被终态化放大。例如它的 “级联删除” 机制,即正常情况(非 orphan 删除)下一旦父类资源被删除,则所有子类资源都会被关联删除:

  1. 删除一个 CRD,其所有对应的 CR 都被清理掉
  2. 删除一个 namespace,这个命名空间下包括 Pod 在内所有资源都被一起删除
  3. 删除一个 workload(Deployment/StatefulSet/...),则下属所有 Pod 被删除

类似这种 “级联删除” 带来的故障,我们已经听到不少社区 K8s 用户和开发者带来的抱怨。对于任何一家企业来说,其生产环境发生这种规模误删除都是不可承受之痛。

因此,在 Kruise v0.9.0 版本中,我们建立了防级联删除能力,期望能为更多的用户带来稳定性保障。在当前版本中如果需要使用该功能,则在安装或升级 Kruise 的时候需要显式打开 ResourcesDeletionProtection 这个 feature-gate。

对于需要防护删除的资源对象,用户可以给其打上 policy.kruise.io/delete-protection 标签,value 可以有两种:

  • Always: 表示这个对象禁止被删除,除非上述 label 被去掉
  • Cascading:这个对象如果还有可用的下属资源,则禁止被删除

目前支持的资源类型、以及 cascading 级联关系如下:

KindGroupVersionCascading judgement
Namespacecorev1whether there is active Pods in this namespace
CustomResourceDefinitionapiextensions.k8s.iov1beta1, v1whether there is existing CRs of this CRD
Deploymentappsv1whether the replicas is 0
StatefulSetappsv1whether the replicas is 0
ReplicaSetappsv1whether the replicas is 0
CloneSetapps.kruise.iov1alpha1whether the replicas is 0
StatefulSetapps.kruise.iov1alpha1, v1beta1whether the replicas is 0
UnitedDeploymentapps.kruise.iov1alpha1whether the replicas is 0

CloneSet 新增功能

删除优先级

controller.kubernetes.io/pod-deletion-cost 是从 Kubernetes 1.21 版本后加入的 annotation,ReplicaSet 在缩容时会参考这个 cost 数值来排序。 CloneSet 从 Kruise v0.9.0 版本后也同样支持了这个功能。

用户可以把这个 annotation 配置到 pod 上,它的 value 数值是 int 类型,表示这个 pod 相较于同个 CloneSet 下其他 pod 的 "删除代价",代价越小的 pod 删除优先级相对越高。 没有设置这个 annotation 的 pod 默认 deletion cost 是 0。

注意这个删除顺序并不是强制保证的,因为真实的 pod 的删除类似于下述顺序:

  1. 未调度 < 已调度
  2. PodPending < PodUnknown < PodRunning
  3. Not ready < ready
  4. 较小 pod-deletion cost < 较大 pod-deletion cost
  5. 处于 Ready 时间较短 < 较长
  6. 容器重启次数较多 < 较少
  7. 创建时间较短 < 较长

配合原地升级的镜像预热

当使用 CloneSet 做应用原地升级时,只会升级容器镜像、而 Pod 不会发生重建。这就保证了 Pod 升级前后所在 node 不会发生变化,从而在原地升级的过程中,如果 CloneSet 提前在所有 Pod 节点上先把新版本镜像拉取好,则在后续的发布批次中 Pod 原地升级速度会得到大幅度提高。

在当前版本中如果需要使用该功能,则在安装或升级 Kruise 的时候需要显式打开 PreDownloadImageForInPlaceUpdate 这个 feature-gate。打开后,当用户更新了 CloneSet template 中的镜像、且发布策略支持原地升级,则 CloneSet 会自动为这个新镜像创建 ImagePullJob 对象(OpenKruise 提供的批量镜像预热功能),来提前在 Pod 所在节点上预热新镜像。

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

apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
annotations:
apps.kruise.io/image-predownload-parallelism: "5"

先扩再缩的 Pod 置换方式

在过去版本中,CloneSet 的 maxUnavailablemaxSurge 策略只对应用发布过程生效。而从 Kruise v0.9.0 版本开始,这两个策略同样会对 Pod 指定删除生效。

也就是说,当用户通过 podsToDeleteapps.kruise.io/specified-delete: true 方式(具体见官网文档)来指定一个或多个 Pod 期望删除时,CloneSet 只会在当前不可用 Pod 数量(相对于 replicas 总数)小于 maxUnavailable 的时候才执行删除。同时,如果用户配置了 maxSurge 策略,则 CloneSet 有可能会先创建一个新 Pod、等待新 Pod ready、再删除指定的旧 Pod。

具体采用什么样的置换方式,取决于当时的 maxUnavailable 和实际不可用 Pod 数量。比如:

  • 对于一个 CloneSet maxUnavailable=2, maxSurge=1 且有一个 pod-a 处于不可用状态, 如果你对另一个 pod-b 指定删除, 那么 CloneSet 会立即删除它,然后创建一个新 Pod。
  • 对于一个 CloneSet maxUnavailable=1, maxSurge=1 且有一个 pod-a 处于不可用状态, 如果你对另一个 pod-b 指定删除, 那么 CloneSet 会先新建一个 Pod、等待它 ready,最后再删除 pod-b。
  • 对于一个 CloneSet maxUnavailable=1, maxSurge=1 且有一个 pod-a 处于不可用状态, 如果你对这个 pod-a 指定删除, 那么 CloneSet 会立即删除它,然后创建一个新 Pod。
  • ...

基于 partition 终态的高效回滚

在原生的 workload 中,Deployment 自身发布不支持灰度发布,StatefulSet 有 partition 语义来允许用户控制灰度升级的数量;而 Kruise workload 如 CloneSet、Advanced StatefulSet,也都提供了 partition 来支持灰度分批。

对于 CloneSet,Partition 的语义是 保留旧版本 Pod 的数量或百分比。比如说一个 100 个副本的 CloneSet,在升级镜像时将 partition 数值阶段性改为 80 -> 60 -> 40 -> 20 -> 0,则完成了分 5 批次发布。

但过去,不管是 Deployment、StatefulSet 还是 CloneSet,在发布的过程中如果想要回滚,都必须将 template 信息(镜像)重新改回老版本。后两者在灰度的过程中,将 partition 调小会触发旧版本升级为新版本,但再次 partition 调大则不会处理。

从 v0.9.0 版本开始,CloneSet 的 partition 支持了 “终态回滚” 功能。如果在安装或升级 Kruise 的时候打开了 CloneSetPartitionRollback 这个 feature-gate,则当用户将 partition 调大时,CloneSet 会将对应数量的新版本 Pod 重新回滚到老版本。

这样带来的好处是显而易见的:在灰度发布的过程中,只需要前后调节 partition 数值,就能灵活得控制新旧版本的比例数量。但需要注意的是,CloneSet 所依据的 “新旧版本” 对应的是其 status 中的 updateRevision 和 currentRevision:

  • updateRevision:对应当前 CloneSet 所定义的 template 版本
  • currentRevision:该 CloneSet 前一次全量发布成功的 template 版本

短 hash

默认情况下,CloneSet 在 Pod label 中设置的 controller-revision-hash 值为 ControllerRevision 的完整名字,比如:

apiVersion: v1
kind: Pod
metadata:
labels:
controller-revision-hash: demo-cloneset-956df7994

它是通过 CloneSet 名字和 ControllerRevision hash 值拼接而成。 通常 hash 值长度为 8~10 个字符,而 Kubernetes 中的 label 值不能超过 63 个字符。 因此 CloneSet 的名字一般是不能超过 52 个字符的,如果超过了,则无法成功创建出 Pod。

在 v0.9.0 版本引入了 CloneSetShortHash 新的 feature-gate。 如果它被打开,CloneSet 只会将 Pod 中的 controller-revision-hash 的值只设置为 hash 值,比如 956df7994,因此 CloneSet 名字的长度不会有任何限制了。(即使启用该功能,CloneSet 仍然会识别和管理过去存量的 revision label 为完整格式的 Pod。)

SidecarSet 新增功能

Sidecar 热升级

SidecarSet 是 Kruise 提供的独立管理 sidecar 容器的 workload。用户可以通过 SidecarSet,来在一定范围的 Pod 中注入和升级指定的 sidecar 容器。

默认情况下,sidecar 的独立原地升级是先停止旧版本的容器,然后创建新版本的容器。这种方式更加适合不影响Pod服务可用性的sidecar容器,比如说日志收集 agent,但是对于很多代理或运行时的 sidecar 容器,例如 Istio Envoy,这种升级方法就有问题了。Envoy 作为 Pod 中的一个代理容器,代理了所有的流量,如果直接重启升级,Pod 服务的可用性会受到影响。如果需要单独升级 envoy sidecar,就需要复杂的 grace 终止和协调机制。所以我们为这种 sidecar 容器的升级提供了一种新的解决方案,即热升级(hot upgrade)。

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
spec:
# ...
containers:
- name: nginx-sidecar
image: nginx:1.18
lifecycle:
postStart:
exec:
command:
- /bin/bash
- -c
- /usr/local/bin/nginx-agent migrate
upgradeStrategy:
upgradeType: HotUpgrade
hotUpgradeEmptyImage: empty:1.0.0
  • upgradeType: HotUpgrade代表该sidecar容器的类型是hot upgrade,将执行热升级方案hotUpgradeEmptyImage: 当热升级sidecar容器时,业务必须要提供一个empty容器用于热升级过程中的容器切换。empty容器同sidecar容器具有相同的配置(除了镜像地址),例如:command, lifecycle, probe等,但是它不做任何工作。
  • lifecycle.postStart: 状态迁移,该过程完成热升级过程中的状态迁移,该脚本需要由业务根据自身的特点自行实现,例如:nginx热升级需要完成Listen FD共享以及流量排水(reload)

更多

更多版本变化,请参考 release pageChangeLog