后端架构师必知必会系列:服务容器与资源调度

作者:禅与计算机程序设计艺术

1.简介

“云计算”一词被炒得火热,从“虚拟化”、“自动化”到“API 网关”,甚至还有“无服务器”(Serverless),但这只是云计算技术的皮毛,真正让云计算落地的,是“云编排”,即通过编排工具将各种基础设施组件(如数据库、消息队列、缓存等)按照预先规划好的逻辑拼装,组成完整的应用系统。当应用程序越来越复杂,需要调用多个微服务时,传统的基于微服务架构的部署方式已然无法应对。为了提高服务的弹性和可用性,我们需要采用更加灵活的分布式架构,即服务集群。

服务集群是分布式架构下用于容纳多个服务的容器化环境。在 Kubernetes 中,这种环境被称为 pod,它是一个封装了多个 Docker 容器的逻辑单元。每个 pod 都有一个或多个网络地址,并且可以包含多个容器。因此,pod 可以看做是一个逻辑上的服务器,由一个或多个服务容器共同提供服务。

而 Kubernetes 中的容器调度器负责管理 pod 的生命周期,包括启动、停止、复制、扩展等操作。容器调度器根据调度策略指派 pod 到不同的节点上运行,确保每个节点上正在运行的容器数量满足集群需求。同时,Kubernetes 提供了一套丰富的接口和机制,允许用户创建自定义的调度器,定制出最合适自己的调度策略。除此之外,Kubernetes 支持多种类型的存储卷,包括本地存储(如 NFS 和 iSCSI)、网络文件存储(如 AWS EBS、Azure Files)和远程存储(如 Ceph、GlusterFS)。

本文将以 Kubernetes 服务集群为背景,结合容器化、微服务、集群调度等相关技术领域知识,探讨容器化服务集群中的服务调度器。我们将重点阐述以下几个方面:

  1. 服务调度原理
  2. 主流的调度器类型及特点
  3. 常用调度器参数配置
  4. 实践经验总结

2.服务调度原理

什么是调度?

容器调度就是将 Pod 分配给 Kubernetes 集群中可用的 Node 上运行的过程。调度器的主要职责就是决定将哪个 Pod 分配给某个 Node,或者将新的 Pod 从何处调入集群。

其工作原理类似于传统服务器硬件调度器,但不同的是,调度器不仅负责确定物理机上 CPU、内存和存储的使用情况,而且还要考虑应用的性能、QoS 等因素。调度器会分析每个节点上的资源利用率,并计算出每台机器上可运行多少 Pod,然后把这些信息汇报给 API Server,以供 Kubelet 使用。如果某个 Node 的资源利用率过高或某些 Pod 因某种原因无法正常运行,调度器可能会把该 Node 上正在运行的 Pod 迁移到其他节点,以保证集群整体的稳定运行。

为什么要调度?

容器集群中通常存在很多节点,而且这些节点的配置和能力都是不断变化的。为了让 Pod 在集群中平衡分布,避免因单个节点资源不足导致的资源利用率下降、Pod 不可靠运行,调度器就需要做好调度工作。另外,由于应用的动态伸缩性要求,随着业务量的增加,集群也会出现资源短缺的问题。为此,调度器除了需要处理常规的资源调度问题外,还要兼顾调度效率和尽可能的减少风险。例如,对于那些对响应时间敏感的服务,调度器应该能够快速分配资源;而对于那些对可靠性和可靠性的要求较高的服务,调度器则应该确保服务的部署副本分布均匀。

Kubernetes 调度器

Kubernetes 具有高度自动化的特性,其中最重要的一项就是资源调度器。默认情况下,Kubernetes 会根据各类调度算法,选择最合适的 Node 来运行相应的 Pod。但是,也可以通过调整调度参数来改变 Kubernetes 默认的调度行为。下面我们将详细介绍 Kubernetes 调度器的功能、原理及配置。

概念及术语

首先,我们需要明确以下几个概念和术语:

  • Node:表示集群中的一台物理服务器或虚拟机,可以是裸金属服务器或云服务器,具有 CPU、内存、磁盘等硬件设备,可以供多个 Pod 运行。
  • Pod:是 Kubernetes 中的最小调度单位,表示一个或多个 Docker 容器的集合,它的资源消耗由所有容器共享。
  • Label:可以附加到 Kubernetes 对象(Pod、Service、Node等)上,用于标识对象。可以为 Label 指定键值对,键值对之间使用冒号分隔。例如,可以在 Pod 上打上 "app=web" 的标签,表示这个 Pod 属于 web 应用。
  • Selector:是一种语法结构,用来匹配对象的属性。例如,可以使用 "app=web" 的标签作为 selector 查找所有的 web 应用 Pod。
  • Scheduler:是 Kubernetes 系统内置的调度器,负责监控新创建的 Pod,并为它们选择 Node。
  • Binding:是 Kubernetes Scheduler 将 Pod 绑定到 Node 的动作,它会触发 kubelet 向 API Server 报告 Pod 状态变更,并启动相应的容器。

调度流程

Kubernetes 的调度器的调度流程如下图所示:

  • 用户提交的 Pod 通过 API Server 上传到 Master 节点。
  • API Server 检查请求中指定的调度器插件是否存在,如果存在则调度器就会执行相应的调度算法。
  • 如果调度算法找到了一个符合条件的 Node 作为目标,则创建一个名为 Binding 的事件,这个 Binding 操作会通知 Kubernetes 将这个 Pod 绑定到这个 Node 上。
  • Kubelet 接收到 Binding 请求之后,就会通知容器引擎(如 Docker)去启动这个 Pod,并将它所在的 Node 设置为该 Pod 的 Node 属性。
  • 当 Pod 启动成功后,Kubernetes 便可以将它视为正常运行。

调度器策略

Kubernetes 调度器支持三种调度策略:

  • 静态调度器(Default Scheduler):当 Pod 没有指定调度器时,使用 Default Scheduler 进行调度。
  • 混合调度器(Mixed Scheduler):混合调度器结合多种调度算法,并允许用户自定义调度规则。
  • 外部调度器(External Scheduler):通过 Kubernetes 提供的接口,调度器可以连接外部调度系统,完成更多的任务。

可以通过 kube-scheduler 命令行参数 --config 来指定调度器使用的配置文件。调度器的配置选项非常丰富,下面我们就来介绍其中一些关键的选项。

Schedule Once

--schedule-once 参数控制 Kubernetes 是否每次只调度一个符合条件的 Node。这个参数的作用是防止同一批次的 Pod 被调度到同一个 Node 上,影响可用性。默认值为 true,即每次只调度一个 Node。设置为 false 时,调度器会尝试在多个 Node 上调度 Pod,直到所有的 Pod 都得到调度。

Max Failed Placement Tries

--max-failed-placement-tries 参数设置失败次数限制,超过限制时 Kubernetes 会终止当前的调度,并切换到备选方案。这个参数的意义在于防止无限循环的调度失败。默认为 20,建议设置成一个比较大的数字。

Rescheduler Policies

调度算法

Kubernetes 支持多种类型的调度算法,下面我们将介绍一些常用的算法:

“汪峰法”调度器

“波特曼”调度器

亲和性和反亲和性优先级

Kubernetes 支持两种类型的优先级,亲和性优先级和反亲和性优先级。

亲和性优先级 是一种软策略,可以使得特定类型 Pod 只被调度到特定的 Node 上。例如,可以给 GPU 任务指定特定的 Node,这样只要有 GPU 可用,就可以安排这些 Pod 到这些 Node 上运行。亲和性优先级通过 Label Selector 配置,例如,可以设置 Node 上的标签 "accelerator=nvidia-tesla-p100" 来标记可以运行 NVIDIA Tesla P100 类的 Pod。

反亲和性优先级 也是一种软策略,可以使得某些类型 Pod 不被调度到特定的 Node 上。例如,可以设置 Node 上的标签 "db=true" 表示这个 Node 上只能运行数据库类型的 Pod。

优先级配置项如下:

spec:
  priorityClassName: high-priority # 优先级名称
  priority: 1000000               # 优先级数值

通过上面示例配置,这个 Pod 将拥有比普通 Pod 更高的优先级,因此只有拥有对应标签的 Node 上才可以调度到它。

3.主流的调度器类型及特点

kube-scheduler

Kubernetes 自带的调度器组件叫做 kube-scheduler,它是 Kubernetes 集群中的独立进程,负责资源的调度。kube-scheduler 以 Pod 作为最小调度单位,会根据调度策略为 Pod 分配 Node。具体来说,kube-scheduler 会按照以下几步顺序调度 Pod:

  1. 查询待调度的 Pod。
  2. 根据优先级排序待调度的 Pod。
  3. 对节点进行绑定,即给每个 Pod 分配一个主机(Node)。
  4. 判断是否有资源不足的情况。
  5. 更新 Node 的状态信息。

除此之外,kube-scheduler 还支持多种调度策略,比如轮询调度、最少使用率调度、最快速度调度、高优相似性调度等。

kube-scheduler 默认采用的是 RoundRobin 调度算法,即每次调度时会将等待调度的 Pod 轮流分配给集群中空闲的节点,这种方式简单、省时,但不一定能做到最优调度。如果希望获得更高的调度效果,可以尝试修改 kube-scheduler 的调度算法。

custom-scheduler

为了满足更加复杂的调度需求,Kubernetes 提供了第三方自定义调度器的机制。通过编写自定义调度器插件,可以对 Kubernetes 集群的调度过程进行干预,实现更加灵活的集群调度策略。当然,使用自定义调度器也是需要付出代价的,因为编写一个好的调度器插件并不是一蹴而就的事情。

目前,Kubernetes 有两个主要的调度器插件:

  • Default Scheduler 插件:它是 Kubernetes 系统自带的默认调度器,采用 “汪峰法” 调度算法。
  • External Scheduler 插件:它允许使用 Kubernetes 自身以外的调度器来调度 Pod。

Summary

Kubernetes 的调度器是一个十分重要的模块,它负责 Pod 调度,包括初始调度、反复调度、扩缩容等。目前,Kubernetes 有两个主要的调度器:kube-scheduler 和自定义调度器。kube-scheduler 是 Kubernetes 自带的调度器,针对大多数用户的场景提供了良好的调度效果。而自定义调度器允许用户通过编写插件的方式实现更加复杂的调度策略,这需要用户自己编写调度器插件,需要开发者对 Kubernetes 的原理有比较深刻的理解。

猜你喜欢

转载自blog.csdn.net/universsky2015/article/details/133446656