Kruise 是 OpenKruise 中的核心项目之一,它提供一套在Kubernetes核心控制器之外的扩展 workload 管理和实现。
目前,Kruise 提供了以下 5 个 workload 控制器:

  • CloneSet: 提供了更加高效、确定可控的应用管理和部署能力,支持优雅原地升级、指定删除、发布顺序可配置、并行/灰度发布等丰富的策略,可以满足更多样化的应用场景。
  • Advanced StatefulSet: 基于原生 StatefulSet 之上的增强版本,默认行为与原生完全一致,在此之外提供了原地升级、并行发布(最大不可用)、发布暂停等功能。
  • SidecarSet: 对 sidecar 容器做统一管理,在满足 selector 条件的 Pod 中注入指定的 sidecar 容器。
  • UnitedDeployment: 通过多个 subset workload 将应用部署到多个可用区。
  • BroadcastJob: 配置一个 job,在集群中所有满足条件的 Node 上都跑一个 Pod 任务。

项目的 roadmap 参考这里Video by Lachlan Evenson 是一个对于新人很友好的 demo。

安装前检查

使用 Kruise 需要在 kube-apiserver 启用一些 feature-gate 比如 MutatingAdmissionWebhookValidatingAdmissionWebhook (K8s 1.12以上默认开启)。 如果你的 K8s 版本低于 1.12,需要先执行以下命令来验证是否支持:

  1. sh -c "$(curl -fsSL https://raw.githubusercontent.com/openkruise/kruise/master/scripts/check_for_installation.sh)"

使用 helm charts 安装 [推荐]

推荐使用 helm v3 安装 Kruise,helm 是一个简单的命令行工具可以从这里 获取。

  1. helm install kruise https://github.com/openkruise/kruise/releases/download/v0.5.0/kruise-chart.tgz

注意直接安装 chart 会使用默认的 template values,你也可以根据你的集群情况指定一些特殊配置,比如修改 resources 限制或者只启用某些特定的控制器能力。

使用 YAML files 安装 [不推荐]

  1. # Install CRDs
  2. kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/crds/apps_v1alpha1_broadcastjob.yaml
  3. kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/crds/apps_v1alpha1_sidecarset.yaml
  4. kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/crds/apps_v1alpha1_statefulset.yaml
  5. kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/crds/apps_v1alpha1_uniteddeployment.yaml
  6. kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/crds/apps_v1alpha1_cloneset.yaml
  7. # Install kruise-controller-manager
  8. kubectl apply -f https://raw.githubusercontent.com/kruiseio/kruise/master/config/manager/all_in_one.yaml

注意 all_in_one.yaml 中包含的 Kruise-manager 镜像是每天周期性从 master 分支打出来的,无法保证功能的稳定性。 所以你可以通过 YAML 部署到测试集群做验证,但不推荐在生产环境使用。
官方的 kruise-manager 镜像维护在 docker hub

可选: 启用部分特定控制器

如果你只需要使用某些 Kruise 中的控制器并关闭其他的控制器,你可以做以下两个方式或同时做:

  1. 只安装你需要使用的 CRD。
  2. 在 kruise-manager 容器中设置 CUSTOM_RESOURCE_ENABLE 环境变量,配置需要启用的功能,比如 CUSTOM_RESOURCE_ENABLE=CloneSet,StatefulSet

如果使用 helm chart 安装,可以通过以下参数来生效这个配置:

  1. helm install kruise https://github.com/openkruise/kruise/releases/download/v0.5.0/kruise-chart.tgz --set manager.custom_resource_

上面部分来自官网。有兴趣的自己的了解

安装完成后可以看到集群中已经存在其CRD了。

  1. # kubectl get crd | grep kruise
  2. broadcastjobs.apps.kruise.io 2020-05-31T00:43:29Z
  3. clonesets.apps.kruise.io 2020-05-31T00:43:28Z
  4. sidecarsets.apps.kruise.io 2020-05-31T00:43:28Z
  5. statefulsets.apps.kruise.io 2020-05-31T00:43:28Z
  6. uniteddeployments.apps.kruise.io 2020-05-31T00:43:28Z

Advanced StatefulSet 顾名思义,是原生 StatefulSet 的增强版,默认行为与原生完全一致,在此之外提供了原地升级、并行发布(最大不可用)、发布暂停等功能。而 CloneSet 则对标原生 Deployment,主要服务于无状态应用,提供了最为全面丰富的部署发布策略。

原地升级

CloneSet、Advanced StatefulSet 均支持指定 Pod 升级方式:

  1. ReCreate:重建 Pod 升级,和原生 Deployment/StatefulSet 一致;
  2. InPlaceIfPossible:如果只修改 image 和 metadata 中的 labels/annotations 等字段,则触发 Pod 原地升级;如果修改了其他 template spec 中的字段,则退化到 Pod 重建升级;
  3. InPlaceOnly:只允许修改 image 和 metadata 中的 labels/annotations 等字段,只会使用原地升级。

所谓原地升级,就是在升级 template 模板的时候,workload 不会把原 Pod 删除、新建,而是直接在原 Pod 对象上更新对应的 image 等数据。
13.5、阿里扩展 workload测试 - 图1
如上图所示,在原地升级的时候 CloneSet 只会更新 Pod spec 中对应容器的 image,而后 kubelet 看到 Pod 中这个容器的定义发生了变化,则会把对应的容器停掉、拉取新的镜像、并使用新镜像创建启动容器。另外可以看到在过程中,这个 Pod 的 sandbox 容器以及其他本次未升级的容器还一直处于正常运行状态,只有需要升级的容器会受到影响。
原地升级给我们带来的好处实在太多了:

  • 首先就是发布效率大大提升了,根据非完全统计数据,在阿里环境下原地升级至少比完全重建升级提升了 80% 以上的发布速度:不仅省去了调度、分配网络、分配远程盘的耗时,连拉取新镜像的时候都得益于 node 上已有旧镜像、只需要拉取较少的增量 layer);
  • IP 不变、升级过程 Pod 网络不断,除本次升级外的其他容器保持正常运行;
  • Volume 不变,完全复用原容器的挂载设备;
  • 保障了集群确定性,使排布拓扑能通过大促验证。

流式+分批发布

目前 Deployment 支持 maxUnavailable/maxSurge 的流式升级,而 StatefulSet 支持 partition 的分批升级。但问题在于,Deployment 无法灰度分批,而 StatefulSet 则只能一个一个 Pod 串行发布,没办法并行的流式升级。

Advanced StatefulSet引入了maxUnavailable 。原生 StatefulSet 的 one by one 发布,大家其实可以理解为一个强制 maxUnavailable=1 的过程,而 Advanced StatefulSet 中如果我们配置了更大的 maxUnavailable,那么就支持并行发布更多的 Pod 了。

再来看一下 CloneSet,它支持原生 Deployment 和 StatefulSet 的全部发布策略,包括 maxUnavailable、maxSurge、partition。那么 CloneSet 是如何把它们结合在一起的呢?我们来看一个例子:

  1. apiVersion: apps.kruise.io/v1alpha1
  2. kind: CloneSet
  3. # ...
  4. spec:
  5. replicas: 5 # Pod 总数为 5
  6. updateStrategy:
  7. type: InPlaceIfPossible
  8. maxSurge: 20% # 多扩出来 5 * 20% = 1 个 Pod (rounding up)
  9. maxUnavailable: 0 # 保证发布过程 5 - 0 = 5 个 Pod 可用
  10. partition: 3 # 保留 3 个旧版本 Pod (只发布 5 - 3 = 2 个 Pod)

针对这个副本数为 5 的 CloneSet,如果我们修改了 template 中的 image,同时配置:maxSurge=20% maxUnavailable=0 partition=3。当开始发布后:

  1. 先扩出来 1 个新版本的 Pod,5 个存量 Pod 保持不动;
  2. 新 Pod ready 后,逐步把旧版本 Pod 做原地升级;
  3. 直到剩余 3 个旧版本 Pod 时,因为满足了 partition 终态,会把新版本 Pod 再删除 1 个;
  4. 此时 Pod 总数仍然为 5,其中 3 个旧版本、1 个新版本。

如果我们接下来把 partition 调整为 0,则 CloneSet 还是会先扩出 1 个额外的新版 Pod,随后逐渐将所有 Pod 升级到新版,最终再次删除一个 Pod,达到 5 个副本全量升级的终态。

发布顺序可配置

对于原生的 Deployment 和 StatefulSet,用户是无法配置发布顺序的。Deployment 下的 Pod 发布顺序完全依赖于它修改 ReplicaSet 后的扩缩顺序,而 StatefulSet 则严格按照 order 的反序来做一一升级。
但在 CloneSet 和 Advanced StatefulSet 中,我们增加了发布顺序的可配置能力,使用户可以定制自己的发布顺序。目前可以通过以下两种发布优先级和一种发布打散策略来定义顺序:

  • 优先级(1):按给定 label key,在发布时根据 Pod labels 中这个 key 对应的 value 值作为权重:

    1. apiVersion: apps.kruise.io/v1alpha1
    2. kind: CloneSet
    3. spec:
    4. # ...
    5. updateStrategy:
    6. priorityStrategy:
    7. orderPriority:
    8. - orderedKey: some-label-key
  • 优先级(2):按 selector 匹配计算权重,发布时根据 Pod 对多个 weight selector 的匹配情况计算权重总和:

    1. apiVersion: apps.kruise.io/v1alpha1
    2. kind: CloneSet
    3. spec:
    4. # ...
    5. updateStrategy:
    6. priorityStrategy:
    7. weightPriority:
    8. - weight: 50
    9. matchSelector:
    10. matchLabels:
    11. test-key: foo
    12. - weight: 30
    13. matchSelector:
    14. matchLabels:
    15. test-key: bar
  • 打散:将匹配 key-value 的 Pod 打散到不同批次中发布:

    1. apiVersion: apps.kruise.io/v1alpha1
    2. kind: CloneSet
    3. spec:
    4. # ...
    5. updateStrategy:
    6. scatterStrategy:
    7. - key: some-label-key
    8. value: foo

cloneSets

cloneSets的功能比较丰富,更多资料可以去这里了解。

  • 支持动态pvc,deployment没有这个功能。
  • 选择性pod删除,就是用户可以选择删除哪个pod
  • 支持原地升级
  • ….

一个简单的demo如下:

  1. apiVersion: apps.kruise.io/v1alpha1
  2. kind: CloneSet
  3. metadata:
  4. labels:
  5. app: sample
  6. name: sample
  7. spec:
  8. replicas: 5
  9. selector:
  10. matchLabels:
  11. app: sample
  12. template:
  13. metadata:
  14. labels:
  15. app: sample
  16. spec:
  17. containers:
  18. - name: nginx
  19. image: nginx:alpine

和咱们正常创建deploy一样。创建完成后则可以生成5个Pod。

  1. # kubectl get pod
  2. NAME READY STATUS RESTARTS AGE
  3. sample-5pc64 1/1 Running 0 5m52s
  4. sample-g5w28 1/1 Running 0 5m52s
  5. sample-mthv7 1/1 Running 0 5m52s
  6. sample-qn8p5 1/1 Running 0 5m52s
  7. sample-xhbjd 1/1 Running 0 5m52s

支持动态pvc的yaml如下:

  1. apiVersion: apps.kruise.io/v1alpha1
  2. kind: CloneSet
  3. metadata:
  4. labels:
  5. app: sample
  6. name: sample-data
  7. spec:
  8. replicas: 5
  9. selector:
  10. matchLabels:
  11. app: sample
  12. template:
  13. metadata:
  14. labels:
  15. app: sample
  16. spec:
  17. containers:
  18. - name: nginx
  19. image: nginx:alpine
  20. volumeMounts:
  21. - name: data-vol
  22. mountPath: /usr/share/nginx/html
  23. volumeClaimTemplates:
  24. - metadata:
  25. name: data-vol
  26. spec:
  27. accessModes: [ "ReadWriteOnce" ]
  28. resources:
  29. requests:
  30. storage: 20Gi

可以看到其yaml的定义和我们熟悉的statefulset一样。

选择性删除pod,比如我们这里要删除名为sample-xhbjd的pod,我们修改yaml文件如下:

  1. apiVersion: apps.kruise.io/v1alpha1
  2. kind: CloneSet
  3. metadata:
  4. labels:
  5. app: sample
  6. name: sample
  7. spec:
  8. replicas: 4
  9. scaleStrategy:
  10. podsToDelete:
  11. - sample-xhbjd
  12. selector:
  13. matchLabels:
  14. app: sample
  15. template:
  16. metadata:
  17. labels:
  18. app: sample
  19. spec:
  20. containers:
  21. - name: nginx
  22. image: nginx:alpine

我们可以看到执行完yaml,信息如下:

  1. # kubectl apply -f cloneset-demo.yaml
  2. cloneset.apps.kruise.io/sample configured
  3. [root@k8s-master ali-workload]# kubectl get pod
  4. NAME READY STATUS RESTARTS AGE
  5. sample-5pc64 1/1 Running 0 14m
  6. sample-g5w28 1/1 Running 0 14m
  7. sample-mthv7 1/1 Running 0 14m
  8. sample-qn8p5 1/1 Running 0 14m
  9. sample-xhbjd 0/1 Terminating 0 14m

是不是我们定义的这个Pod在被删除?

注意:要删除的pod必须存在,不然就会报错哦。如果定义了要删除了pod而没有修改replicas,那么会删除存在的Pod,然后生成新的Pod。

更多功能慢慢来测试,不过我觉得很强大,而且还实用。

参考连接: