阿里云容器服务的自动伸缩能力是通过节点自动伸缩组件实现的,可以按需弹出普通实例、GPU实例、竞价付费实例,支持多可用区、多实例规格、多种伸缩模式,满足不同的节点伸缩场景。

工作原理

在Kubernetes中,节点自动伸缩的工作原理与传统意义上基于使用率阈值的模型有所差别,这个也是很多开发者在从传统的IDC或者其他编排系统例如Swarm等迁移到Kubernetes后最难理解的地方。

传统的弹性伸缩模型是基于使用率的,例如:一个集群中有3个节点,当集群中的节点CPU、内存使用率超过特定的阈值时,此时弹出新的节点。但当深入思考时会发现以下几个问题:

在一个集群中,部分热点节点的利用率会较高,而另外一个节点的利用率会很低。如果选择平均利用率的话可能会造成弹性伸缩的不及时。如果使用最高的节点的利用率,那么也会造成弹出资源的浪费。

在Kubernetes中,应用是以Pod为最小单元,部署在集群的不同节点上的。当一个Pod资源利用率较高的时候,即便此时所在的节点或者集群的总量触发了弹性扩容,但是这个应用的Pod数目,以及Pod对应的Limit没有任何变换,那么负载的压力是无法转移到新扩容出的节点上的。

如果基于资源利用率的方式判断节点是否缩容,那么很有可能出现,Request很大,但是Usage很小的Pod被驱逐,一旦集群中这种类型的Pod较多,会导致集群的调度资源被占满,部分Pod无法调度。

Kubernetes节点伸缩是怎么解决这个问题的呢?Kubernetes是通过调度与资源解耦的两层弹性模型来解决的。

简单的理解,可以基于资源的使用率来触发应用副本的变化,也就是调度单元的变化。而当集群的调度水位达到100%的时候会触发资源层的弹性扩容,当资源弹出后,无法调度的单元会自动调度到新弹出的节点上,从而降低整个应用的负载状况。以下介绍Kubernetes弹性伸缩的技术细节:

cluster-autoscaler是通过对处在Pending的Pod进行监听而触发的。当Pod处在Pending的原因是调度资源不足的时候,会触发cluster-autoscaler的模拟调度,模拟调度器会计算在配置的伸缩组中哪个伸缩组弹出节点后可以调度这些Pending的Pod。如果有伸缩组可以满足,那么就弹出相应的节点。

简单的理解,模拟调度就是将一个伸缩组当成一个抽象的Node,伸缩组中配置的机型规格对应会成为Node的CPU/内存/GPU的容量,然后设置伸缩组上面的Label、Taint,也就是Node的Label与Taint。模拟调度器会在调度模拟的时候,将这个抽象的Node纳入调度参考。如果Pending的Pod可以调度到抽象的Node,那么就会计算所需的Node的数目,驱动伸缩组弹出节点。

首先只有弹性伸缩弹出的节点会被缩容,静态的节点是无法被cluster-autoscaler接管的。缩容的判断是通过每个节点单独判断的。当任意一个节点的调度利用率低于所设置的调度阈值时,会触发节点的缩容判断。此时cluster-autoscaler会尝试模拟驱逐节点上面的负载,判断当前节点是否可以排水彻底。如果有些特殊的Pod(kube-system命名空间的非DaemonSet Pod、PDB控制的Pod等),则会跳过这个节点而选择其他的候选节点。当节点发生驱逐时,会先进行排水,将节点上的Pod驱逐到其他的节点,然后再下线这个节点。

不同分组之间,实际上相当于不同的虚拟的Node之间的选择,和调度策略一样,这里也存在一个打分的机制。首先符合调度策略的Node会先过滤出来,在符合调度策略的Node中,会根据affinity等亲和性的策略进行选择。如果上述的策略都不存在,默认情况下cluster-autoscaler会通过least-waste的策略来进行抉择。least-waste的策略的核心就是模拟弹出节点后,剩余的资源最少。此外,有一个特别的场景,当有一个GPU的伸缩组和CPU的伸缩组同时可以弹出生效时,默认CPU会优先与GPU弹出。

弹性伸缩的成功率主要取决如下两个因素:

  • 调度策略是否满足

    首先在配置好伸缩组后,开发者需要先确认下这个伸缩组可以承载的Pod的调度策略范围。如果无法直接判断,最简单的方式是通过nodeSelector直接选择伸缩组的Label进行预弹模拟。

  • 资源配置是否充分

    当模拟调度通过后,会选择伸缩组进行弹出,但是伸缩组中配置的ECS规格是否有库存会直接决定是否可以成功弹出实例。因此配置多个可用区、多个不同机型组合可以大大提高弹出成功率。

  • 方法一:可以通过极速模式加速弹出速度,当伸缩组预热后(已完成一次扩容和缩容),伸缩组即可进入极速伸缩模式。
  • 方法二:使用自定义镜像的方式,通过Alibaba Cloud Linux 2(原Aliyun Linux 2)作为基础镜像,可以大大提升IaaS层的资源交付速度(50%)。

注意事项

  • 默认单个用户每个区域按量付费实例的配额是50 vCPU,在专有网络下创建的单个路由表可创建的自定义路由数限额是48条。如需更大的配额,请提交工单申请。
  • 单一规格的ECS库存容量波动较大,建议在伸缩组中配置多种同规格的实例类型,提高节点伸缩成功率。
  • 极速弹出模式在节点进入停机回收状态时,节点将进行停机,并处在NotReady状态,当再次伸缩弹出时,节点状态会变为Ready
  • 极速弹出模式的节点处在停机回收状态时,只收取磁盘的费用,不收取计算费用(不包含拥有本地盘的机型系列,例如,ecs.d1ne.2xlarge),在库存充裕的前提下可以极速启动。
  • 选择绑定EIP时,通过ECS控制台直接删除自动伸缩组或者扩容出的ECS节点会导致弹性公网EIP(Elastic IP Address)无法自动释放。

步骤一:执行自动伸缩

  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,单击集群
  3. 在目标集群右侧,选择更多 > 自动伸缩
  4. 进入自动弹性伸缩配置页面。
    • 选择所需的集群并单击操作列的更多 > 自动伸缩
      1. 选择所需集群然后单击操作列的详情
      2. 在集群应用配置左侧导航栏单击节点池
      3. 在节点池页面右上方,单击自动弹性伸缩配置

步骤二:授权

  1. 开通ESS服务。
    1. 单击弹出对话框中的第一个链接,进入弹性伸缩服务ESS页面。
    2. 单击开通ESS服务,进入云产品开通页
    3. 选中我已阅读并同意复选框,单击立即开通
    4. 开通成功后,在开通完成页签,单击管理控制台,进入弹性伸缩服务ESS页面。
    5. 单击前往授权,进入云资源访问授权页面,配置对云资源的访问权限。
    6. 单击同意授权
    页面自动跳转至弹性伸缩控制台,说明授权成功。关闭页面,继续配置授权角色
  2. 授权角色。
    1. 单击弹出对话框中的第二个链接,进入RAM角色管理详情页面。
      说明 此处需要以主账号登录控制台。
    2. 权限管理页签,单击目标授权策略名称,进入授权策略详情页面。
    3. 单击修改策略内容,从右侧滑出侧边栏修改策略内容页面。
    4. 策略内容Action字段中补充以下策略。
      "ess:Describe*", 
      "ess:CreateScalingRule", 
      "ess:ModifyScalingGroup", 
      "ess:RemoveInstances", 
      "ess:ExecuteScalingRule", 
      "ess:ModifyScalingRule", 
      "ess:DeleteScalingRule", 
      "ecs:DescribeInstanceTypes",
      "ess:DetachInstances",
      "vpc:DescribeVSwitches"
      说明 在策略内容的任意一个Action字段的最后一行补充英文逗号()。

      如果自动伸缩组需要绑定EIP,您还需补充以下策略。

      "ecs:AllocateEipAddress",
      "ecs:AssociateEipAddress",
      "ecs:DescribeEipAddresses",
      "ecs:DescribeInstanceTypes",
      "ecs:DescribeInvocationResults",
      "ecs:DescribeInvocations",
      "ecs:ReleaseEipAddress",
      "ecs:RunCommand",
      "ecs:UnassociateEipAddress",
      "ess:CompleteLifecycleAction",
      "ess:CreateScalingRule",
      "ess:DeleteScalingRule",
      "ess:Describe*",
      "ess:DetachInstances",
      "ess:ExecuteScalingRule",
      "ess:ModifyScalingGroup",
      "ess:ModifyScalingRule",
      "ess:RemoveInstances",
      "vpc:AllocateEipAddress",
      "vpc:AssociateEipAddress",
      "vpc:DescribeEipAddresses",
      "vpc:DescribeVSwitches",
      "vpc:ReleaseEipAddress",
      "vpc:UnassociateEipAddress",
      "vpc:TagResources"
      然后,在RAM角色管理详情页,单击信任策略管理,在修改信任策略中添加oos.aliyuncs.com,如下图所示。oos
    5. 单击确认

步骤三:配置自动伸缩

  1. 自动伸缩页面,填写以下信息,并单击提交
    配置 说明
    集群 目标集群名称。
    缩容阈值 cluster-autoscaler管理的伸缩组中,每一个节点的资源申请值(Request)/ 每一个节点的资源容量。当低于配置的阈值时,节点会进行缩容。
    说明 弹性伸缩中,扩容会基于调度自动触发,只需设置缩容条件即可。
    缩容触发时延 集群满足配置的缩容阈值时,在配置的缩容触发时延到达后,集群开始缩容。单位:分钟。默认情况下是10分钟。
    静默时间 在集群删除节点后,在静默时间内,集群不会再次触发缩容,单位 :分钟。默认情况下是10分钟。
  2. 根据所需要弹性伸缩的资源类型(普通/GPU/抢占式),单击操作列创建
  3. 伸缩组配置填写以下信息。
    配置 说明
    地域 所创建伸缩组将要部署到的地域。与伸缩组所在集群的地域一致,不可变更。
    专有网络 所创建伸缩组的网络,与伸缩组所在集群的网络一致。
    虚拟交换机 所创建伸缩组的虚拟交换机,包含伸缩组的可用区及Pod地址段。
  4. 配置Worker节点的信息。
    配置 说明
    节点类型 所创建的伸缩组的节点类型,与创建集群时所选择的节点类型一致。
    实例规格 伸缩组内实例的规格。
    已选规格 所选择的伸缩组的实例规格,最多可以选择10种实例规格。
    系统盘 伸缩组的系统盘。
    挂载数据盘 是否在创建伸缩组时挂载数据盘,默认情况下不挂载。
    实例数量 伸缩组所包含的实例数量。
    说明
    • 实例不包含客户已有的实例。
    • 默认情况,实例的最小值是0台,超过0台的时候,集群会默认向伸缩组中添加实例,并将实例加入到伸缩组对应的Kubernetes集群中。
    密钥对 登录伸缩后的节点时所使用的密钥对。可以在ECS控制台新建密钥对。
    说明 目前只支持密钥对方式登录。
    伸缩模式 支持标准模式极速模式
    RDS白名单 弹性伸缩后的节点可以访问的RDS实例。
    节点标签 在集群中添加节点标签(Label)后,会自动添加到弹性伸缩扩容出的节点上。
    污点(Taints) 添加污点后,Kubernetes将不会将Pod调度到这个节点上。
  5. 单击确定,创建伸缩组。

预期结果

  1. 自动伸缩页面,可以在普通实例下看到新创建的一个伸缩组。
    自动伸缩
  2. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  3. 在集群管理页左侧导航栏中,单击工作负载
  4. 无状态页签,选择kube-system命名空间,可以看到名称为cluster-autoscaler的组件,表示伸缩组已创建成功。

常见问题

  • 为什么节点自动伸缩组件无法弹出节点?
    请检查是否存在如下几种场景:
    • 配置伸缩组的实例类型无法满足Pod的资源申请(Request)。默认节点会安装系统组件,Pod的申请资源要小于实例的规格。
    • 是否完整按照步骤执行了授权操作。授权操作是集群维度的,需要每个集群操作一次。
    • 集群是否拥有出网能力。节点自动伸缩组件需要调用阿里云的OpenAPI,因此需要部署节点自动伸缩组件的节点具备出网能力。
  • 为什么节点自动伸缩组件无法缩容节点?
    请检查是否存在如下几种场景:
    • 所有的节点Pod的资源申请(Request)阈值高于设置的缩容阈值。
    • 节点上运行kube-system命名空间的Pod。
    • 节点上的Pod包含强制的调度策略,导致其他节点无法运行此Pod。
    • 节点上的Pod拥有PodDisruptionBudget,且到达了PodDisruptionBudget的最小值。

    您可以在开源社区得到更多关于节点自动伸缩组件的常见问题与解答。

  • 多个伸缩组在弹性伸缩的时候是如何被选择的?

    在Pod处在无法调度时,会触发弹性伸缩组件的模拟调度逻辑,会根据伸缩组配置的标签和污点以及实例规格等信息进行判断。当配置的伸缩组可以模拟调度Pod的时候,就会被选择进行节点弹出。当同时有多个伸缩组满足模拟调度条件的时候,默认采用的是最少浪费原则,即根据模拟弹出后节点上剩余的资源最小为原则进行抉择。