第4章 节点伸缩的实现

阿里云Kubernetes集群的一个重要特性,是集群的节点可以动态地增加或减少。有了这个特性,集群才能在计算资源不足的情况下扩容,增加新的节点,同时也可以在资源利用率降低的时候,释放节点以节省费用。

在这一章,我们讨论阿里云Kubernetes集群节点伸缩的实现原理。理解了实现原理,在遇到问题的时候,我们就可以高效地排查并找出原因。

4.1 节点增加原理

阿里云Kubernetes集群增加节点的方式有三类:添加已有节点集群扩容自动伸缩。其中,添加已有节点又可分为手动添加已有节点自动添加已有节点

节点的增加涉及的组件有:节点准备ESS(弹性伸缩)集群管控Cluster Autoscaler调度器,如图4-1所示。

图4-1 集群节点增加原理

插图展示了以下组件及其协作关系:

  • 节点准备
  • ESS(弹性伸缩)
  • 集群管控
  • Cluster Autoscaler
  • 调度器

不同增加方式的流程均涉及上述组件,但组合方式不同。

4.1.1 手动添加已有节点

节点准备,其实就是把一个普通的ECS实例安装配置成一个Kubernetes集群节点的过程。这个过程仅靠一条命令就可以完成。这条命令使用 curl 下载 attach_node.sh 脚本,然后以 openapi token 为参数,在ECS上运行。

这里 token 是一个 <<key, value>> 对的 key,而 value 是当前集群的基本信息。阿里云Kubernetes集群的管控,在接到手动添加已有节点请求的时候,会生成这个 <<key, value>> 对,并把 key 作为 token 返回给用户。

这个 token(key) 存在的价值,是其可以让 attach_node.sh 脚本匿名在ECS上检索到集群的基本信息(value),而这些基本信息对节点准备至关重要。

总体上来说,节点准备就做两件事情:。读即数据收集,写即节点配置。节点初始化过程如图4-2所示。

图4-2 节点初始化过程

过程示意(推测流程):

flowchart TD
    A[开始]
    A --> B[读取集群基本信息<br>(从token解析value)]
    B --> C[配置节点:<br>安装Docker、kubelet等]
    C --> D[使用kubeadm注册到Master]
    D --> E[节点就绪]

这里的读写过程,绝大部分内容都比较基础,大家可以通过阅读脚本来了解细节。唯一需要特别说明的是Kubeadm把节点注册到Master的过程,此过程需要新加节点和集群Master之间建立互信。

一方面,新加节点从管控处获取的 bootstrap token(与 openapi token 不同,此 tokenvalue 的一部分内容),实际上是管控通过可信的途径从集群Master上获取的。新加节点使用这个 bootstrap token 连接Master,Master则可通过验证这个 bootstrap token 来建立对新加节点的信任。

另一方面,新加节点以匿名身份从Master kube-public 命名空间中获取集群 cluster-infocluster-info 包括集群CA(证书授权中心)证书,和使用集群 bootstrap token 对这个CA做的签名。新加节点使用从管控处获取的 bootstrap token,对CA生成新的签名,然后将此签名与 cluster-info 内签名做对比,如果两个签名一致,则说明 cluster-infobootstrap token 来自同一集群。新加节点因为信任管控,所以建立对Master的信任。集群节点注册机制如图4-3所示。

图4-3 集群节点注册机制

双向信任建立流程:

sequenceDiagram
    participant NewNode as 新加节点
    participant Master as 集群Master
    participant ControlPlane as 管控
    ControlPlane->>NewNode: 下发bootstrap token (来自Master)
    NewNode->>Master: 使用bootstrap token连接
    Master-->>NewNode: 验证bootstrap token,建立信任
    NewNode->>Master: 获取cluster-info (含CA证书+签名)
    Note over NewNode: 用bootstrap token对CA生成新签名
    Note over NewNode: 对比新签名与cluster-info中的签名
    Master-->>NewNode: 签名一致则信任Master

4.1.2 自动添加已有节点

自动添加已有节点,不需要人为把脚本拷贝到ECS命令行来完成节点准备的过程。管控使用了ECS Userdata的特性,把类似以上节点准备的脚本写入ECS Userdata,然后重启ECS并更换系统盘。当ECS重启之后,会自动执行Userdata里边的脚本,来完成节点添加的过程。

这里我们看到,attach_node.sh 的参数与前一节的参数有很大的不同。其实这里的参数都是前一节 value 的内容,即管控创建并维护的集群基本信息。自动添加已有节点省略了通过 key 获取 value 的过程。

4.1.3 集群扩容

集群扩容与以上添加已有节点不同,此功能针对需要新购节点的情形。集群扩容的实现,在添加已有节点的基础上引入了ESS组件。ESS组件负责从无到有的过程,而剩下的过程与添加已有节点类似,即依靠ECS Userdata脚本来完成节点准备。图4-4所示为集群扩容过程。

图4-4 集群扩容过程

流程示意:

flowchart LR
    A[触发扩容请求] --> B[ESS创建新的ECS实例]
    B --> C[ECS启动时执行Userdata脚本]
    C --> D[节点准备过程<br>(同添加已有节点)]
    D --> E[节点加入集群]

4.1.4 自动伸缩

前面几种方式是需要人为干预的伸缩方式,而自动伸缩的本质不同,因为它可以在业务需求量增加的时候,自动创建ECS实例并加入集群。为了实现自动化,这里引入了另外一个组件 Cluster Autoscaler。集群自动伸缩包括两个独立的过程,如图4-5所示。

其中第一个过程主要用来配置节点的规格属性,包括设置节点的用户数据。这个用户数据和手动添加已有节点的脚本类似,不同的地方在于,其针对自动伸缩这种场景增加了一些专门的标记。attach_node.sh 脚本会根据这些标记来设置节点的属性。

而第二个过程是实现自动增加节点的关键。这里引入了一个新的组件 Autoscaler,它以Pod的形式运行在Kubernetes集群中。从理论上来说,我们可以把这个组件当作一个控制器。因为它的作用与控制器类似,基本上还是监听Pod状态,以便在Pod因为节点资源不足而不能被调度时,去修改ESS的伸缩规则来增加新的节点。

图4-5 集群自动伸缩过程

两个独立过程:

过程1:配置节点规格属性

  • 设置用户数据(包含自动伸缩标记)
  • 类似于手动添加节点脚本,但增加特殊标记

过程2:自动增加节点

  • Autoscaler(Pod形式)监听Pod调度状态
  • 当Pod因资源不足无法调度时,修改ESS伸缩规则
  • ESS创建新ECS实例,通过Userdata加入集群

知识点:预订率 vs 使用率

集群调度器衡量资源是否充足的标准是**“预订率”,而不是“使用率”**。这两者的差别,类似酒店房间预订率和实际入住率:完全有可能有人预订了酒店,但是并没有实际入住。

在开启自动伸缩功能的时候,我们需要设置缩容阈值,就是“预订率”的下限。之所以不需要设置扩容阈值,是因为Autoscaler扩容集群依靠的是Pod的调度状态:当Pod因为节点资源“预订率”太高而无法被调度的时候,Autoscaler就会扩容集群。

4.2 节点减少原理

与增加节点不同,集群减少节点的操作只有一个移除节点的入口。但对于用不同方法加入的节点,其移除方式略有不同,如图4-6所示。

  • 通过添加已有节点加入的节点,需要三步去移除:

    1. 管控通过ECS API清除ECS Userdata。
    2. 管控通过Kubernetes API从集群中删除节点。
    3. 管控通过ECS InvokeCommand 在ECS上执行 kubeadm reset 命令清理节点。
  • 通过集群扩容加入的节点,则在前面步骤的基础上增加了断开ESS和ECS关系的操作。此操作由管控调用ESS API完成。

图4-6 集群节点减少原理

不同来源节点移除流程对比:

节点来源步骤
添加已有节点1. 清除Userdata
2. 从集群删除节点
3. 执行kubeadm reset
集群扩容加入上述步骤 + 断开ESS与ECS关系
Cluster Autoscaler动态增加CPU预订率降低时自动移除释放(依赖Metrics)

经过Cluster Autoscaler动态增加的节点,在集群CPU资源“预订率”降低的时候,由Cluster Autoscaler自动移除释放。其触发点是CPU“预订率”,这就是图4-6中加上Metrics的原因。

4.3 节点池原理

针对不同的业务需求,阿里云容器服务实际上已经支持了包括托管版标准专有版异构版弹性裸金属Windows在内的诸多集群类型,如图4-7所示。

图4-7 阿里云容器集群多样性

  • 托管版
  • 标准专有版
  • 异构版
  • 弹性裸金属
  • Windows

面对丰富的集群类型,我们思考一个问题,就是怎样才能将如此多样的能力合并到同一个集群中,用户无须创建多个集群,只需要在一个集群中就具有管理多种集群的能力,如图4-8所示。

图4-8 阿里云容器集群节点池

概念示意:一个Kubernetes集群内部包含多个不同类型的节点池,每个节点池可以承载不同规格/类型的节点。

面对这样的需求,容器服务Kubernetes需要提供更有层次的节点维度的管理功能。为此,我们设计了节点池的概念,利用节点池我们可以对不同节点类型做分组管理。

如此一来,在同一个Kubernetes集群中,我们就可以通过创建不同类型的节点池来满足不同的业务场景。

节点池内节点的伸缩原理和前两节所述的基本一致,这里不再赘述。

4.4 总结

总体上来说,Kubernetes集群节点的增加与减少主要涉及四个组件,分别是 Cluster AutoscalerESS管控节点本身(节点的准备与清理)。

根据场景的不同,我们需要排查不同的组件:

  • Cluster Autoscaler:是一个普通的Pod,其日志的获取和其他Pod无异。
  • ESS:有其专门的控制台,我们可以在控制台排查其伸缩配置、伸缩规则等相关子实例日志和状态。
  • 管控:其日志可以通过查看日志功能来查看。
  • 节点的准备与清理:其实就是排查对应的脚本的执行过程。

本章主要阐述了节点伸缩实现的原理,希望对大家理解节点伸缩和问题排查有所帮助。