找回密码
 立即注册
首页 业界区 业界 HAMi vGPU 原理分析 Part3:hami-scheduler 工作流程分 ...

HAMi vGPU 原理分析 Part3:hami-scheduler 工作流程分析

湄圳啸 昨天 08:31
1.png

上篇我们分析了 hami-webhook,该 Webhook 将申请了 vGPU 资源的 Pod 的调度器修改为 hami-scheduler,后续使用 hami-scheduler 进行调度。
本文为 HAMi 原理分析的第三篇,分析 hami-scheduler 工作流程。
上篇主要分析了 hami-webhook,解决了:Pod 是如何使用到 hami-scheduler,创建 Pod 时我们未指定 SchedulerName 默认会使用 default-scheduler 进行调度才对 问题。
这篇开始分析 hami-scheduler,解决另一个问题:hami-scheduler 逻辑,spread & binpark 等 高级调度策略是如何实现的
写完发现内容还是很多,spread & binpark 调度策略下一篇在分析吧,这篇主要分析调度流程。
以下分析基于 HAMi v2.4.0
省流:
HAMi Webhook 、Scheduler 工作流程如下:
2.png


  • 1)用户创建 Pod 并在 Pod 中申请了 vGPU 资源
  • 2)kube-apiserver 根据 MutatingWebhookConfiguration 配置请求 HAMi-Webhook
  • 3)HAMi-Webhook 检测 Pod 中的 Resource,如果申请的由 HAMi 管理的 vGPU 资源,就会把 Pod 中的 SchedulerName 改成了 hami-scheduler,这样这个 Pod 就会由 hami-scheduler 进行调度了。
  • 对于特权模式的 Pod,Webhook 会直接跳过不处理
  • 对于使用 vGPU 资源但指定了 nodeName 的 Pod,Webhook 会直接拒绝
  • 4)hami-scheduler 进行 Pod 调度,不过就是用的 k8s 的默认 kube-scheduler 镜像,因此调度逻辑和默认的 default-scheduler 是一样的,但是 kube-scheduler 还会根据 KubeSchedulerConfiguration 配置,调用 Extender Scheduler 插件
  • 这个 Extender Scheduler 就是 hami-scheduler Pod 中的另一个 Container,该 Container 同时提供了 Webhook 和 Scheduler 相关 API。
  • 当 Pod 申请了 vGPU 资源时,kube-scheduler 就会根据配置以 HTTP 形式调用 Extender Scheduler 插件,这样就实现了自定义调度逻辑
  • 5)Extender Scheduler 插件包含了真正的 hami 调度逻辑, 调度时根据节点剩余资源量进行打分选择节点

    • 这里就包含了 spread & binpark 等 高级调度策略的实现

  • 6)异步任务,包括 GPU 感知逻辑

    • devicePlugin 中的后台 Goroutine 定时上报 Node 上的 GPU 资源并写入到 Node 的 Annoations
    • 除了 DevicePlugin 之外,还使用异步任务以 Patch Annotation 方式提交更多信息
    • Extender Scheduler 插件根据 Node Annoations 解析出 GPU 资源总量、从 Node 上已经运行的 Pod 的 Annoations 中解析出 GPU 使用量,计算出每个 Node 剩余的可用资源保存到内存供调度时使用

1. 概述

Hami-scheduler 主要是 Pod 的调度逻辑,从集群节点中为当前 Pod 选择最合适的节点。
Hami-scheduler 也是通过 Scheduler Extender 方式实现的,可以参考上一篇文章 K8s 自定义调度器 Part1:通过 Scheduler Extender 实现自定义调度逻辑 手把手实现一个自定义调度器。
但是 HAMi 并没有直接扩展 default-scheduler,而是使用默认的 kube-scheduler 镜像额外启动了一个 scheduler,但是通过配置把名称指定为了 hami-scheduler。
然后给这个 hami-scheduler 配置了 Extender,Extender 服务就是同 Pod 中的另一个 Container 启动的一个 http 服务。
ps:后续说的 hami-scheduler 一般只这部分 Extender 实现的调度插件
2. 具体部署

Deployment

Hami-scheduler 使用 Deployment 进行部署,该 Deployment 中有两个 Container,其中一个是原生的 kube-scheduler,另一个则是 HAMi 的 Scheduler 服务。
完整 yaml 如下:
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4.   name: vgpu-hami-scheduler
  5.   namespace: kube-system
  6. spec:
  7.   template:
  8.     spec:
  9.       containers:
  10.       - command:
  11.         - kube-scheduler
  12.         - --config=/config/config.yaml
  13.         - -v=4
  14.         - --leader-elect=true
  15.         - --leader-elect-resource-name=hami-scheduler
  16.         - --leader-elect-resource-namespace=kube-system
  17.         image: 192.168.116.54:5000/kube-scheduler:v1.23.17
  18.         imagePullPolicy: IfNotPresent
  19.         name: kube-scheduler
  20.         resources: {}
  21.         terminationMessagePath: /dev/termination-log
  22.         terminationMessagePolicy: File
  23.         volumeMounts:
  24.         - mountPath: /config
  25.           name: scheduler-config
  26.       - command:
  27.         - scheduler
  28.         - --resource-name=nvidia.com/vgpu
  29.         - --resource-mem=nvidia.com/gpumem
  30.         - --resource-cores=nvidia.com/gpucores
  31.         - --resource-mem-percentage=nvidia.com/gpumem-percentage
  32.         - --resource-priority=nvidia.com/priority
  33.         - --http_bind=0.0.0.0:443
  34.         - --cert_file=/tls/tls.crt
  35.         - --key_file=/tls/tls.key
  36.         - --scheduler-name=hami-scheduler
  37.         - --metrics-bind-address=:9395
  38.         - --default-mem=0
  39.         - --default-gpu=1
  40.         - --default-cores=0
  41.         - --iluvatar-memory=iluvatar.ai/vcuda-memory
  42.         - --iluvatar-cores=iluvatar.ai/vcuda-core
  43.         - --cambricon-mlu-name=cambricon.com/vmlu
  44.         - --cambricon-mlu-memory=cambricon.com/mlu.smlu.vmemory
  45.         - --cambricon-mlu-cores=cambricon.com/mlu.smlu.vcore
  46.         - --ascend-name=huawei.com/Ascend910
  47.         - --ascend-memory=huawei.com/Ascend910-memory
  48.         - --ascend310p-name=huawei.com/Ascend310P
  49.         - --ascend310p-memory=huawei.com/Ascend310P-memory
  50.         - --overwrite-env=false
  51.         - --node-scheduler-policy=binpack
  52.         - --gpu-scheduler-policy=spread
  53.         - --debug
  54.         - -v=4
  55.         image: projecthami/hami:v2.3.13
  56.         imagePullPolicy: IfNotPresent
  57.         name: vgpu-scheduler-extender
  58.         ports:
  59.         - containerPort: 443
  60.           name: http
  61.           protocol: TCP
  62.         volumeMounts:
  63.         - mountPath: /tls
  64.           name: tls-config
  65.       dnsPolicy: ClusterFirst
  66.       priorityClassName: system-node-critical
  67.       restartPolicy: Always
  68.       schedulerName: default-scheduler
  69.       serviceAccount: vgpu-hami-scheduler
  70.       serviceAccountName: vgpu-hami-scheduler
  71.       terminationGracePeriodSeconds: 30
  72.       volumes:
  73.       - name: tls-config
  74.         secret:
  75.           defaultMode: 420
  76.           secretName: vgpu-hami-scheduler-tls
  77.       - configMap:
  78.           defaultMode: 420
  79.           name: vgpu-hami-scheduler-newversion
  80.         name: scheduler-config
复制代码
KubeSchedulerConfiguration

对应的 Scheduler 的配置文件存储在 Configmap 中,具体内容如下:
  1. apiVersion: v1
  2. data:
  3.   config.yaml: |
  4.     apiVersion: kubescheduler.config.k8s.io/v1beta2
  5.     kind: KubeSchedulerConfiguration
  6.     leaderElection:
  7.       leaderElect: false
  8.     profiles:
  9.     - schedulerName: hami-scheduler
  10.     extenders:
  11.     - urlPrefix: "https://127.0.0.1:443"
  12.       filterVerb: filter
  13.       bindVerb: bind
  14.       nodeCacheCapable: true
  15.       weight: 1
  16.       httpTimeout: 30s
  17.       enableHTTPS: true
  18.       tlsConfig:
  19.         insecure: true
  20.       managedResources:
  21.       - name: nvidia.com/vgpu
  22.         ignoredByScheduler: true
  23.       - name: nvidia.com/gpumem
  24.         ignoredByScheduler: true
  25.       - name: nvidia.com/gpucores
  26.         ignoredByScheduler: true
  27.       - name: nvidia.com/gpumem-percentage
  28.         ignoredByScheduler: true
  29.       - name: nvidia.com/priority
  30.         ignoredByScheduler: true
  31.       - name: cambricon.com/vmlu
  32.         ignoredByScheduler: true
  33.       - name: hygon.com/dcunum
  34.         ignoredByScheduler: true
  35.       - name: hygon.com/dcumem
  36.         ignoredByScheduler: true
  37.       - name: hygon.com/dcucores
  38.         ignoredByScheduler: true
  39.       - name: iluvatar.ai/vgpu
  40.         ignoredByScheduler: true
  41.       - name: huawei.com/Ascend910-memory
  42.         ignoredByScheduler: true
  43.       - name: huawei.com/Ascend910
  44.         ignoredByScheduler: true
  45.       - name: huawei.com/Ascend310P-memory
  46.         ignoredByScheduler: true
  47.       - name: huawei.com/Ascend310P
  48.         ignoredByScheduler: true
  49. kind: ConfigMap
  50. metadata:
  51.   name: vgpu-hami-scheduler-newversion
  52.   namespace: kube-system
复制代码
SchedulerName

首先是指定了 Scheduler 名字叫做 hami-scheduler,k8s 默认的调度器叫做 default-scheduler。
  1. profiles:
  2. - schedulerName: hami-scheduler
复制代码
创建 Pod 的时候我们是没有指定 schedulerName 的,所以默认都会使用 default-scheduler,也就是默认 kube-scheduler 进行调度。
之前 hami-webhook 修改 SchedulerName 时就需要和这里配置的名称对应。
Extenders

调度器核心配置如下:
  1. extenders:
  2. - urlPrefix: "https://127.0.0.1:443"
  3.   filterVerb: filter
  4.   bindVerb: bind
复制代码
参数解释:

  • urlPrefix: "https://127.0.0.1:443":这是一个调度器扩展器的服务地址,Kubernetes 调度器会调用这个地址来请求外部调度逻辑。可以通过 HTTPS 访问。

    • External Scheduler 因为是和 kube-scheduler 部署在一个 Pod 里的,因此使用 127.0.0.1 进行访问

  • filterVerb: filter:这个动词指示了调度器会调用这个扩展器服务来过滤节点,即决定哪些节点适合调度 Pod。

    • Filter 接口对应这个 http 服务的 url 就是 /filter

  • bindVerb: bind:调度器扩展器可以执行绑定操作,即将 Pod 绑定到特定节点。

    • 同上,bind 就要对应 /bind 这个接口

managedResources

managedResources 这部分指定这个扩展调度器 hami-cheduler 管理的资源,只有 Pod Resource 中申请了 managedResources 中指定的资源时,Scheduler 才会请求我们配置的 Extender,也就是 hami-scheduler。
即:只要没申请 vGPU 资源,就是指定使用 hami-scheduler 调度,也是由名为 hami-scheduler 的 kube-scheduler 进行调度,不会请求 Extender,真正的 HAMi 调度插件不会生效。
  1. managedResources:
  2. - name: nvidia.com/vgpu
  3.   ignoredByScheduler: true
  4. - name: nvidia.com/gpumem
  5.   ignoredByScheduler: true
  6.   ...
复制代码

  • name: nvidia.com/vgpu:资源名称
  • ignoredByScheduler:当设置为 true 时,调度器在做节点资源匹配和资源分配时,会忽略这个资源。这些资源都由扩展的 hami-scheduler 进行调度即可。
这样配置之后,对于 nvidia.com/vgpu、nvidia.com/gpumem 等等 managedResources 中指定的资源,调度器在做节点资源匹配和资源分配时,会忽略这个资源,不会因为 Node 上没有这些虚拟资源,就直接调度失败了。
当调度器请求扩展的 hami-scheduler 进行调度时,hami-scheduler 就能够正常处理这些资源,根据 Pod 申请的 Resource 配置找到对应的节点。
接下来则分析 hami-scheduler 的具体实现,包括两个问题:

  • 1)hami-scheduler 如何感知 Node 上的 GPU 信息的,因为前面提到 gpucore、gpumem 这些都是虚拟资源, DevicePlugin 也是没有直接上报到 Node 上的
  • 2)hami-scheduler 是如何选择最合适的节点的,spark & binpark 等高级调度策略是如何实现的
3. hami 如何感知 Nod 上的 GPU 资源情况的

分为两部分:

  • 1)感知 Node 上的 GPU 资源信息
  • 2)感知 Node 上 GPU 资源使用情况
因为 gpucore、gpumem 这些都是虚拟资源,因此不能像 DevicePlugin 上报的标准第三方资源一样,由 K8s 直接维护,而是需要 hami 自行维护。
为什么需要自定义感知逻辑

到这里大家可能会有疑问,上一篇文章中介绍了 hami-device-plugin-nvidia,这里的 devicePlugin 不就已经感知了节点上的 GPU 并上报到 kube-apiserver 了吗,怎么还需要实现一个感知逻辑?
在节点上都可以看到了,例如下面节点上就有 20 个 GPU
  1. root@j99cloudvm:~# k describe node j99cloudvm |grep Capa -A 7
  2. Capacity:
  3.   cpu:                64
  4.   ephemeral-storage:  2097139692Ki
  5.   hugepages-1Gi:      0
  6.   hugepages-2Mi:      0
  7.   memory:             131966216Ki
  8.   nvidia.com/gpu:     20
复制代码
因为:hami-scheduler 做了细粒度的 gpucore、gpumem 切分,那么就要知道节点上的 GPU 具体数量、每张卡的显存大小等信息,不然如果把 Pod 分配到一个所有 GPU 显存都已经消耗完的 Node 上不就出问题了。
感知节点上的 GPU 资源

Hami 是如何感知节点上的 GPU 情况的呢?也就是之前 start 方法中的这个 Goroutine 在维护,核心就是在这个 RegisterFromNodeAnnotations 方法中
  1. go sher.RegisterFromNodeAnnotations()
复制代码
精简后代码如下:
调用 kube-apiserver 获取到节点列表,然后从 node 的 Annoations 中解析出 Device 信息,并保存到内存中。
[code]func (s *Scheduler) RegisterFromNodeAnnotations() {    klog.V(5).Infoln("Scheduler into RegisterFromNodeAnnotations")    ticker := time.NewTicker(time.Second * 15)    for {       select {       case
您需要登录后才可以回帖 登录 | 立即注册