使用 Kueue 部署批处理系统


本教程介绍如何使用 Kueue 来部署批处理系统。Kueue 是用于在 Google Kubernetes Engine (GKE) 上执行 Job 排队的云原生 Job 调度器。完成本教程,了解如何设置 GKE 和 Kueue,以先进先出 (FIFO) 顺序运行 Job。

背景

Job 是运行到完成的应用,例如机器学习、渲染、模拟、分析、CI/CD 和类似工作负载。

Kueue 是一个云原生 Job 调度器,使用默认的 Kubernetes 调度器、Job 控制器和集群自动扩缩器来提供端到端批处理系统。Kueue 会根据配额和层次结构,在团队之间共享资源,从而实现 Job 排队,确定 Job 应等待的时间和应开始的时间。

Kueue 具有以下特征:

  • 它针对云架构进行了优化,其中资源是异构、可互换且可扩缩。
  • 它提供了一组 API,用于管理弹性配额和管理 Job 队列。
  • 它不会重新实现自动扩缩、Pod 调度或 Job 生命周期管理等现有功能。
  • Kueue 内置了对 Kubernetesbatch/v1.Job API 的支持。
  • 它可以与其他作业 API 集成。

Kueue 会将任何 API 定义的作业用作工作负载,以避免与特定 Kubernetes Job API 混淆。

目标

本教程适合需要在 Kubernetes 上实现批处理系统的集群运维人员和其他用户。在本教程中,您将为两个租户团队设置共享集群。每个团队都有自己的命名空间,用于创建 Job 并共享由相应配额控制的全局资源。

本教程介绍以下步骤:

  1. 创建 GKE 集群
  2. 创建 ResourceFlavor
  3. 创建 ClusterQueue
  4. 创建 LocalQueue
  5. 创建 Job 并观察允许的工作负载

费用

本教程使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。

完成本教程后,请删除您创建的资源,以避免继续计费。如需了解详情,请参阅清理

准备工作

设置项目

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the GKE API.

    Enable the API

  5. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Go to project selector

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the GKE API.

    Enable the API

设置 Google Cloud CLI 的默认值

  1. 在 Google Cloud 控制台中,启动 Cloud Shell 实例:
    打开 Cloud Shell

  2. 下载此示例应用的源代码:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd kubernetes-engine-samples/batch/kueue-intro
    
  3. 设置默认环境变量:

    gcloud config set project PROJECT_ID
    gcloud config set compute/region COMPUTE_REGION
    

    替换以下值:

创建 GKE 集群

  1. 创建名为 kueue-autopilot 的 GKE Autopilot 集群:

    gcloud container clusters create-auto kueue-autopilot \
      --release-channel "rapid" --region COMPUTE_REGION
    

    Autopilot 集群是全代管式,并具有内置自动扩缩功能。详细了解 GKE Autopilot

    Kueue 还支持使用节点自动预配功能和标准自动扩缩节点池的标准 GKE。

    创建集群后,结果类似于以下内容:

      NAME: kueue-autopilot
      LOCATION: us-central1
      MASTER_VERSION: 1.26.2-gke.1000
      MASTER_IP: 35.193.173.228
      MACHINE_TYPE: e2-medium
      NODE_VERSION: 1.26.2-gke.1000
      NUM_NODES: 3
      STATUS: RUNNING
    

    其中,kueue-autopilotSTATUSRUNNING

  2. 获取用于集群的身份验证凭据

    gcloud container clusters get-credentials kueue-autopilot
    
  3. 在集群上安装 Kueue:

    VERSION=VERSION
    kubectl apply --server-side -f \
      https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml
    

    VERSION 替换为最新版本的 Kueue。如需详细了解 Kueue 版本,请参阅 Kueue 版本

  4. 等待 Kueue Pod 准备就绪:

    watch kubectl -n kueue-system get pods
    

    在继续下一步之前,输出应类似于以下内容:

    NAME                                        READY   STATUS    RESTARTS   AGE
    kueue-controller-manager-66d8bb946b-wr2l2   2/2     Running   0          3m36s
    
  5. 创建两个名为 team-ateam-b 的新命名空间:

    kubectl create namespace team-a
    kubectl create namespace team-b
    

创建 ResourceFlavor

ResourceFlavor 是一种对象,用于表示集群内节点的变体,即将它们与节点标签和污点相关联。例如,您可以使用 ResourceFlavor 表示具有不同预配保证(例如 Spot 与按需)、架构(例如 x86 与 ARM CPU)、品牌和型号(例如 Nvidia A100 与 T4 GPU)的虚拟机。

在本教程中,kueue-autopilot 集群具有同构资源。因此,请为 CPU、内存、临时存储和 GPU 创建一个 ResourceFlavor,而不使用标签或污点。

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: default-flavor # This ResourceFlavor will be used for all the resources
部署 ResourceFlavor:

kubectl apply -f flavors.yaml

创建 ClusterQueue

ClusterQueue 是集群级对象,用于管理 CPU、内存、GPU 等资源池。它负责管理 ResourceFlavor,并限制其用量并决定工作负载的允许顺序。

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: cluster-queue
spec:
  namespaceSelector: {} # Available to all namespaces
  queueingStrategy: BestEffortFIFO # Default queueing strategy
  resourceGroups:
  - coveredResources: ["cpu", "memory", "nvidia.com/gpu", "ephemeral-storage"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 10
      - name: "memory"
        nominalQuota: 10Gi
      - name: "nvidia.com/gpu"
        nominalQuota: 10
      - name: "ephemeral-storage"
        nominalQuota: 10Gi

部署 ClusterQueue:

kubectl apply -f cluster-queue.yaml

使用顺序由 .spec.queueingStrategy 确定,其中有两种配置:

  • BestEffortFIFO

    • 默认排队策略配置。
    • 工作负载准入遵循先进先出 (FIFO) 规则,但如果配额不足以允许队列头部的工作负载,则将尝试队列中的下一项工作负载。
  • StrictFIFO

    • 保证 FIFO 语义。
    • 队列头部的工作负载可以阻止将更多工作负载加入队列,直到该工作负载获得准入许可。

cluster-queue.yaml 中,您可以创建一个名为 cluster-queue 的新 ClusterQueue。此 ClusterQueue 使用 flavors.yaml 中创建的变种管理四个资源:cpumemorynvidia.com/gpuephemeral-storage。配额由工作负载 Pod 规范中的请求使用。

每个变种都包含表示为 .spec.resourceGroups[].flavors[].resources[].nominalQuota 的使用限制。在这种情况下,当且仅当满足以下条件时,ClusterQueue 才允许工作负载:

  • CPU 请求的总和小于或等于 10
  • 内存请求的总和小于或等于 10Gi
  • GPU 请求的总和小于或等于 10
  • 使用的存储空间总和小于或等于 10Gi

创建 LocalQueue

LocalQueue 是一个命名空间对象,接受来自命名空间中用户的工作负载。不同命名空间的 LocalQueue 可以指向同一个 ClusterQueue,它们可以在其中共享资源的配额。在这种情况下,命名空间 team-ateam-b 中的 LocalQueue 指向 .spec.clusterQueue 下的同一 ClusterQueue cluster-queue

apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: team-a # LocalQueue under team-a namespace
  name: lq-team-a
spec:
  clusterQueue: cluster-queue # Point to the ClusterQueue
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: team-b # LocalQueue under team-b namespace
  name: lq-team-b
spec:
  clusterQueue: cluster-queue # Point to the ClusterQueue

每个团队都在其命名空间中将其工作负载发送到 LocalQueue。然后,ClusterQueue 会分配这些资源。

部署 LocalQueue:

kubectl apply -f local-queue.yaml

创建 Job 并观察允许的工作负载

apiVersion: batch/v1
kind: Job
metadata:
  namespace: team-a # Job under team-a namespace
  generateName: sample-job-team-a-
  annotations:
    kueue.x-k8s.io/queue-name: lq-team-a # Point to the LocalQueue
spec:
  ttlSecondsAfterFinished: 60 # Job will be deleted after 60 seconds
  parallelism: 3 # This Job will have 3 replicas running at the same time
  completions: 3 # This Job requires 3 completions
  suspend: true # Set to true to allow Kueue to control the Job when it starts
  template:
    spec:
      nodeSelector:
        cloud.google.com/gke-accelerator: "nvidia-tesla-t4" # Specify the GPU hardware
      containers:
      - name: dummy-job
        image: gcr.io/k8s-staging-perf-tests/sleep:latest
        args: ["10s"] # Sleep for 10 seconds
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
            ephemeral-storage: "512Mi"
            nvidia.com/gpu: "1"
          limits:
            cpu: "500m"
            memory: "512Mi"
            ephemeral-storage: "512Mi"
            nvidia.com/gpu: "1"
      restartPolicy: Never

在命名空间 team-a 下创建 Job。此 Job 指向 LocalQueue lq-team-a。为了请求 GPU 资源,nodeSelector 会设置为 nvidia-tesla-t4

Job 由三个 Pod 并行休眠 10 秒组成。根据 ttlSecondsAfterFinished,Job 会在 60 秒后清理。

此 Job 需要 1500 milliCPU、1536 Mi 内存、1536 Mi 临时存储空间和三个 GPU,因为有三个 Pod。

Job 还在文件 job-team-b.yaml(其命名空间属于 team-b)下创建,其中请求表示具有不同需求的不同团队。

如需了解详情,请参阅在 Autopilot 中部署 GPU 工作负载

  1. 在新终端中,观察每两秒钟刷新一次的 ClusterQueue 的状态:

    watch -n 2 kubectl get clusterqueue cluster-queue -o wide
    
  2. 在新终端中,观察节点的状态:

    watch -n 2 kubectl get nodes -o wide
    
  3. 在新终端中,每 10 秒从命名空间 team-ateam-b 创建到 LocalQueue 的 Job:

    ./create_jobs.sh job-team-a.yaml job-team-b.yaml 10
    
  4. 观察正在排队的 Job、在 ClusterQueue 中允许的 Job,以及使用 GKE Autopilot 启动的节点。

  5. 从命名空间 team-a 获取 Job:

    kubectl -n team-a get jobs
    

    结果类似于以下内容:

    NAME                      COMPLETIONS   DURATION   AGE
    sample-job-team-b-t6jnr   3/3           21s        3m27s
    sample-job-team-a-tm7kc   0/3                      2m27s
    sample-job-team-a-vjtnw   3/3           30s        3m50s
    sample-job-team-b-vn6rp   0/3                      40s
    sample-job-team-a-z86h2   0/3                      2m15s
    sample-job-team-b-zfwj8   0/3                      28s
    sample-job-team-a-zjkbj   0/3                      4s
    sample-job-team-a-zzvjg   3/3           83s        4m50s
    
  6. 复制上一步中的 Job 名称,并通过 Workloads API 观察 Job 的准入状态和事件:

    kubectl -n team-a describe workload JOB_NAME
    
  7. 当待处理 Job 开始从 ClusterQueue 增加时,请在正在运行的脚本上按 CTRL + C 结束脚本。

  8. 完成所有 Job 后,请注意节点是否缩减。

清理

为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

删除项目

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

逐个删除资源

  1. 删除 Kueue 配额系统:

    kubectl delete -n team-a localqueue lq-team-a
    kubectl delete -n team-b localqueue lq-team-b
    kubectl delete clusterqueue cluster-queue
    kubectl delete resourceflavor default-flavor
    
  2. 删除 Kueue 清单:

    VERSION=VERSION
    kubectl delete -f \
      https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml
    
  3. 删除集群:

    gcloud container clusters delete kueue-autopilot --region=COMPUTE_REGION
    

后续步骤