Kueue를 사용한 일괄 시스템 배포

이 튜토리얼에서는 Google Kubernetes Engine(GKE)에서 Kueue작업을 예약하여 사용 가능한 리소스를 최적화하는 방법을 보여줍니다. 이 튜토리얼에서는 Kueue를 사용하여 일괄 작업을 효율적으로 관리 및 예약하고, 리소스 활용률을 개선하고, 워크로드 관리를 간소화하는 방법을 배웁니다. 두 테넌트 팀에 대해 공유 클러스터를 설정합니다. 여기에서 각 팀은 자체 네임스페이스를 사용하고, 각 팀이 전역 리소스를 공유하는 작업을 만듭니다. 또한 정의한 리소스 할당량에 따라 작업을 예약하도록 Kueue를 구성합니다.

이 튜토리얼은 GKE를 사용하여 일괄 시스템을 구현하는 데 관심이 있는 클라우드 설계자 및 플랫폼 엔지니어를 대상으로 합니다. Google Cloud콘텐츠에서 참조하는 일반적인 역할 및 예시 태스크에 대해 자세히 알아보려면 일반 GKE 사용자 역할 및 태스크를 참조하세요.

이 페이지를 읽기 전 다음 내용을 숙지해야 합니다.

배경

작업은 머신러닝, 렌더링, 시뮬레이션, 분석, CI/CD 및 유사한 워크로드 등 완료될 때까지 실행되는 애플리케이션입니다.

Kueue는 기본 Kubernetes 스케줄러, 작업 컨트롤러, 클러스터 자동 확장 처리와 함께 작동하여 엔드 투 엔드 일괄 시스템을 제공하는 클라우드 네이티브 작업 스케줄러입니다. Kueue는 작업을 큐에 추가하여 여러 팀 간에 리소스가 공정하게 공유되도록 할당량과 계층 구조에 따라 작업의 대기 시기 및 시작 시기를 결정합니다.

Kueue의 특성은 다음과 같습니다.

  • 리소스가 여러 다른 종류로 이루어지며 교환 및 확장이 가능한 클라우드 아키텍처에 최적화되어 있습니다.
  • 탄력적인 할당량을 관리하고 작업을 큐에 추가하는 것을 관리할 수 있는 API 집합을 제공합니다.
  • 자동 확장, 포드 예약, 작업 수명 주기 관리와 같은 기존 기능은 다시 구현되지 않습니다.
  • Kueue에는 Kubernetes batch/v1.Job API에 대한 기본 제공 지원이 포함됩니다.
  • 다른 작업 API와 통합할 수 있습니다.

Kueue는 특정 Kubernetes Job API와 혼동되지 않도록 모든 API로 정의된 작업을 워크로드로 참조합니다.

ResourceFlavor 만들기

ResourceFlavor는 노드 라벨 및 taint와 연결하여 클러스터에서 사용 가능한 노드의 변형을 나타내는 객체입니다. 예를 들어 ResourceFlavor를 사용하여 다양한 프로비저닝 보장(예: 스팟 vs 주문형), 아키텍처(예: x86 vs ARM CPU), 브랜드 및 모델(예: Nvidia A100 vs T4 GPU)의 VM을 나타낼 수 있습니다.

이 튜토리얼에서는 kueue-autopilot 클러스터에 동종 리소스가 사용됩니다. 따라서 라벨 또는 taint 없이 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에 생성된 버전으로 cpu, memory, nvidia.com/gpu, ephemeral-storage의 4개 리소스를 관리합니다. 이 할당량은 워크로드 포드 사양의 요청에 따라 소비됩니다.

각 버전에는 .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

작업을 만들고 허용된 워크로드 관찰하기

이 섹션에서는 team-a 네임스페이스에서 Kubernetes 작업을 만듭니다. Kubernetes의 작업 컨트롤러는 하나 이상의 포드를 만들고 특정 태스크를 성공적으로 실행하는지 확인합니다.

team-a 네임스페이스의 작업에는 다음 속성이 포함됩니다.

  • lq-team-a LocalQueue를 가리킵니다.
  • nodeSelector 필드를 nvidia-tesla-t4로 설정하여 GPU 리소스를 요청합니다.
  • 10초 동안 동시에 절전 모드로 전환되는 3개의 포드로 구성됩니다. ttlSecondsAfterFinished 필드에 정의된 값에 따라 60초 후에 작업이 삭제됩니다.
  • 포드가 3개 있으므로 1,500milliCPU, 메모리 1,536Mi, 임시 스토리지 1,536Mi, GPU 3개가 필요합니다.
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-b에 속하는 job-team-b.yaml 파일 아래에 작업이 생성되고, 서로 다른 요구사항을 가진 다른 팀을 나타내는 요청이 포함됩니다.

자세한 내용은 Autopilot에서 GPU 워크로드 배포를 참조하세요.

  1. 새 터미널에서 2초마다 새로고침되는 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에 대한 작업을 만듭니다.

    ./create_jobs.sh job-team-a.yaml job-team-b.yaml 10
    
  4. 큐에 추가되고 ClusterQueue에 허용된 작업, GKE Autopilot을 사용해 가져온 노드를 관찰합니다.

  5. team-a 네임스페이스에서 작업을 가져옵니다.

    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. 이전 단계에서 작업 이름을 복사하고 Workloads API를 통해 작업의 허용 상태와 이벤트를 관찰합니다.

    kubectl -n team-a describe workload JOB_NAME
    
  7. 대기 중인 작업이 ClusterQueue에서 증가하기 시작하면 실행 중인 스크립트에서 CTRL + C를 눌러 스크립트를 종료합니다.

  8. 모든 작업이 완료되면 노드가 축소되는 것을 확인할 수 있습니다.