---
slug: openkruise-0.10.0
title: OpenKruise 0.10.0：新增应用弹性拓扑管理、应用防护等能力
authors: [FillZpp]
tags: [release]
---


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

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

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

- 应用部署需要按 node 维度打散，避免堆叠（提高容灾能力）
- 应用部署需要按 AZ（available zone）维度打散（提高容灾能力）

这些基本的诉求，通过 Kubernetes 原生提供的 pod affinity、[topology spread constraints](https://kubernetes.io/docs/concepts/workloads/pods/pod-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 的分区与弹性拓扑。
以下是一个简化的例子：

```yaml
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](https://kubernetes.io/docs/reference/labels-annotations-taints/#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](https://kubernetes.io/docs/concepts/workloads/pods/disruptions/) 场景中 Kubernetes 原生提供的 [Pod Disruption Budget（PDB）](https://kubernetes.io/docs/tasks/run-application/configure-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 删除、原地升级 等。

```yaml
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](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/)，则 CloneSet 缩容时会按照其中的 topology 维度打散来选择 Pod 删除（比如尽量打平多个 zone 上部署 Pod 的数量）
- 如果应用没有配置 [topology spread constraints](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/)，则默认情况下 CloneSet 缩容时会按照 node 节点维度打散来选择 Pod 删除（尽量减少同 node 上的堆叠数量）

## Advanced StatefulSet 支持流式扩容

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

```yaml
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 page](https://github.com/openkruise/kruise/releases) 或 [ChangeLog](https://github.com/openkruise/kruise/blob/master/CHANGELOG.md)


