OpenKruise v0.10.0 新特性 WorkloadSpread 解读
简介: 针对需求,OpenKruise 在 v0.10.0 版本中新增了 WorkloadSpread 特性。目前它支持配合 Deployment、ReplicaSet、CloneSet 这些 workload,来管理它们下属 Pod 的分区部署与弹性伸缩。下文会深入介绍 WorkloadSpread 的应用场景和实现原理,帮助用户更好的了解该特性。
背景
Workload 分布在不同 zone,不同的硬件类型,甚至是不同的集群和云厂商已经是一个非常普遍的需求。过去一般只能将一个应用拆分为多个 workload(比如 Deployment)来部署,由 SRE 团队手工管理或者对 PaaS 层深度定制,来支持对一个应用多个 workload 的精细化管理。
进一步来说,在应用部署的场景下有着多种多样的拓扑打散以及弹性的诉求。其中最常见就是按某种或多种拓扑维度打散,比如:
- 应用部署需要按 node 维度打散,避免堆叠(提高容灾能力)。
- 应用部署需要按 AZ(available zone)维度打散(提高容灾能力)。
- 按 zone 打散时,需要指定在不同 zone 中部署的比例数。
随着云原生在国内外的迅速普及落地,应用对于弹性的需求也越来越多。各公有云厂商陆续推出了 Serverless 容器服务来支撑弹性部署场景,如阿里云的弹性容器服务 ECI,AWS 的 Fragate 容器服务等。以 ECI 为例,ECI 可以通过Virtual Kubelet对接 Kubernetes 系统,给予 Pod 一定的配置就可以调度到 virtual-node 背后的 ECI 集群。总结一些常见的弹性诉求,比如:
- 应用优先部署到自有集群,资源不足时再部署到弹性集群。缩容时,优先从弹性节点缩容以节省成本。
- 用户自己规划基础节点池和弹性节点池。应用部署时需要固定数量或比例的 Pod 部署在基础节点池,其余的都扩到弹性节点池。
针对这些需求,OpenKruise 在 v0.10.0 版本中新增了 WorkloadSpread 特性。目前它支持配合 Deployment、ReplicaSet、CloneSet 这些 workload,来管理它们下属 Pod 的分区部署与弹性伸缩。下文会深入介绍 WorkloadSpread 的应用场景和实现原理,帮助用户更好的了解该特性
WorkloadSpread介绍
官方文档(见文末相关链接一)
简而言之,WorkloadSpread 能够将 workload 所属的 Pod 按一定规则分布到不同类型的 Node 节点上,能够同时满足上述的打散与弹性场景。
现有方案对比
简单对比一些社区已有的方案。
Pod Topology Spread Constrains(见文末相关链接二)
Pod Topology Spread Constrains 是 Kubernetes 社区提供的方案,可以定义按 topology key 的水平打散。用户在定义完后,调度器会依据配置选择符合分布条件的 node。
由于 PodTopologySpread 更多的是均匀打散,无法支持自定义的分区数量以及比例配置,且缩容时会破坏分布。WorkloadSpread 可以自定义各个分区的数量,并且管理着缩容的顺序。因此在一些场景下可以避免 PodTopologySpread 的不足。
UnitedDeployment(见文末相关链接三)
UnitedDeployment 是 Kruise 社区提供的方案,通过创建和管理多个 workload 管理多个区域下的 Pod。
UnitedDeployment非常好的支持了打散与弹性的需求,不过它是一个全新的 workload,用户的使用和迁移成本会比较高。而 WorkloadSpread 是一种轻量化的方案,只需要简单的配置并关联到 workload 即可。
应用场景
下面我会列举一些 WorkloadSpread 的应用场景,给出对应的配置,帮助大家快速了解 WorkloadSpread 的能力。
1. 基础节点池至多部署 100 个副本,剩余的部署到弹性节点池

subsets:
- name: subset-normal
maxReplicas: 100
requiredNodeSelectorTerm:
matchExpressions:
- key: app.deploy/zone
operator: In
values:
- normal
- name: subset-elastic #副本数量不限
requiredNodeSelectorTerm:
matchExpressions:
- key: app.deploy/zone
operator: In
values:
- elastic
当 workload 少于 100 副本时,全部部署到 normal 节点池,超过 100 个部署到 elastic 节点池。缩容时会优先删除 elastic 节点上的 Pod。
由于 WorkloadSpread 不侵入 workload,只是限制住了 workload 的分布,我们还可以通过结合 HPA 根据资源负载动态调整副本数,这样当业务高峰时会自动调度到 elastic 节点上去,业务低峰时会优先释放 elastic 节点池上的资源。
2. 优先部署到基础节点池,资源不足再部署到弹性资源池

scheduleStrategy:
type: Adaptive
adaptive:
rescheduleCriticalSeconds: 30
disableSimulationSchedule: false
subsets:
- name: subset-normal #副本数量不限
requiredNodeSelectorTerm:
matchExpressions:
- key: app.deploy/zone
operator: In
values:
- normal
- name: subset-elastic #副本数量不限
requiredNodeSelectorTerm:
matchExpressions:
- key: app.deploy/zone
operator: In
values:
- elastic
两个 subset 都没有副本数量限制,且启用 Adptive 调度策略的模拟调度和 Reschedule 能力。部署效果是优先部署到 normal 节点池,normal 资源不足时,webhook 会通过模拟调度选择 elastic 节点。当 normal 节点池中的 Pod 处于 pending 状态超过 30s 阈值, WorkloadSpread controller 会删除该 Pod 以触发重建,新的 Pod 会被调度到 elastic 节点池。缩容时还是优先缩容 elastic 节点上的 Pod,为用户节省成本。
3. 打散到3个zone,比例分别为1:1:3

subsets:
- name: subset-a
maxReplicas: 20%
requiredNodeSelectorTerm:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- zone-a
- name: subset-b
maxReplicas: 20%
requiredNodeSelectorTerm:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- zone-b
- name: subset-c
maxReplicas: 60%
requiredNodeSelectorTerm:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- zone-c
按照不同 zone 的实际情况,将 workload 按照 1:1:3 的比例打散。WorkloadSpread 会确保 workload 扩缩容时按照定义的比例分布。
4. workload在不同CPU Arch上配置不同的资源配额
workload 分布的 Node 可能有不同的硬件配置,CPU 架构等,这就可能需要为不同的 subset 分别制定 Pod 配置。这些配置可以是 label 和 annotation 等元数据也可以是 Pod 内部容器的资源配额,环境变量等。

subsets:
- name: subset-x86-arch
# maxReplicas...
# requiredNodeSelectorTerm...
patch:
metadata:
labels:
resource.cpu/arch: x86
spec:
containers:
- name: main
resources:
limits:
cpu: "500m"
memory: "800Mi"
- name: subset-arm-arch
# maxReplicas...
# requiredNodeSelectorTerm...
patch:
metadata:
labels:
resource.cpu/arch: arm
spec:
containers:
- name: main
resources:
limits:
cpu: "300m"
memory: "600Mi"
从上面的样例中我们为两个 subset 的 Pod 分别 patch 了不同的 label, container resources,方便我们对 Pod 做更精细化的管理。当 workload 的 Pod 分布在不同的 CPU 架构的节点上,配置不同的资源配额以更好的利用硬件资源。
实现原理
WorkloadSpread 是一个纯旁路的弹性/拓扑管控方案。用户只需要针对自己的 Deployment/CloneSet/Job 对象创建对应的 WorkloadSpread 即可,无需对 workload 做改动,也不会对用户使用 workload 造成额外成本。

1. subset优先级与副本数量控制
WorkloadSpread 中定义了多个 subset,每个 subset 代表一个逻辑域。用户可以自由的根据节点配置,硬件类型,zone 等来划分 subset。特别的,我们规定了 subset 的优先级:
- 按定义从前往后的顺序,优先级从高到低。
- 优先级越高,越先扩容;优先级越低,越先缩容。
2. 如何控制缩容优先级
理论上,WorkloadSpread 这种旁路方案是无法干涉到 workload 控制器里的缩容顺序逻辑的。
不过,这个问题在近期得以解决—— 经过一代代用户的不懈努力(反馈),K8s 从 1.21 版本开始为 ReplicaSet(Deployment)支持了通过设置 controller.kubernetes.io/pod-deletion-cost 这个 annotation 来指定 Pod 的 “删除代价”:deletion-cost 越高的 Pod,删除的优先级越低。
而 Kruise 从 v0.9.0 版本开始,就在 CloneSet 中支持了 deletion-cost 特性。
因此,WorkloadSpread controller通过调整各个 subset 下属 Pod 的 deletion-cost,来控制workload的缩容顺序。
举个例子:对于以下 WorkloadSpread,以及它关联的 CloneSet 有 10 个副本:
subsets:
- name: subset-a
maxReplicas: 8
- name: subset-b # 副本数量不限
则 deletion-cost 数值以及删除顺序为:
- 2 个在 subset-b上的 Pod,deletion-cost 为 100(优先缩容)
- 8 个在 subset-a上的 Pod,deletion-cost 为 200(最后缩容)
然后,如果用户修改了 WorkloadSpread 为:
subsets:
- name: subset-a
maxReplicas: 5 # 8-3,
- name: subset-b
则 workloadspread controller 会将其中 3 个在 susbet-a 上 Pod 的 deletion-cost 值由 200 改为 -100
- 3 个在 subset-a 上的 Pod,deletion-cost 为 -100(优先缩容)
- 2 个在 subset-b 上的 Pod,deletion-cost 为 100(其次缩容)
- 5 个在 subset-a 上的 Pod,deletion-cost 为 200(最后缩容)
这样就能够优先缩容那些超过 subset 副本限制的 Pod 了,当然总体还是按照 subset 定义的顺序从后向前缩容。
3. 数量控制
如何确保 webhook 严格按照 subset 优先级顺序、maxReplicas 数量来注入 Pod 规则是 WorkloadSpread 实现层面的重点难题。
3.1 解决并发一致性问题
在 workloadspread 的 status 中有对应每个 subset 的 status,其中 missingReplicas 字段表示了这个 subset 需要的 Pod 数量,-1 表示没有数量限制(subset 没有配置 maxReplicas)。
spec:
subsets:
- name: subset-a
maxReplicas: 1
- name: subset-b
# ...
status:
subsetStatuses:
- name: subset-a
missingReplicas: 1
- name: subset-b
missingReplicas: -1
# ...
当 webhook 收到 Pod create请求时:
- 根据 subsetStatuses 顺序依次找 missingReplicas 大于 0 或为 -1 的 suitable subset。
- 找到suitable subset后,如果 missingReplicas 大于 0,则先减 1 并尝试更新 workloadspread status。
- 如果更新成功,则将该 subset定义的规则注入到 pod 中。
- 如果更新失败,则重新 get 这个 workloadspread以获取最新的 status,并回到步骤 1(有一定重试次数限制)。
同样,当 webhook 收到 Pod delete/eviction 请求时,则将 missingReplicas 加 1 并更新。
毫无疑问,我们在使用乐观锁来解决更新冲突。但是仅使用乐观锁是不合适的,因为 workload 在创建 Pod 时会并行创建大量的 Pod,apiserver 会在一瞬间发送很多 Pod create 请求到 webhook,并行处理会产生非常多的冲突。大家都知道,冲突太多就不适合使用乐观锁了,因为它解决冲突的重试成本非常高。为此我们还加入了 workloadspread 级别的互斥锁,将并行处理限制为串行处理。加入互斥锁还有新的问题,即当前 groutine 获取锁后,极有可能从 infromer 中拿的 workloadspread 不是最新的,还是会冲突。所以 groutine 在更新完 workloadspread 之后,先将最新的 workloadspread 对象缓存起来再释放锁,这样新的 groutine 获取锁后就可以直接从缓存中拿到最新的 workloadspread。当然,多个 webhook 的情况下还是需要结合乐观锁机制来解决冲突。
3.2 解决数据一致性问题
那么,missingReplicas 数值是否交由 webhook 控制即可呢?答案是不行,因为:
- webhook 收到的 Pod create 请求,最终不一定真的能成功(比如 Pod 不合法,或在后续 quota 等校验环节失败了)。
- webhook 收到的 Pod delete/eviction 请求,最终也不一定真的能成功(比如后续被 PDB、PUB 等拦截了)。
- K8s 里总有种种的可能性,导致 Pod 没有经过 webhook 就结束或没了(比如 phase 进入 Succeeded/Failed,或是 etcd 数据丢了等等)。
- 同时,这也不符合面向终态的设计理念。
因此,workloadspread status 是由 webhook 与 controller 协作来控制的:
- webhook 在 Pod create/delete/eviction 请求链路拦截,修改 missingReplicas 数值。
- 同时 controller 的 reconcile 中也会拿到当前 workload 下的所有 Pod,根据 subset 分类,并将 missingReplicas 更新为当前实际缺少的数量。
- 从上面的分析中,controller 从 informer 中获取 Pod 很可能存在延时,所以我们还在status中增加了 creatingPods map, webook 注入的时候会记录 key 为pod.name, value 为时间戳的一条 entry 到 map,controller 再结合 map 维护真实的 missingReplicas。同理还有一个 deletingPods map 来记录 Pod 的delete/eviction 事件。
4. 自适应调度能力
在 WorkloadSpread 中支持配置 scheduleStrategy。默认情况下,type 为 Fixed,即固定按照各个 subset 的前后顺序、maxReplicas 限制来将 Pod 调度到对应的 subset 中。
但真实的场景下,很多时候 subset 分区或拓扑的资源,不一定能完全满足 maxReplicas 数量。用户需要按照实际的资源情况,来为 Pod 选择有资源的分区扩容。这就需要用 Adaptive 这种自适应的调度分配。
WorkloadSpread 提供的 Adaptive 能力,逻辑上分为两种:
- SimulationSchedule:在 Kruise webhook 中根据 informer 里已有的 nodes/pods 数据,组装出调度账本,对 Pod 进行模拟调度。即通过 nodeSelector/affinity、tolerations、以及基本的 resources 资源,做一次简单的过滤。(对于 vk 这种节点不太适用)
- Reschedule:在将 Pod 调度到一个 subset 后,如果调度失败超过 rescheduleCriticalSeconds 时间,则将该 subset 暂时标记为 unschedulable,并删除 Pod 触发重建。默认情况下,unschedulable 会保留 5min,即在 5min 内的 Pod 创建会跳过这个 subset。
小结
WorkloadSpread 通过结合一些 kubernetes 现有的特性以一种旁路的形式赋予 workload 弹性部署与多域部署的能力。我们希望用户通过使用 WorkloadSpread 降低 workload 部署复杂度,利用其弹性伸缩能力切实降低成本。
目前阿里云内部正在积极的落地,落地过程中的调整会及时反馈社区。未来 WorkloadSpread 还有一些新能力计划,比如让 WorkloadSpread 支持 workload 的存量 Pod 接管,支持批量的 workload 约束,甚至是跨过 workload 层级使用 label 来匹配 Pod。其中一些能力需要实际考量社区用户的需求场景。希望大家多多参与到 Kruise 社区,多提 issue 和 pr,帮助用户解决更多云原生部署方面的难题,构建一个更好的社区。
- Github:https://github.com/openkruise/kruise
- Official:https://openkruise.io/
- Slack: Channel in Kubernetes Slack
原文链接
本文为阿里云原创内容,未经允许不得转载。
OpenKruise v0.10.0 新特性 WorkloadSpread 解读的更多相关文章
- [转] Scala 2.10.0 新特性之字符串插值
[From] https://unmi.cc/scala-2-10-0-feature-string-interpolation/ Scala 2.10.0 新特性之字符串插值 2013-01-20 ...
- 背水一战 Windows 10 (1) - C# 6.0 新特性
[源码下载] 背水一战 Windows 10 (1) - C# 6.0 新特性 作者:webabcd 介绍背水一战 Windows 10 之 C# 6.0 新特性 介绍 C# 6.0 的新特性 示例1 ...
- Hadoop3.0新特性介绍,比Spark快10倍的Hadoop3.0新特性
Hadoop3.0新特性介绍,比Spark快10倍的Hadoop3.0新特性 Apache hadoop 项目组最新消息,hadoop3.x以后将会调整方案架构,将Mapreduce 基于内存+io+ ...
- 返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 Web API
原文:返璞归真 asp.net mvc (10) - asp.net mvc 4.0 新特性之 Web API [索引页][源码下载] 返璞归真 asp.net mvc (10) - asp.net ...
- 背水一战 Windows 10 (43) - C# 7.0 新特性
[源码下载] 背水一战 Windows 10 (43) - C# 7.0 新特性 作者:webabcd 介绍背水一战 Windows 10 之 C# 7.0 新特性 介绍 C# 7.0 的新特性 示例 ...
- 跨时代的MySQL8.0新特性解读
目录 MySQL发展历程 MySQL8.0新特性 秒级加列 性能提升 文档数据库 SQL增强 共用表表达式(CTEs) 不可见索引(Invisible Indexes) 降序索引(Descending ...
- 【译】 Node.js v0.12的新特性 -- Cluster模式采用Round-Robin负载均衡
原文:https://strongloop.com/strongblog/whats-new-in-node-js-v0-12-cluster-round-robin-load-balancing 本 ...
- fir.im Weekly - 从 iOS 10 SDK 新特性说起
从 iOS 7 翻天覆地的全新设计,iOS 8 中 Size Classes 的出现,应用扩展,以及 Cloud Kit 的加入,iOS 9 的分屏多任务特性,今年的 WWDC iOS 10 SDK ...
- C#发展历程以及C#6.0新特性
一.C#发展历程 下图是自己整理列出了C#每次重要更新的时间及增加的新特性,对于了解C#这些年的发展历程,对C#的认识更加全面,是有帮助的. 二.C#6.0新特性 1.字符串插值 (String In ...
- [转]Servlet 3.0 新特性详解
原文地址:http://blog.csdn.net/xiazdong/article/details/7208316 Servlet 3.0 新特性概览 1.Servlet.Filter.Listen ...
随机推荐
- 崩溃bug日志总结3
目录介绍 1.1 OnErrorNotImplementedException[ Can't create handler inside thread that has not called Loop ...
- 从时间复杂度的角度出发,list和vector之间查找,插入,删除等数据操作的区别
list和vector是STL(标准模板库)中常用的两种序列容器,它们各自在不同类型的操作上有着不同的优势.下面是list和vector在不同操作上的擅长之处: list的擅长操作 插入和删除操作:l ...
- Mysql中的锁(case篇)
case1(表锁的读-写-读阻塞) 上篇文档中提到过 WRITE locks normally have higher priority than READ locks to ensure that ...
- .net 开源混淆器 ConfuserEx
官网:http://yck1509.github.io/ConfuserEx/ 下载地址:https://github.com/yck1509/ConfuserEx/releases 使用参考:htt ...
- Fastjson反序列化分析
依赖 先研究1.2.24版本的,版本高了就有waf了,不过也能绕,高版本以后再说 <dependency> <groupId>com.alibaba</groupId&g ...
- 2024 VEXIQ 赛季笔(游)记 Pt.1
2024/03/07 老师让我们做机器初步思考了. 搞搞戒指,只要一个小夹子加上赛季的抬升吸环改一下就可以了,方便的一批. 于是夹子 10 分钟不到搞完了,现在是缝合怪时间. 但是老师下课不让我搞了 ...
- 【已解决】同时使用ajax和form表单传数据的冲突问题
昨天踩了一个大坑,下面总结一下: 前后端数据交互的两种方式: 1.ajax发起请求(请求中可以带有数据)并获取返回的数据 下面给出一个ajax的常见格式: 1 $.ajax({ 2 url:" ...
- #CDQ分治,单调栈,双指针#BZOJ 4237 稻草人 AT1225 かかし
洛谷传送门 BZOJ 4237 稻草人 题意 在一个平面直角坐标系上给出\(n\)个点, 问有多少个点对\((i,j)\)满足\(x_i<x_j,y_i<y_j\), 而且对于\(n\)个 ...
- #树状数组,离散#C 波动序列
分析 设\(dp[i][j][0/1/2/3]\)表示前\(i\)个位置当前选的数为\(j\), 且选择的是第一行/第二行/第三行不下降/第三行不上升, 状态转移方程显然,用线段树或者树状数组维护一下 ...
- Qt使用https协议发送带参数的post请求
背景: 现在公司项目需要做一个检测更新的功能,通过访问后台接口,判断是否需要更新. 后台接口是 https 协议的,接口需要post请求,需要带参数,来判断是哪个软件需要做检测更新的操作. 客户端软件 ...