【阅读原文】戳:面向Workload级别的灵活可配置Serverless弹性解决方案
本文作者:
阿里云容器应用生态团队研发工程师 仲天云
Serverless是云计算的进一步延伸,因此其继承了云计算的最大特点,即按需弹性伸缩。这样的模型设计让开发者无需关注具体的部署资源,充分利用资源规模效应,提供更好的弹性能力,也能让企业切实享受到真正的按需使用特征。正因如此,更多的云厂商们不约而同地转向Serverless这一新的架构设计理念。
“灵活可配置”作为Serverless技术的弹性核心能力之一,所关注的是“通过简单、少侵入、灵活可配置的方法让具体用云场景能充分使用弹性资源”。其本质是解决了容量规划与实际集群负载配置间的矛盾。本文将依次介绍ElasticWorkload[1]、WorkloadSpread[2]、UnitedDeployment[3]和 ResourcePolicy[4]这四种资源可配置插件,详细探讨它们的核心能力、技术原理与优劣势,以及在真实场景中的应用。通过这些内容分享阿里云容器服务在应对Serverless负载弹性问题时的技术演进和思考。
弹性场景概述
随着Serverless技术的成熟,越来越多企业倾向于使用弹性资源(如ACS等Serverless容器实例)而非静态资源(如由ECS组成的资源池、自建的IDC机房等)来承载具有临时性、潮汐性、突发性等特征的应用,以按需使用的形式提高资源利用效率,降低整体成本。下面列出一些典型的弹性场景:
1. 优先使用线下IDC机房的自建资源,当资源不足时,将应用副本扩展到云上承载。
2. 优先使用包年包月购买的ECS资源池,资源不足时使用按量付费的ACS实例承载副本。
3. 优先使用持有的高质量稳定算力(如独享型的ECS实例),用完后再使用较低质量的算力(如Spot实例)。
4. 为部署到不同算力形式(如X86架构、ARM架构、ACS Serverless实例等)上的容器副本配置不同的资源量,以获得相似的性能表现。
5. 为部署到节点上与Serverless环境的副本注入不同的中间件配置(如节点上副本使用共享Daemon,Serverless副本注入 Sidecar)。
本文介绍的四种组件,在解决上述问题,具有各自的优势场景。用户可以根据自身实际场景选择合适的能力来用好弹性算力。
四种组件的能力及其优势场景概述
• ElasticWorkload:容器服务ACK产品(后文简称ACK)的一个插件,将原始的工作负载Workload克隆为多个副本,为每个副本配置不同的调度策略以实现弹性分区。目前已停止维护,仅推荐ACK产品Kubernetes版本过低(1.18及以下)的老集群使用。
• WorkloadSpread:OpenKruise社区的组件,通过Webhook工作机制拦截符合条件的Pod创建请求,并对其执行Patch操作完成差异化配置注入。适合需要将资源划分为多个弹性分区,并为各分区内Pod的Metadata、Spec等字段进行自定义配置的现存业务。
• UnitedDeployment:OpenKruise社区的一种原生支持弹性分区与Pod自定义配置的工作负载,具有比WorkloadSpread更强的Pod自定义配置与容量规划能力,适合需要划分多个弹性分区并为各个分区单独进行配置的新业务。
• ResourcePolicy:ACK产品调度器提供的原生能力,适合需要为多个弹性分区指定细致的调度策略而不需要对Pod进行自定义的ACK用户使用。
1. ElasticWorkload:通过克隆原始工作负载实现分区弹性
ElasticWorkload是由阿里云容器服务ACK早期提供的一种弹性工作负载控制器。尽管该组件目前已停止维护,但其在实现可配置分区弹性方面的创新思路,为后续其他解决方案的开发提供了重要的技术参考。
通过在ElasticWorkload资源中声明原始负载的引用以及定义弹性单元,控制器能够持续监听原始负载状态,并根据每个弹性单元中的配置参数,克隆并生成多个同类型但具备不同调度策略的弹性负载实例。ElasticWorkload控制器通过在这些克隆的弹性负载实例之间合理分配副本数,进而为Deployment等基础工作负载提供灵活的分区弹性能力,从而优化资源利用率和提升系统的整体弹性。作为一种弹性工作负载,ElasticWorkload同样支持通过HPA自动扩缩容。
ElasticWorkload能够解决分区弹性场景中面临的调度策略问题。与传统的以Pod作为最小控制单元的工作负载控制器不同,ElasticWorkload采用弹性单元(Elastic units)作为最小控制单元。在每一个弹性单元中,通过标签(Label)选择了一组节点,并为这些节点配置了可部署的最大和最小副本数。ElasticWorkload为每一个弹性单元创建一个独立的弹性工作负载:该工作负载与原始负载共享相同的类型和规格(Spec),并根据弹性单元的配置添加了额外的调度策略以确保其下属的Pod都能调度到合适的节点上。以下是一个ElasticWorkload资源的简单定义示例:
apiVersion: autoscaling.alibabacloud.com/v1beta1 kind: ElasticWorkload metadata: name: elasticworkload-sample spec: sourceTarget: name: nginx-deployment-basic kind: Deployment apiVersion: apps/v1 replicas: 6 elasticUnit: - name: ecs # 前五个 Pod 调度到 ECS 上 labels: alibabacloud.com/acs: "false" max: 5 - name: acs # 剩余 Pod 调度到 ACS 虚拟节点上 labels: alibabacloud.com/acs: "true"
ElasticWorkload接管了原始工作负载的副本管理能力,因此副本数需要配置在ElasticWorkload资源中。也就是说,对于应用的扩缩容,不应再调整原始Deployment等工作负载的replicas字段,而是应当调整ElasticWorkload资源的replicas字段。ElasticWorkload能够支持一些典型的弹性负载场景有:
1. 当ECS资源不足时,新增的副本将被调度到支持的虚拟节点(Virtual Kubelet, VK),如ACS实例等。
2. 在混合云环境中,当IDC中的副本宕机时,可以将任务迁移到公有云。
3. 工作负载副本数一定时,优先运行在由普通ECS实例组成的静态资源池中;当副本数量临时增加时,将增量Pod调度到使用Spot实例的资源池中。
为了支持弹性负载场景,ElasticWorkload采用了在各个弹性单元之间实施正向扩容和逆向缩容的控制器策略。当需要进行扩容,即ElasticWorkload的副本数增加时,控制器会优先将新增的副本数分配给elasticUnit列表中靠前且未达到最大容量的单元;当前序单元的副本数达到其最大值后,新增的副本数才会被分配到后续的单元。相反,当需要进行缩容时,控制器会优先从列表中靠后的单元中减少副本数;只有当后续单元的副本数减少至零后,才会开始减少前序单元的副本数。
ElasticWorkload提供的正向扩容和逆向缩容的控制策略有效地满足了弹性业务的基本需求。这些策略确保了一组稳定实例能够处理日常流量,而在业务高峰期启动一组弹性实例以应对额外负载,并在高峰期过后销毁这些弹性实例。因此,ElasticWorkload能够有效地管理资源,确保系统在高峰负载期间的稳定运行,并在负载减轻时合理地释放资源。
2. WorkloadSpread:基于Pod Webhook的弹性策略插件
WorkloadSpread是由OpenKruise社区提供的一种可配置能力,能够根据特定规则将目标工作负载的 Pod 分布到不同类型的节点上,从而赋予原始工作负载多区域部署和弹性部署的能力。
WorkloadSpread支持Job、CloneSet、Deployment、ReplicaSet、StatefulSet等工作负载类型。在形式上,WorkloadSpread与ElasticWorkload类似,通过将资源外挂到基础工作负载上,在避免对原始工作负载的直接修改的前提下增强了工作负载在多种环境中的适应性和灵活性,使其能够更好地应对不同的部署需求和运行条件。下面是一个典型WorkloadSpread示例:
apiVersion: apps.kruise.io/v1alpha1 kind: WorkloadSpread metadata: name: workloadspread-demo spec: targetRef: # WorkloadSpread 同时支持 K8S 原生与 Kruise 工作负载 apiVersion: apps/v1 | apps.kruise.io/v1alpha1 kind: Deployment | CloneSet name: workload-xxx subsets: - name: subset-a # 前三个副本调度到该 Subset 中 maxReplicas: 3 # Pod 的亲和性配置 requiredNodeSelectorTerm: matchExpressions: - key: topology.kubernetes.io/zone operator: In values: - zone-a patch: # 为调度到该 Subset 中的 Pod 额外注入一个自定义 Label metadata: labels: xxx-specific-label: xxx - name: subset-b # 弹性 Subset 部署到 ACS,不配置容量,副本数不设上限 requiredNodeSelectorTerm: matchExpressions: - key: topology.kubernetes.io/zone operator: In values: - acs-cn-hangzhou scheduleStrategy: # 调度策略,Adaptive 模式会将调度失败的 Pod 重新调度到其他 Subset type: Adaptive | Fixed adaptive: rescheduleCriticalSeconds: 30
WorkloadSpread通过Subsets来划分不同的弹性分区,其概念与ElasticWorkload的Elastic Units类似。同样地,WorkloadSpread也具有与ElasticWorkload相同的弹性策略,即按照Subsets顺序正向扩容、逆向缩容。
2.1. 强大的分区能力
WorkloadSpread不仅具有ElasticWorkload的全部能力,并且得益于活跃的社区支持,其分区功能在许多方面都更加强大。
2.1.1. 灵活的调度配置
在Subset层面,WorkloadSpread不仅支持通过Label选定节点,还支持细致地自定义Subset中Pod的污点与容忍度配置。例如,通过requiredNodeSelectorTerm字段可以指定必须满足的节点属性、通过preferredNodeSelectorTerms字段设置优选的节点属性、以及通过tolerations字段配置Pod对节点污点的容忍度。这些高级配置选项使得WorkloadSpread能够更加精准地控制Pod的调度和分布,从而满足各种复杂的部署需求。
在全局层面,WorkloadSpread支持通过scheduleStrategy字段配置两种不同的调度策略:Fixed与Adaptive。Fixed策略确保Pod严格按照预定义的Subsets进行分布,即使因为各种原因调度失败。这对于需要精确控制工作负载分布的场景非常有用。而Adaptive策略则提供了更高的灵活性,当某个Subset无法满足调度需求时,Pod可以自动重新调度到其他有可用容量的Subset上,从而提高系统的弹性和可靠性。
通过强大且高效的调度策略,WorkloadSpread能够在复杂的弹性业务场景中确保Pod在不同的弹性分区之间实现灵活且均衡的分配。
2.1.2. 细致的Pod定制配置方式
在WorkloadSpread的subset配置中,可以通过patch字段对调度到该分区的Pod进行细致的自定义。几乎所有Pod中支持patch操作的字段都可以进行修改。这些字段包括但不限于容器镜像、资源限制、环境变量、卷挂载、启动命令、探针配置和标签等。通过Pod字段的精细化定制,Pod的基本规格(在目标工作负载模板中定义)与环境适配(在subset的patch字段中定义)实现了有效解耦,从而使得工作负载能够灵活地适配各种不同的分区环境。下面是一些常用的自定义示例:
... # patch pod with a topology label: patch: metadata: labels: topology.application.deploy/zone: "zone-a" ...
以上片段展示了如何对Subset中的所有Pod添加或修改一个Label。
... # patch pod container resources: patch: spec: containers: - name: main resources: limit: cpu: "2" memory: 800Mi ...
以上片段展示了如何修改一个Subset中Pod的资源。
... # patch pod container env with a zone name: patch: spec: containers: - name: main env: - name: K8S_AZ_NAME value: zone-a ...
以上片段展示了如何修改一个Subset中Pod的环境变量。
2.2. WorkloadSpread的Pod Webhook工作原理
WorkloadSpread通过Pod Webhook直接作用于目标工作负载所创建的Pod而非工作负载本身,因此扩缩容操作依然通过操作目标工作负载的replicas进行。相比之下,ElasticWorkload对原有业务的侵入性较大,而WorkloadSpread则因为完全不操作目标工作负载、所有功能都由单独的控制器与Webhook直接作用于Pod,因而具有更高的内聚性。
当符合条件的Pod创建时,WorkloadSpread的Pod Webhook 会将其拦截,并读取相应的WorkloadSpread配置。Webhook 会按照扩容顺序,从配置中根据容量情况选择一个合适的 subset分配给该Pod,并依据subset中定义的调度信息和自定义信息修改Pod的配置。同时,控制器还会维护所有相关Pod的 controller.kubernetes.io/pod-deletion-cost标签,从而确保缩容顺序的正确性。
2.3. WorkloadSpread存在的不足方面
WorkloadSpread在设计上涉及到多个不耦合的组件,并且执行过程较为松散,因此其存在一些不足之处。
2.3.1. Webhook的潜在风险
WorkloadSpread依赖Pod Webhook来实现其功能,因此它会拦截集群中所有Pod的创建请求。当运行Webhook的Pod(即 kruise-manager)性能不足或发生故障时,可能会导致整个集群无法创建新的Pod。此外,当集群在短时间内需要进行大量的扩缩容操作时,Webhook也可能会成为性能瓶颈。
2.3.2. 作用于Pod的局限性
尽管通过Webhook直接作用于Pod可以减少对用户业务的侵入,但这种方法也引入了一些局限性。例如,对于CloneSet而言,它只能通过partition字段来控制整个集群内灰度升级的比例,而无法精细地控制每个subset中的灰度比例。同样地,WorkloadSpread的设计注定了其无法将工作负载级别的能力应用到每个subset中。
客户案例1:大规模压测场景下进行带宽包分配
该案例中,客户需要在某购物节大促前对线上系统进行压测。为此,客户开发了一个load-agent程序用于产生请求,并通过一个CloneSet管理agent的副本数来控制压测流量大小。在对业务场景进行分析后,客户判断压测需要3000个agent副本,并且需要配置每300个副本共享200M的带宽。为此,客户购买了10个200M的共享带宽包实例,期望将其动态地分配给弹性伸缩的agent副本。
考虑到模拟请求的CloneSet是通过客户的压测系统发布与动态伸缩的,不适合重建,因而客户选择给CloneSet外挂WorklaodSpread来实现带宽包的分配。具体地,客户在压测集群中创建了一个指向上述CloneSet的WorkloadSpread,它包含11个Subset:前10个Subset的容量为300,并通过patch修改了Pod的Annotations用于绑定具体的带宽包;最后一个 Subset没有容量,不指定带宽包,用于阻止万一创建超过3000个副本后额外的带宽分配。
apiVersion: apps.kruise.io/v1alpha1 kind: WorkloadSpread metadata: name: bandwidth-spread namespace: loadtest spec: targetRef: apiVersion: apps.kruise.io/v1alpha1 kind: CloneSet name: load-agent-XXXXX subsets: - name: bandwidthPackage-1 maxReplicas: 300 patch: metadata: annotations: k8s.aliyun.com/eip-common-bandwidth-package-id: <id1> - ... - name: bandwidthPackage-10 maxReplicas: 300 patch: metadata: annotations: k8s.aliyun.com/eip-common-bandwidth-package-id: <id10> - name: no-eip
客户案例2:为自建K8S集群中的服务弹性扩容到云上实例提供兼容性
该案例中,客户有一个运行于私有云的服务。由于业务发展需要扩容更多的算力,但是暂时无法扩建线下机房,因此选择使用Virtual Kubelet能力接入ACS弹性算力。客户的应用使用了一些加速服务(如fluid),这些服务组件在私有云中通过DaemonSet等方式预先部署在了节点上提供服务;但是在云上没有部署基础服务,因而需要为Pod额外注入一个sidecar来提供加速能力。
客户诉求在现有业务扩容上云的过程中,不能变动原有工作负载(Deployment)的8个副本,仅对云上Pod的配置做修订。该诉求适合通过结合OpenKruise的SidecarSet能力与WorkloadSpread能力来实现。客户先在集群中部署了一个OpenKruise的SidecarSet,规则为对带有标签needs-acceleration-sidecar=true的Pod注入一个加速组件Sidecar,之后通过WorkloadSpread给扩容到ACS的Pod打上这个标签。相关示例如下:
apiVersion: apps.kruise.io/v1alpha1 kind: WorkloadSpread metadata: name: data-processor-spread spec: targetRef: apiVersion: apps/v1 kind: Deployment name: data-processor subsets: - name: local maxReplicas: 8 patch: metadata: labels: needs-acceleration-sidecar: "false" - name: aliyun-acs patch: metadata: labels: needs-acceleration-sidecar: "true"
3. UnitedDeployment:原生具备Serverless弹性能力的工作负载
UnitedDeployment是OpenKruise社区提供的一种原生支持分区管理的轻量化高级工作负载。与ElasticWorkload和WorkloadSpread对基础工作负载进行增强的设计理念不同,UnitedDeployment提供了一种全新的模式来管理分区弹性应用。UnitedDeployment资源通过一个模板来定义应用,控制器则通过创建并管理多个次级工作负载来匹配不同的分区。UnitedDeployment最显著的特征在于,它作为一个一体化的弹性负载,在单个资源中同时完成应用定义、分区定义、容量管理、扩缩容和应用升级等应用全生命周期管理。以下是一个典型的UnitedDeployment资源示例:
apiVersion: apps.kruise.io/v1alpha1 kind: UnitedDeployment metadata: name: sample-ud spec: replicas: 6 selector: matchLabels: app: sample template: # UnitedDeployment 会通过此模板为每个分区创建一个 CloneSet cloneSetTemplate: metadata: labels: app: sample spec: # CloneSet Spec ... topology: subsets: - name: ecs # 该分区的副本调度到具有标签 ecs 的托管节点池上 nodeSelectorTerm: matchExpressions: - key: node operator: In values: - ecs # 该分区最多有两个副本 maxReplicas: 2 - name: acs-serverless # acs 弹性分区副本数无限 nodeSelectorTerm: matchExpressions: - key: node operator: In values: - acs-virtual-kubelet
3.1. UnitedDeployment的优势
3.1.1 一站式弹性应用管理
UnitedDeployment的一站式应用管理体现在只需要使用一个资源就可以完成应用的定义、分区、扩缩容、升级等操作。
UnitedDeployment控制器会根据工作负载模板,为每个分区管理一个对应类型的次级工作负载,这些次级工作负载无需用户额外关注。在上图中,用户只需要管理蓝色的应用与分区,UnitedDeployment 控制器则会根据全局配置完成后续对各个次级工作负载的管理工作,包括创建、修改、删除等。除了直接对次级工作负载的管理,在必要时,控制器还会监控这些工作负载创建的Pod状态以进行相应的调整。由于所有相关的资源全都由控制器直接管理,资源的操作的发生时机是可控的。因此,UnitedDeployment控制器总能获取相关资源的正确信息,并在正确的时机对其进行操作,而不会发生一致性问题。
用户在UnitedDeployment资源中配置的副本数将会被控制器适当地分配到各个次级工作负载上,由工作负载控制器实施具体的扩缩容操作。因此,使用UnitedDeployment的扩缩容与直接使用对应的工作负载扩缩容产生完全相同的效果,用户不需要额外学习。
得益于对次级工作负载的直接管理,UnitedDeployment具备了对应用进行更新和升级的能力。当工作负载模板中的配置发生任何变更时,这些变更将被同步到相应的次级工作负载中,并由工作负载控制器执行具体的升级逻辑。这意味着,例如CloneSet 的原地升级等特性可以在各个分区内正常生效。此外,如果分区内的次级工作负载支持灰度升级特性,UnitedDeployment还能够统一它们的partition字段值,从而实现各分区之间更细致的灰度控制。
3.1.2 细致的分区配置
UnitedDeployment内置了两种容量分配算法,让用户可以通过细致的分区容量配置来应对弹性应用的各种场景。
弹性分配算法实现了与ElasticWorkload、WorkloadSpread类似的经典弹性容量分配方式:通过为每个分区设定容量的上下限,让Pod按照分区的定义顺序正序扩容、逆序缩容。这种方法上文已经进行了充分介绍,此处不再赘述。
指定分配算法则是一种新的容量分配方式。这种方式通过固定数值或百分比直接为部分分区指定容量,并预留至少一个弹性分区用于平摊剩余的副本。指定分配算法可以适配一些传统弹性分配算法无法应对的场景。比如:固定容量适合管控节点、入口网关等组件;百分比容量则适合将核心副本分散到不同地域节点,并预留一些突发弹性资源。
除了容量分配,UnitedDeployment还允许为每个分区分别配置 Pod的任何Spec字段(包括容器镜像,Image),这为 UnitedDeployment的分区配置赋予了更为强大的灵活性:理论上甚至可以使用一个UnitedDeployment管理部署一整套微服务应用或是大模型推理管线。
3.1.3. 自适应弹性能力
UnitedDeployment具备强大的自适应弹性能力,能够自动地完成扩缩容和重调度等操作,而无需用户过多关注,降低运维成本。
UnitedDeployment支持Kubernetes的水平自动扩展(HPA),能够根据预先配置的条件自动地进行扩缩容操作——这些操作依然严格遵循分区配置。通过HPA,应用在某个分区资源即将耗尽时,能够自动向其他分区扩容,实现自动伸缩,从而提高资源利用率。
UnitedDeployment还具有自适应Pod重调度能力。当控制器发现某个分区中存在一些由于各种原因调度失败而长时间处于 Pending状态的Pod时,会将其重新调度到其他分区,以确保可用Pod副本的数量,并且依旧遵循分区配置的容量分配策略。在扩容过程中,控制器会避免将Pod分配到不可调度的分区中,即使该分区还有剩余容量。用户能够配置调度失败的超时时间以及分区从不可用状态中恢复的时间,从而更好地对自适应调度能力进行控制。
得益于UnitedDeployment强大的自适应能力,用户在使用该组件时可以仅进行分区划分,而不必详细规划每个分区的容量。控制器将自动在不同的分区间分配副本数量,无需用户干预。
3.2. UnitedDeployment的三级负载模式
UnitedDeployment包含了三个级别的工作负载。UnitedDeployment自身作为一级负载,包含负载模板、分区配置以及副本数量。控制器会为每个分区(Subset)创建并管理一个次级工作负载。
这些次级工作负载是UnitedDeployment中负载模板应用于对应分区配置(如调度策略、计算后的副本数、Pod自定义等)后生成的具体实例。这些次级工作负载实例的控制器(如Deployment、CloneSet、StatefulSet等)将会管理作为三级负载的Pod。
三级工作负载的模式使得UnitedDeployment不直接管理Pod,而是能够复用次级工作负载的各种能力。未来,随着 UnitedDeployment支持更多次级负载类型,其功能也将得到进一步扩展,从而支持更多、更复杂的弹性场景。
3.3. UnitedDeployment存在的不足之处
UnitedDeployment 的许多优势均源自其作为一个独立工作负载所具备的一站式管理能力,但这也导致了其具有业务改造侵入性较高的不足。对于用户的现有业务,需要对上层平台(运维系统、发布系统等)进行改造,以便从现有的Deployment、CloneSet 等工作负载切换到 UnitedDeployment。
客户案例1:ACK集群中Pod弹性扩展到ACS实例,并针对Serverless容器做适配
该案例中,客户准备上线一个新的业务,该业务预计具有很明显的峰谷:在高峰期与低谷期流量有数十倍的差距。为了应对这种业务特性,客户选择采购了一批ECS组成ACK集群用于承载基础流量,并在业务高峰期到来时快速地将新的副本弹性扩容到ACS中。同时,客户的应用程序具有一定的特殊性,在Serverless环境需要做一些额外的配置才能运行。
客户对这个新业务的工作负载的诉求是,保证优先用完ECS资源的前提下,通过HPA自动地弹性伸缩,同时针对不同的环境注入不同的环境变量。对于这种比较复杂的弹性应用场景,UnitedDeployment比较符合需求。其中一个配置的实例如下:
apiVersion: apps.kruise.io/v1alpha1 kind: UnitedDeployment metadata: name: elastic-app spec: # 省略业务的工作负载模板 ... topology: # 启动 Adaptive 调度,自适应地将 Pod 副本调度到 ECS 节点池与 ACS 实例 scheduleStrategy: type: Adaptive adaptive: # ECS 节点调度失败 10 秒后开始调度到 ACS Serverless 实例 rescheduleCriticalSeconds: 10 # 上述调度失败后 1 小时内不再调度到 ECS 节点 unschedulableLastSeconds: 3600 subsets: # 优先调度 ECS 并不设上限,直到调度失败才将 Pod 调度到 ACS # 缩容时,先删除 ACS 实例,之后再删除 ECS 节点池中的 Pod - name: ecs nodeSelectorTerm: matchExpressions: - key: type operator: NotIn values: - acs-virtual-kubelet - name: acs-serverless nodeSelectorTerm: matchExpressions: - key: type operator: In values: - acs-virtual-kubelet # 通过 patch 修改调度到弹性算力的 Pod 环境变量,启用应用的 Serverless 模式 patch: spec: containers: - name: main env: - name: APP_RUNTIME_MODE value: SERVERLESS --- # 结合 HPA 自动扩缩容 apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: elastic-app-hpa spec: minReplicas: 1 maxReplicas: 100 metrics: - resource: name: cpu targetAverageUtilization: 2 type: Resource scaleTargetRef: apiVersion: apps.kruise.io/v1alpha1 kind: UnitedDeployment name: elastic-app
客户案例2:为调度到不同架构ECS上的Pod分配不同的资源
该案例中,客户分别采购了Intel 、AMD与ARM平台CPU的若干ECS实例,准备上线一个新的服务。用户希望调度到不同平台的Pod实例能表现出相近的性能,承载相同的QPS。经过压测,发现以Intel CPU为基准,要承载同样的压力,AMD平台需要更多的CPU核数,而ARM平台需要更多的内存。同时,客户采购的三种服务器中,按照算力单位,Intel平台占约一半,AMD与ARM各占约四分之一。
客户诉求这个新服务使用的Workload能够将Pod按照比例均摊到不同的算力上,并且针对不同的算力平台配置不同的资源量,以向下游调用方提供较为稳定的性能。使用UnitedDeployment 的Pod自定义能力配合指定容量分配算法,可以基本满足需求。其中一个配置的实例如下:
apiVersion: apps.kruise.io/v1alpha1 kind: UnitedDeployment metadata: name: my-app spec: replicas: 4 selector: matchLabels: app: my-app template: deploymentTemplate: ... # 省略业务工作负载模板 topology: # intel、amd、倚天710ARM机型分别承载50%、25%、25%的副本 subsets: - name: intel replicas: 50% nodeSelectorTerm: ... # 通过 label 选中 Intel 节点池 patch: spec: containers: - name: main resources: limits: cpu: 2000m memory: 4000Mi - name: amd64 replicas: 25% nodeSelectorTerm: ... # 通过 label 选中 AMD 节点池 # AMD 平台分配更多 CPU patch: spec: containers: - name: main resources: limits: cpu: 3000m memory: 4000Mi - name: yitian-arm replicas: 25% nodeSelectorTerm: ... # 通过 label 选中 ARM 节点池 # ARM 平台分配更多内存 patch: spec: containers: - name: main resources: limits: cpu: 2000m memory: 6000Mi
4. ResourcePolicy:由调度器原生支持的弹性调度策略
资源策略(ResourcePolicy)是阿里云ACK调度器提供的一种原生弹性调度策略,它可以设置应用实例Pod被调度到不同类型节点资源的顺序,并在缩容过程中按照原调度顺序逆序缩容。以下是一个ResourcePolicy资源的配置示例:
apiVersion: scheduling.alibabacloud.com/v1alpha1 kind: ResourcePolicy metadata: name: test namespace: default spec: # 将存量 Pod 纳入管理 ignorePreviousPod: false # 忽略关闭中的 Pod ignoreTerminatingPod: true # 根据这些 Key 分组,组内单独计算副本数 matchLabelKeys: - pod-template-hash # 最后一个 Unit 都调度失败时,才进行抢占 preemptPolicy: AfterAllUnits # 纳入管理的 Pod 条件 selector: key1: value1 strategy: prefer # 两个 units 分别对应 ECS 节点池与 ECI 弹性算力 units: - nodeSelector: unit: first resource: ecs - resource: eci # 某个 unit 调度失败一分钟后,再尝试下一个 whenTryNextUnits: policy: TimeoutOrExceedMax timeout: 1m
4.1. ACK的原生调度器能力
在弹性场景中,无论是使用WorkloadSpread还是 UnitedDeployment,都需要引入外部组件。虽然这些组件功能丰富,但如果仅仅为了满足控制Pod调度,它们显得过于复杂,并且显得不够“专业”。作为ACK调度器的原生能力,ResourcePolicy可以在无需进行集群改造的情况下,精细化地自定义Pod调度策略。
在ResourcePolicy中,通过Label selector来指定一批Pod,并通过Units来定义用户自定义的调度单元。扩容时,将按照Units下资源的顺序进行扩容;而在缩容时,则按照逆序进行缩容。当前,弹性调度策略支持三种资源类型:ECI、ECS以及Elastic(如ACS)。
4.2. 丰富的调度策略
ResourcePolicy管理同一命名空间下label符合Selector的所有 Pod,将其按照一定的优先级部署到定义的多个Unit 中,顺序扩容、逆序缩容。在Unit中,用户可以进行丰富的自定义操作。除了基本的通过Max指定Unit容量以及通过Node selector指定 Unit对应的ECS节点外,还支持更丰富的调度策略。
4.2.1 单元抢占
当ResourcePolicy中存在多个Unit时,可以通过Preempt policy指定是否允许ResourcePolicy在每个Unit调度失败时尝试抢占。BeforeNextUnit表示调度器将在每个Unit调度失败时尝试抢占,而AfterAllUnits表示ResourcePolicy只在最后一个Unit调度失败时才尝试抢占。相比于WorkloadSpread和UnitedDeployment的Adaptive调度策略的超时重调度操作,ResourcePolicy的单元抢占能够提供更细致的控制选项和更及时的调整。
4.2.2. Pod计数机制
WorkloadSpread与ElasticWorkload对于其创建前就已存在的Pod,采用默认的忽略逻辑,之后创建的Pod全量纳入管理,用户无法进行控制。在这一方面,ResourcePolicy具有更精细的可控性。
当各个Unit中配置了Max指定容量后,可以通过配置 IgnorePreviousPod为True,以在进行Pod数量统计时,忽略 ResourcePolicy创建之前已经调度的Pod;或者配置 IgnoreTerminatingPod为True,以在数量统计时忽略处于 Terminating状态的副本。
除此之外,还可以在ResourcePolicy中配置一个MatchLabelKeys列表,来指定一组Label key。调度器会根据Pod上对应Label的值的组合将其进行分组,不同分组将分别适用于单独的Max容量。
4.2.3. 细致的自适应单元调度
一般情况下,ResourcePolicy会按照Units中指定的单元顺序,为Pod分配一个靠前的单元。当一个Pod在调度前,如果调度器发现其对应的单元资源不足可能调度失败,ResourcePolicy可以将其直接调度到下一个单元,从而在一定程度上避免调度失败导致的Pending Pod产生。用户可以通过配置 WhenTryNextUnits 来精细地控制这一行为。
目前ResourcePolicy支持ExceedMax、LackResourceAndNoTerminating、TimeoutOrExceedMax、LackResourceOrExceedMax四种自适应单元调度策略:
• ExceedMax:如果当前Unit的Max未设置,或当前Unit中的Pod数量已经达到或超过设置的Max值,则允许Pod使用下一级资源。
• TimeoutOrExceedMax:如果当前Unit的Max已设置且Unit中的Pod数量小于设置的Max值,或当前Unit的Max未设置且当前 Unit的类型为elastic时:如果当前Unit的资源不足以调度Pod,将在该Unit中等待,等待时间的最长时长为timeout,之后调度到下一个Unit。
• LackResourceOrExceedMax:如果当前Unit中的Pod数量已经达到或超过设置的Max值,或当前Unit中已没有多余资源,则允许Pod使用下一级资源。该策略为默认策略,适用于大多数场景的基本需求。
• LackResourceAndNoTerminating:如果当前Unit中的Pod数量已经达到或超过设置的Max值,或当前Unit中已没有多余资源并且当前Unit中没有处于Terminating状态的Pod时,允许Pod使用下一级资源。该策略适合与滚动更新策略配合使用,以确保在滚动更新时不会因为Pod处于Terminating状态而导致新Pod被调度到后续Unit上。
相比于WorkloadSpread与UnitedDeployment发现Pending后将其重调度的做法,ResourcePolicy得益于直接由调度器实现的优势,能够提供强大的多的自适应调度策略。
4.2.4. ACK原生能力集成
ResourcePolicy作为ACK调度器的能力,能够集成更多ACK的原生特性。比如节点池,作为ACK的概念,不存在于原生K8S中。ResourcePolicy能够直接按照节点池做弹性优先级调度,而其他通用组件则需要额外地去管理不同节点池中的节点标来实现类似的功能。相比通用组件,在涉及ACK原生能力时,ResourcePolicy具有更强的直观性与便利性。
4.3. ResourcePolicy的不足之处
ResourcePolicy是由ACK调度器提供的能力,因而在其他K8S 集群中无法使用。同时,由于其专注于调度,也不具备 WorkloadSpread与UnitedDeployment提供的丰富的分区Pod自定义能力。
客户案例:使用ACS承载突发流量
在这个案例中,客户的业务流量相对稳定,在ACK集群中创建了四个4C8G的Pod就可以满足日常负载。但是,偶发地(大概一年一两次),当一些相关的热点事件发生时,该业务会突发地承载大量流量,此时服务很可能会不堪重负而发生宕机,增加了一些运维成本。因此,客户希望为这个业务添加一定的弹性能力,使用ACS弹性算力偶尔承载突发流量:当流量来临时,临时扩容到弹性算力,流量结束后,删除弹性实例。
由于突发流量产生的频次较少,额外安装OpenKruise组件引入的额外运维成本与算力开支相比非常低频的弹性需求显得过于笨重,且大部分的功能并没有派上用场。此时,比较适合直接使用 ResourcePolicy,保证ECS上的四个副本不变,后续副本扩容到ACS上,并在缩容时优先删除ACS Pod。其中的一个配置示例如下所示:
apiVersion: scheduling.alibabacloud.com/v1alpha1 kind: ResourcePolicy metadata: name: news-rp namespace: default spec: selector: app: newx-xxx strategy: prefer units: - resource: ecs max: 4 # 第五个及以后的副本调度到 eci 上,缩容时优先删除 eci pod nodeSelector: paidtype: subscription - resource: elastic
以上示例能够无侵入地在将临时产生的低频副本扩容到ACS实例上,而不用付出额外的资源与运维成本。
总结
弹性算力能够极大地降低业务的成本,并且有效地提高服务的性能上限。要用好弹性算力,需要根据具体业务特点,选择合适的弹性技术。下表是本文介绍的四种组件的能力对比总结,希望能够给您提供一定的参考。
相关链接:
[1] 如何安装以及使用弹性负载
[2] WorkloadSpread用户文档
https://openkruise.io/zh/docs/user-manuals/workloadspread/
[3] UnitedDeployment用户文档
https://openkruise.io/zh/docs/user-manuals/uniteddeployment/
[4] 自定义弹性资源优先级调度
我们是阿里巴巴云计算和大数据技术幕后的核心技术输出者。
获取关于我们的更多信息~