Skip to content

Commit

Permalink
修改调度逻辑
Browse files Browse the repository at this point in the history
  • Loading branch information
isno committed May 4, 2024
1 parent 3bc36ff commit 4ec12cf
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 54 deletions.
3 changes: 1 addition & 2 deletions .vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,7 @@ export default defineUserConfig({
"/container/resource.md",
"/container/Qos.md",
"/container/auto-scaling.md",
"/container/kube-scheduler.md",
"/container/scheduling-framework.md",
"/container/kube-scheduler.md"
]
},

Expand Down
Binary file added assets/Volcano-arch.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/device-plugin.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 18 additions & 21 deletions container/kube-scheduler.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,33 @@
# 7.5.4 Kubernetes 默认调度器
# 7.5.4 Kubernetes 调度器

调度器主要的职责是为一个新创建出来的 Pod,寻找一个最合适的节点(Node)
Kubernetes 的设计思想是足够地开放,这种思想也体现在调度逻辑上

:::tip <a/>

为了充分利用硬件资源,通常会将各种类型(CPU 密集、IO 密集、批量处理、低延迟作业)的 workloads 运行在同一台机器上,这种方式减少了硬件上的投入,但也使调度问题更加复杂。

随着集群规模的增大,需要调度的任务的规模也线性增大,由于调度器的工作负载与集群大小大致成比例,调度器有成为可伸缩性瓶颈的风险。

:::right

—— from Omega 论文
Kubernetes 从 v1.15 版本起,为 kube-scheduler 设计了可插拔的调度框架(Scheduling Framework)。有了 Scheduling Framework,在保持调度“核心”简单且可维护的同时,用户可以编写自己的调度插件注册到 Scheduling Framework 的扩展点来实现自己想要的调度逻辑。

如下图,现实了 Pod 的调度上下文以及调度框架公开的扩展点。
:::center
![](../assets/scheduling-framework-extensions.png)<br/>
Kubernetes 调度框架
:::


默认调度器的主要职责,就是为一个新创建出来的 Pod,寻找一个最合适的节点(Node)而这里“最合适”的含义,包括两层:
一个 Pod 完整调度过程可以分为两个阶段 :scheduling cycle(调度周期)和 binding cycle(绑定周期)。

- 预选:从集群所有的节点中,根据调度算法挑选出所有可以运行该 Pod 的节点;
- 优选:从第一步的结果中,再根据调度算法挑选一个最符合条件的节点作为最终结果
- scheduling cycle 主要职责是为 Pod 选择一个 node,类似于数据库查询和筛选。去除那些不符合要求的节点后,剩余节点根据给定的分数进行排名,最后选择一个得分最高的节点。这些步骤被称为过滤和评分。
- binding cycle 是落实上一个阶段的选择,确保 Kubelet 在选定的节点上启动 Pod

虽然 scheduling cycle 为 Pod 选择了一个 node,但是在接下来的 binding cycle 中, 在这个 node 上给这个 Pod 创建 persistent volume 失败了, 那整个调度过程也是算失败的,需要回到最开始的步骤重新调度。以上两个过程加起来称为一个 scheduling context(调度上下文)。

所以在具体的调度流程中,默认调度器会首先调用一组叫作 Predicate 的调度算法,来检查每个 Node。然后,再调用一组叫作 Priority 的调度算法,来给上一步得到的结果里的每个 Node 打分。最终的调度结果,就是得分最高的那个Node。

## 扩展调度插件

:::tip inform
:::
Pod 是原子的调度单位,这句话意味着 Kubernetes 默认调度器是以 Pod 为调度单元进行依次调度的,并不考虑 Pod 之间的关联关系。但是很多数据**计算类的离线作业具有组合调度的特点,要求所有的子任务都能够成功创建后,整个作业才能正常运行**,即所谓的 All_or_Nothing。

例如:JobA 需要 4 个 Pod 同时启动,才算正常运行。kube-scheduler 依次调度 3 个 Pod 并创建成功,到第 4 个 Pod 时,集群资源不足,则 JobA 的 3 个 Pod 处于空等的状态。但是它们已经占用了部分资源,如果第 4 个 Pod 不能及时启动的话,整个 JobA 无法成功运行,更糟糕的集群其他的资源刚好被 JobB 的 3 个 Pod 所占用,同时在等待 JobB 的第 4 个 Pod 创建,此时整个集群就出现了死锁。

**解决以上问题的思想是将调度单元从 Pod 修改为 PodGroup,以组的形式进行调度,实现「Gang Scheduling」**

:::center
![](../assets/kube-scheduler.png)<br/>
图 7-1 kube-scheduler 组件概览
:::
最开始是社区催化 kube-batch,能够将一个训练任务的多个 Pod 当做一个整体进行调度,只有当任务所有 Pod 的资源都满足,才会将容器在节点上启动;kube-batch还提供了 Queue 的机制(其实就是多租户),不同队列之间可以设置优先级,优先级高的队列中的任务会优先得到调度。

但仅有调度器还不足以支持相应的批量计算作业,作为一个批量计算系统还需要其它很多组件的支持,例如 作业管理,数据管理,资源规划等等。后续社区中又陆续出现 Volcano、Koordinator 等一些以 Kubernetes 为基础的通用的计算系统。
虽功能上有些差异,但总体而言核心依靠最基本的 Gang Scheduling,提供主流架构的 CPU、GPU 在内的异构设备混合调度能力,再补些 MPI 等辅助功能。
33 changes: 27 additions & 6 deletions container/resource.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,39 @@

## 异构资源

当容器运行需要一些特殊资源,Kubernetes 就无能为力了,为了支持异构计算和高性能网络,Kubernetes 提供了 Device Plugin 与各类高性能硬件集成。如此,设备厂商只需要实现相应的 API 接口,无需修改 Kubelet 主干代码,就可以实现譬如 RoCE 网卡、GPU、NPU、FPGA 等各种设备的扩展
当容器运行需要一些特殊资源,Kubernetes 就无能为力了,为了支持异构计算和高性能网络,Kubernetes 提供了 Device Plugin 与各类高性能硬件集成。如此,设备厂商只需要实现相应的 API 接口,无需修改 Kubelet 主干代码,就可以实现譬如 RoCE 网卡、GPU、NPU、FPGA 等各种设备的扩展

实际上 Device plugins 是简单的 grpc server,需要实现以下两个方法 ListAndWatch和Allocate,并监听在/var/lib/kubelet/device-plugins/目录下的Unix Socket,比如/var/lib/kubelet/device-plugins/nvidia.sock
如下代码所示,在安装好驱动的前提下,两个步骤就可以使用 GPU 资源:

其中:
1. 安装 NVIDIA 设备插件来识别和管理 GPU 资源。

- ListAndWatch: Kubelet会调用该API做设备发现和状态更新(比如设备变得不健康)
- Allocate: 当Kubelet创建要使用该设备的容器时, Kubelet会调用该API执行设备相应的操作并且通知Kubelet初始化容器所需的device,volume和环境变量的配置。
```
kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.9.0/nvidia-device-plugin.yml
```

2. 创建一个需要一个 NVIDIA GPU 的 Pod。

```
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
containers:
- name: cuda-container
image: nvidia/cuda:10.0-base
resources:
limits:
nvidia.com/gpu: 1
```

实际上 Device plugins 就是需要实现 ListAndWatch 和 Allocate 两个接口的 grpc server,其中:

- ListAndWatch: Kubelet 会调用该 API 做设备发现和状态更新(比如设备变得不健康);
- Allocate: 当 Kubelet 创建要使用该设备的容器时,Kubelet 会调用该 API 执行设备相应的操作并且通知 Kubelet 初始化容器所需的 device、volume 和环境变量的配置。

:::center
![](../assets/Device-plugin.webp)<br/>
![](../assets/device-plugin.png)<br/>
:::

device plugin 能实现一些异构资源基本支持,但面临复杂的场景还是有点能力不足。譬如大模型训练场依赖高性能网络,而高性能网络的节点间通信需要用到 RDMA 协议和支持 RDMA 协议的网络设备,而这些设备又和 GPU 在节点上的系统拓扑层面是紧密协作的,这就要求在分配 GPU 和 RDMA 时需要感知硬件拓扑,尽可能就近分配这种设备。
Expand Down
25 changes: 0 additions & 25 deletions container/scheduling-framework.md

This file was deleted.

0 comments on commit 4ec12cf

Please sign in to comment.