多可用区均衡部署分布式系统实现高可用架构的核心策略之一。业务负载增大时,多可用区均衡调度策略能够自动扩容出多个可用区的实例来满足集群的调度水位。
前提条件
已在待弹出实例的可用区创建至少一个交换机,请参见创建和管理交换机。创建后,您可以在创建和管理节点池时选择对应的vSwitch。
功能介绍
节点自动伸缩组件可通过预调度判断服务能否部署在某伸缩组上,并将扩容请求发送至指定ESS伸缩组完成实例的生成。但当前在单伸缩组配置多可用区vSwitch的机制存在一个问题:当多可用区业务Pod因集群资源不足无法调度时,ACK虽会触发伸缩组扩容,但由于缺乏可用区与实例的关联信息传递机制,ESS弹性伸缩组无法识别具体需要扩容的可用区。这可能导致扩容实例集中在单个可用区,而非多可用区均衡扩展,无法满足业务对跨可用区同时扩容的需求。
为此,ACK引入了ack-autoscaling-placeholder组件,通过少量的资源冗余方式,将多可用区的弹性伸缩转变为并发节点池的定向伸缩,详情请参见基于ack-autoscaling-placeholder实现容器秒级伸缩。原理如下。
为每个可用区创建一个节点池,并分别在各个节点池打上可用区的标签。
通过配置可用区标签nodeSelector的方式,使用ack-autoscaling-placeholder为每个可用区创建占位Pod。默认的占位Pod具有比较低权重的PriorityClass,优先级低于应用Pod。
业务应用Pod Pending后会抢占各个可用区占位Pod,带有可用区nodeSelector的多可用区占位Pod处于Pending后,节点自动伸缩组件感知到的调度策略就从多个可用区的antiAffinity变成了可用区的nodeSelector,从而处理发出扩容区域的节点的请求。
以下以两个可用区为例,介绍如何基于现有架构满足多可用区的同时扩容。
ack-autoscaling-placeholder作为业务应用和节点自动伸缩组件之间的桥梁,为每个可用区创建占位Pod。占位Pod的调度优先级低于实际业务应用的调度优先级。
应用Pod Pending后会迅速抢占占位Pod,并部署在各个可用区的已有节点上,同时,被抢占的占位Pod会处于Pending状态。
占位Pod是带有可用区nodeSelector调度策略的,节点自动伸缩组件可以并发扩容到对应的可用区。
步骤一:为可用区创建节点池并配置自定义节点标签
步骤二:部署Placeholder及占位Deployment
在控制台左侧导航栏,选择 。
搜索ack-autoscaling-placeholder,单击组件卡片,然后单击一键部署。
选择集群和命名空间,然后单击下一步,选择Chart版本,编辑参数,然后单击确定。
创建成功后,在
页面,可查看到该应用状态为已部署。在集群管理页左侧导航栏,选择 。
在Helm页面,单击ack-autoscaling-placeholder-default操作列的更新。
在更新发布面板中,参见下方示例更新YAML,然后单击确定。将每个可用区都设置Placeholder,每个区域需要定义一个占位Deployment。
本示例以在可用区I、K、H创建占位Deployment为例。
deployments: - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m # 调度单元CPU。 memory: 6 # 调度单元内存。 imagePullSecrets: {} labels: {} name: ack-place-holder-I # 占位Deployment名称。 nodeSelector: {"avaliable_zone":i} # 可用区标签(需要与步骤一创建节点池中配置的标签一致)。 replicaCount: 10 # 每次扩容可快速弹出Pod数量。 tolerations: [] - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m # 调度单元CPU。 memory: 6 # 调度单元内存。 imagePullSecrets: {} labels: {} name: ack-place-holder-K # 占位Deployment名称。 nodeSelector: {"avaliable_zone":k} # 可用区标签(需要与步骤一创建节点池中配置的标签一致)。 replicaCount: 10 # 每次扩容可快速弹出Pod数量。 tolerations: [] - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m # 调度单元CPU。 memory: 6 # 调度单元内存。 imagePullSecrets: {} labels: {} name: ack-place-holder-H # 占位Deployment名称。 nodeSelector: {"avaliable_zone":h} # 可用区标签(需要与步骤一创建节点池中配置的标签一致)。 replicaCount: 10 # 每次扩容可快速弹出Pod数量。 tolerations: [] fullnameOverride: "" nameOverride: "" podSecurityContext: {} priorityClassDefault: enabled: true name: default-priority-class value: -1
更新成功后,各个可用区将创建对应的占位Deployment。
步骤三:创建实际负载的PriorityClass
使用以下YAML示例,创建名为priorityClass.yaml的文件。
apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000000 # 配置优先级,比步骤二的工作负载默认优先级高。 globalDefault: false description: "This priority class should be used for XYZ service pods only."
若无需为Pod单独配置PriorityClass,可通过全局PriorityClass配置全局默认设置。配置生效后,未指定PriorityClass的Pod将自动采用此优先级值,抢占能力自动生效。
apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: global-high-priority value: 1 # 配置优先级,比步骤二的工作负载默认优先级高。 globalDefault: true description: "This priority class should be used for XYZ service pods only."
部署工作负载的PriorityClass。
kubectl apply -f priorityClass.yaml
预期输出:
priorityclass.scheduling.k8s.io/high-priority created
步骤四:创建实际工作负载
以可用区I为例。
使用以下YAML示例,创建名为workload.yaml的文件。
apiVersion: apps/v1 kind: Deployment metadata: name: placeholder-test labels: app: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: nodeSelector: # 节点选择。 avaliable_zone: "i" priorityClassName: high-priority # 步骤三配置的PriorityClass名称。如果全局配置开启,则为非必填。 containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 resources: requests: cpu: 3 # 实际负载的资源需求。 memory: 5
部署实际的工作负载。
kubectl apply -f workload.yaml
预期输出:
deployment.apps/placeholder-test created
部署后,在
页面可以发现,由于实际负载的PriorityClass比占位Pod高,抢占的占位Pod会在弹出节点上运行,被抢占的占位Pod会触发节点自动伸缩组件的并发扩容,为下次实际负载扩容做准备。在
页面,可以看到负载Pod运行在之前占位Pod的节点上。