혼합된 AI/ML 학습 및 추론 워크로드에 맞게 GKE 리소스 사용률 최적화


이 튜토리얼에서는 단일 Google Kubernetes Engine (GKE) 클러스터 내에서 학습 워크로드와 추론 워크로드 간에 가속기 리소스를 효율적으로 공유하는 방법을 보여줍니다. 혼합 워크로드를 단일 클러스터에 분산하면 리소스 사용률이 개선되고, 클러스터 관리가 간소화되며, 가속기 수 제한으로 인한 문제가 줄어들고, 전반적인 비용 효율성이 향상됩니다.

이 튜토리얼에서는 추론을 위한 Gemma 2 대규모 언어 모델 (LLM)과 Hugging Face TGI (텍스트 생성 인터페이스) 제공 프레임워크를 사용하여 우선순위가 높은 제공 배포를 만들고 우선순위가 낮은 LLM 미세 조정 작업을 만듭니다. 두 워크로드 모두 NVIDIA L4 GPU를 사용하는 단일 클러스터에서 실행됩니다. 오픈소스 Kubernetes 기반 작업 큐잉 시스템인 Kueue를 사용하여 워크로드를 관리하고 예약합니다. Kueue를 사용하면 게재 작업의 우선순위를 지정하고 우선순위가 낮은 학습 작업을 선점하여 리소스 활용을 최적화할 수 있습니다. 게재 수요가 감소하면 확보된 가속기를 재할당하여 학습 작업을 재개합니다. Kueue 및 우선순위 클래스를 사용하여 프로세스 전반에서 리소스 할당량을 관리합니다.

이 튜토리얼은 GKE 클러스터에서 머신러닝 (ML) 모델을 학습시키고 호스팅하려는 머신러닝 (ML) 엔지니어, 플랫폼 관리자 및 운영자, 데이터 및 AI 전문가를 대상으로 하며, 특히 제한된 수의 가속기를 처리할 때 비용과 관리 오버헤드를 줄이려는 사용자를 대상으로 합니다. Google Cloud 콘텐츠에서 참조하는 일반적인 역할 및 예시 작업에 대해 자세히 알아보려면 일반 GKE Enterprise 사용자 역할 및 작업을 참고하세요.

이 페이지를 읽기 전에 다음 사항을 숙지해야 합니다.

목표

이 가이드를 마치면 다음 단계를 수행할 수 있습니다.

  • 우선순위가 높은 게재 배포를 구성합니다.
  • 우선순위가 낮은 학습 작업을 설정합니다.
  • 다양한 수요를 해결하기 위해 선점 전략을 구현합니다.
  • Kueue를 사용하여 학습 및 게재 태스크 간의 리소스 할당을 관리합니다.

시작하기 전에

  • 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.
  • In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

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

  • Enable the required APIs.

    Enable the APIs

  • In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

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

  • Enable the required APIs.

    Enable the APIs

  • Make sure that you have the following role or roles on the project: roles/container.admin, roles/iam.serviceAccountAdmin

    Check for the roles

    1. In the Google Cloud console, go to the IAM page.

      Go to IAM
    2. Select the project.
    3. In the Principal column, find all rows that identify you or a group that you're included in. To learn which groups you're included in, contact your administrator.

    4. For all rows that specify or include you, check the Role column to see whether the list of roles includes the required roles.

    Grant the roles

    1. In the Google Cloud console, go to the IAM page.

      IAM으로 이동
    2. 프로젝트를 선택합니다.
    3. 액세스 권한 부여를 클릭합니다.
    4. 새 주 구성원 필드에 사용자 식별자를 입력합니다. 일반적으로 Google 계정의 이메일 주소입니다.

    5. 역할 선택 목록에서 역할을 선택합니다.
    6. 역할을 추가로 부여하려면 다른 역할 추가를 클릭하고 각 역할을 추가합니다.
    7. 저장을 클릭합니다.

환경 준비

이 섹션에서는 추론 및 학습 워크로드용 TGI 및 모델을 배포하는 데 필요한 리소스를 프로비저닝합니다.

모델 액세스 권한 얻기

GKE에 배포하기 위해 Gemma 모델에 액세스하려면 먼저 라이선스 동의 계약에 서명한 다음 Hugging Face 액세스 토큰을 생성해야 합니다.

  1. 라이선스 동의 계약에 서명합니다. 모델 동의 페이지에 액세스하여 Hugging Face 계정으로 동의를 확인하고 모델 약관에 동의합니다.
  2. 액세스 토큰을 생성합니다. Hugging Face를 통해 모델에 액세스하려면 Hugging Face 토큰이 필요합니다. 아직 토큰이 없으면 다음 단계에 따라 새 토큰을 생성합니다.

    1. 내 프로필 > 설정 > 액세스 토큰을 클릭합니다.
    2. 새 토큰을 선택합니다.
    3. 원하는 이름과 Read 이상의 역할을 지정합니다.
    4. 토큰 생성을 선택합니다.
    5. 클립보드에 생성된 토큰을 복사합니다.

Cloud Shell 실행

이 튜토리얼에서는 Cloud Shell을 사용하여Google Cloud에서 호스팅되는 리소스를 관리합니다. Cloud Shell에는 kubectl, gcloud CLI, Terraform 등 이 튜토리얼에 필요한 소프트웨어가 사전 설치되어 있습니다.

Cloud Shell로 환경을 설정하려면 다음 단계를 따르세요.

  1. Google Cloud 콘솔에서 Google Cloud 콘솔Cloud Shell 활성화 아이콘 Cloud Shell 활성화를 클릭하여 Cloud Shell 세션을 시작합니다. 그러면 Google Cloud 콘솔 하단 창에서 세션이 시작됩니다.

  2. 기본 환경 변수를 설정합니다.

    gcloud config set project PROJECT_ID
    export PROJECT_ID=$(gcloud config get project)
    

    PROJECT_ID를 Google Cloud 프로젝트 ID로 바꿉니다.

  3. GitHub에서 샘플 코드를 복제합니다. Cloud Shell에서 다음 명령어를 실행합니다.

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/
    cd kubernetes-engine-samples/ai-ml/mix-train-and-inference
    export EXAMPLE_HOME=$(pwd)
    

GKE 클러스터 만들기

혼합 워크로드에는 Autopilot 또는 Standard 클러스터를 사용할 수 있습니다. 완전 관리형 Kubernetes 환경을 사용하려면 Autopilot 클러스터를 사용하는 것이 좋습니다. 워크로드에 가장 적합한 GKE 작업 모드를 선택하려면 GKE 작업 모드 선택을 참고하세요.

Autopilot

  1. Cloud Shell에서 기본 환경 변수를 설정합니다.

    export HF_TOKEN=HF_TOKEN
    export REGION=REGION
    export CLUSTER_NAME="llm-cluster"
    export PROJECT_NUMBER=$(gcloud projects list \
        --filter="$(gcloud config get-value project)" \
        --format="value(PROJECT_NUMBER)")
    export MODEL_BUCKET="model-bucket-$PROJECT_ID"
    

    다음 값을 바꿉니다.

    • HF_TOKEN: 이전에 생성한 Hugging Face 토큰입니다.
    • REGION: 사용하려는 가속기 유형을 지원하는 리전입니다(예: L4 GPU의 경우 us-central1).

    학습된 모델 가중치를 저장하는 Cloud Storage 버킷을 나타내는 MODEL_BUCKET 변수를 조정할 수 있습니다.

  2. Autopilot 클러스터를 만듭니다.

    gcloud container clusters create-auto ${CLUSTER_NAME} \
        --project=${PROJECT_ID} \
        --region=${REGION} \
        --release-channel=rapid
    
  3. 미세 조정 작업을 위한 Cloud Storage 버킷을 만듭니다.

    gcloud storage buckets create gs://${MODEL_BUCKET} \
        --location ${REGION} \
        --uniform-bucket-level-access
    
  4. Cloud Storage 버킷에 대한 액세스 권한을 부여하려면 다음 명령어를 실행합니다.

    gcloud storage buckets add-iam-policy-binding "gs://$MODEL_BUCKET" \
        --role=roles/storage.objectAdmin \
        --member=principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$PROJECT_ID.svc.id.goog/subject/ns/llm/sa/default \
        --condition=None
    
  5. 클러스터의 인증 사용자 인증 정보를 가져오려면 다음 명령어를 실행합니다.

    gcloud container clusters get-credentials llm-cluster \
        --region=$REGION \
        --project=$PROJECT_ID
    
  6. 배포의 네임스페이스를 만듭니다. Cloud Shell에서 다음 명령어를 실행합니다.

    kubectl create ns llm
    

스탠더드

  1. Cloud Shell에서 기본 환경 변수를 설정합니다.

    export HF_TOKEN=HF_TOKEN
    export REGION=REGION
    export CLUSTER_NAME="llm-cluster"
    export GPU_POOL_MACHINE_TYPE="g2-standard-24"
    export GPU_POOL_ACCELERATOR_TYPE="nvidia-l4"
    export PROJECT_NUMBER=$(gcloud projects list \
        --filter="$(gcloud config get-value project)" \
        --format="value(PROJECT_NUMBER)")
    export MODEL_BUCKET="model-bucket-$PROJECT_ID"
    

    다음 값을 바꿉니다.

    • HF_TOKEN: 이전에 생성한 Hugging Face 토큰입니다.
    • REGION: 사용하려는 가속기 유형을 지원하는 리전입니다(예: L4 GPU의 경우 us-central1).

    다음 변수를 조정할 수 있습니다.

    • GPU_POOL_MACHINE_TYPE: 선택한 리전에서 사용할 노드 풀 머신 시리즈입니다. 이 값은 선택한 가속기 유형에 따라 다릅니다. 자세한 내용은 GKE에서 GPU를 사용할 때의 제한사항을 참고하세요. 예를 들어 이 튜토리얼에서는 노드당 GPU 2개가 연결된 g2-standard-24를 사용합니다. 사용 가능한 GPU의 최신 목록은 컴퓨팅 워크로드용 GPU를 참고하세요.
    • GPU_POOL_ACCELERATOR_TYPE: 선택한 리전에서 지원되는 가속기 유형입니다. 예를 들어 이 튜토리얼에서는 nvidia-l4을 사용합니다. 사용 가능한 최신 GPU 목록은 컴퓨팅 워크로드용 GPU를 참고하세요.
    • MODEL_BUCKET: 학습된 모델 가중치를 저장하는 Cloud Storage 버킷입니다.
  2. 표준 클러스터 만들기

    gcloud container clusters create ${CLUSTER_NAME} \
        --project=${PROJECT_ID} \
        --region=${REGION} \
        --workload-pool=${PROJECT_ID}.svc.id.goog \
        --release-channel=rapid \
        --machine-type=e2-standard-4 \
        --addons GcsFuseCsiDriver \
        --num-nodes=1
    
  3. 추론 및 미세 조정 워크로드용 GPU 노드 풀을 만듭니다.

    gcloud container node-pools create gpupool \
        --accelerator type=${GPU_POOL_ACCELERATOR_TYPE},count=2,gpu-driver-version=latest \
        --project=${PROJECT_ID} \
        --location=${REGION} \
        --node-locations=${REGION}-a \
        --cluster=${CLUSTER_NAME} \
        --machine-type=${GPU_POOL_MACHINE_TYPE} \
        --num-nodes=3
    
  4. 미세 조정 작업을 위한 Cloud Storage 버킷을 만듭니다.

    gcloud storage buckets create gs://${MODEL_BUCKET} \
        --location ${REGION} \
        --uniform-bucket-level-access
    
  5. Cloud Storage 버킷에 대한 액세스 권한을 부여하려면 다음 명령어를 실행합니다.

    gcloud storage buckets add-iam-policy-binding "gs://$MODEL_BUCKET" \
        --role=roles/storage.objectAdmin \
        --member=principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$PROJECT_ID.svc.id.goog/subject/ns/llm/sa/default \
        --condition=None
    
  6. 클러스터의 인증 사용자 인증 정보를 가져오려면 다음 명령어를 실행합니다.

    gcloud container clusters get-credentials llm-cluster \
        --region=$REGION \
        --project=$PROJECT_ID
    
  7. 배포의 네임스페이스를 만듭니다. Cloud Shell에서 다음 명령어를 실행합니다.

    kubectl create ns llm
    

Hugging Face 사용자 인증 정보용 Kubernetes 보안 비밀 만들기

Hugging Face 토큰이 포함된 Kubernetes 보안 비밀을 만들려면 다음 명령어를 실행합니다.

kubectl create secret generic hf-secret \
    --from-literal=hf_api_token=$HF_TOKEN \
    --dry-run=client -o yaml | kubectl apply --namespace=llm --filename=-

Kueue 구성

이 튜토리얼에서 Kueue는 학습 워크로드와 제공 워크로드 간에 GPU를 효율적으로 공유할 수 있는 중앙 리소스 관리자입니다. Kueue는 리소스 요구사항 ('버전')을 정의하고, 큐를 통해 워크로드의 우선순위를 지정 (학습보다 게재 작업이 우선순위가 높음)하며, 수요와 우선순위에 따라 리소스를 동적으로 할당하여 이를 실행합니다. 이 튜토리얼에서는 워크로드 리소스 유형을 사용하여 추론 워크로드와 미세 조정 워크로드를 각각 그룹화합니다.

Kueue의 선점 기능은 리소스가 부족할 때 우선순위가 낮은 학습 작업을 일시중지하거나 제거하여 우선순위가 높은 게재 워크로드에 항상 필요한 리소스가 있도록 합니다.

Kueue로 추론 서버 배포를 제어하려면 Kustomize를 사용하여 맞춤 구성을 적용하여 v1/pod 통합을 사용 설정하여 서버 포드에 "kueue-job: true" 라벨을 지정합니다.

  1. /kueue 디렉터리에서 kustomization.yaml의 코드를 확인합니다. 이 매니페스트는 맞춤 구성으로 Kueue 리소스 관리자를 설치합니다.

    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    resources:
    - https://github.com/kubernetes-sigs/kueue/releases/download/v0.10.0/manifests.yaml
    patches:
    - path: patch.yaml
      target:
        version: v1
        kind: ConfigMap
        name: kueue-manager-config
    
  2. /kueue 디렉터리에서 patch.yaml의 코드를 확인합니다. 이 ConfigMap은 "kueue-job: true" 라벨이 있는 포드를 관리하도록 Kueue를 맞춤설정합니다.

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: kueue-manager-config
    data:
      controller_manager_config.yaml: |
        apiVersion: config.kueue.x-k8s.io/v1beta1
        kind: Configuration
        health:
          healthProbeBindAddress: :8081
        metrics:
          bindAddress: :8080
        # enableClusterQueueResources: true
        webhook:
          port: 9443
        leaderElection:
          leaderElect: true
          resourceName: c1f6bfd2.kueue.x-k8s.io
        controller:
          groupKindConcurrency:
            Job.batch: 5
            Pod: 5
            Workload.kueue.x-k8s.io: 5
            LocalQueue.kueue.x-k8s.io: 1
            ClusterQueue.kueue.x-k8s.io: 1
            ResourceFlavor.kueue.x-k8s.io: 1
        clientConnection:
          qps: 50
          burst: 100
        #pprofBindAddress: :8083
        #waitForPodsReady:
        #  enable: false
        #  timeout: 5m
        #  blockAdmission: false
        #  requeuingStrategy:
        #    timestamp: Eviction
        #    backoffLimitCount: null # null indicates infinite requeuing
        #    backoffBaseSeconds: 60
        #    backoffMaxSeconds: 3600
        #manageJobsWithoutQueueName: true
        #internalCertManagement:
        #  enable: false
        #  webhookServiceName: ""
        #  webhookSecretName: ""
        integrations:
          frameworks:
          - "batch/job"
          - "kubeflow.org/mpijob"
          - "ray.io/rayjob"
          - "ray.io/raycluster"
          - "jobset.x-k8s.io/jobset"
          - "kubeflow.org/mxjob"
          - "kubeflow.org/paddlejob"
          - "kubeflow.org/pytorchjob"
          - "kubeflow.org/tfjob"
          - "kubeflow.org/xgboostjob"
          - "pod"
        #  externalFrameworks:
        #  - "Foo.v1.example.com"
          podOptions:
            # You can change namespaceSelector to define in which 
            # namespaces kueue will manage the pods.
            namespaceSelector:
              matchExpressions:
              - key: kubernetes.io/metadata.name
                operator: NotIn
                values: [ kube-system, kueue-system ]
            # Kueue uses podSelector to manage pods with particular 
            # labels. The default podSelector will match all the pods. 
            podSelector:
              matchExpressions:
              - key: kueue-job
                operator: In
                values: [ "true", "True", "yes" ]
    
  3. Cloud Shell에서 다음 명령어를 실행하여 Kueue를 설치합니다.

    cd ${EXAMPLE_HOME}
    kubectl kustomize kueue |kubectl apply --server-side --filename=-
    

    Kueue 포드가 준비될 때까지 기다립니다.

    watch kubectl --namespace=kueue-system get pods
    

    출력은 다음과 비슷하게 표시됩니다.

    NAME                                        READY   STATUS    RESTARTS   AGE
    kueue-controller-manager-bdc956fc4-vhcmx    2/2     Running   0          3m15s
    
  4. /workloads 디렉터리에서 flavors.yaml, cluster-queue.yaml, local-queue.yaml 파일을 봅니다. 이러한 매니페스트는 Kueue가 리소스 할당량을 관리하는 방법을 지정합니다.

    ResourceFlavor

    이 매니페스트는 리소스 관리를 위해 Kueue에서 기본 ResourceFlavor를 정의합니다.

    apiVersion: kueue.x-k8s.io/v1beta1
    kind: ResourceFlavor
    metadata:
      name: default-flavor
    

    ClusterQueue

    이 매니페스트는 CPU, 메모리, GPU의 리소스 제한이 있는 큐 ClusterQueue를 설정합니다.

    이 튜토리얼에서는 2개의 Nvidia L4 GPU가 연결된 노드를 사용하며, 해당 노드 유형은 g2-standard-24이며 24개의 vCPU와 96GB의 RAM을 제공합니다. 이 코드 예에서는 워크로드의 리소스 사용량을 최대 6개의 GPU로 제한하는 방법을 보여줍니다.

    ClusterQueue 구성의 preemption 필드는 PriorityClasses를 참조하여 리소스가 부족할 때 선점할 수 있는 포드를 결정합니다.

    apiVersion: kueue.x-k8s.io/v1beta1
    kind: ClusterQueue
    metadata:
      name: "cluster-queue"
    spec:
      namespaceSelector: {} # match all.
      preemption:
        reclaimWithinCohort: LowerPriority
        withinClusterQueue: LowerPriority
      resourceGroups:
      - coveredResources: [ "cpu", "memory", "nvidia.com/gpu", "ephemeral-storage" ]
        flavors:
        - name: default-flavor
          resources:
          - name: "cpu"
            nominalQuota: 72
          - name: "memory"
            nominalQuota: 288Gi
          - name: "nvidia.com/gpu"
            nominalQuota: 6
          - name: "ephemeral-storage"
            nominalQuota: 200Gi
    

    LocalQueue

    이 매니페스트는 llm 네임스페이스에 lq라는 LocalQueue 큐를 만듭니다.

    apiVersion: kueue.x-k8s.io/v1beta1
    kind: LocalQueue
    metadata:
      namespace: llm # LocalQueue under llm namespace 
      name: lq
    spec:
      clusterQueue: cluster-queue # Point to the ClusterQueue
    
  5. default-priorityclass.yaml, low-priorityclass.yaml, high-priorityclass.yaml 파일을 봅니다. 이러한 매니페스트는 Kubernetes 예약을 위한 PriorityClass 객체를 정의합니다.

    기본 우선순위

    apiVersion: scheduling.k8s.io/v1
    kind: PriorityClass
    metadata:
      name: default-priority-nonpreempting
    value: 10
    preemptionPolicy: Never
    globalDefault: true
    description: "This priority class will not cause other pods to be preempted."
    

    낮은 우선순위

    apiVersion: scheduling.k8s.io/v1
    kind: PriorityClass
    metadata:
      name: low-priority-preempting
    value: 20
    preemptionPolicy: PreemptLowerPriority
    globalDefault: false
    description: "This priority class will cause pods with lower priority to be preempted."
    

    높은 우선순위

    apiVersion: scheduling.k8s.io/v1
    kind: PriorityClass
    metadata:
      name: high-priority-preempting
    value: 30
    preemptionPolicy: PreemptLowerPriority
    globalDefault: false
    description: "This high priority class will cause other pods to be preempted."
    
  6. 다음 명령어를 실행하여 Kueue 및 Kubernetes 객체를 만들고 해당 매니페스트를 적용합니다.

    cd ${EXAMPLE_HOME}/workloads
    kubectl apply --filename=flavors.yaml
    kubectl apply --filename=default-priorityclass.yaml
    kubectl apply --filename=high-priorityclass.yaml
    kubectl apply --filename=low-priorityclass.yaml
    kubectl apply --filename=cluster-queue.yaml
    kubectl apply --filename=local-queue.yaml --namespace=llm
    

TGI 추론 서버 배포

이 섹션에서는 TGI 컨테이너를 배포하여 Gemma 2 모델을 제공합니다.

  1. /workloads 디렉터리에서 tgi-gemma-2-9b-it-hp.yaml 파일을 봅니다. 이 매니페스트는 TGI 제공 런타임 및 gemma-2-9B-it 모델을 배포하는 Kubernetes 배포를 정의합니다.

    배포는 추론 작업에 우선순위를 두고 모델에 GPU 2개를 사용합니다. NUM_SHARD 환경 변수를 설정하여 텐서 병렬 처리를 사용하여 모델을 GPU 메모리에 맞춥니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: tgi-gemma-deployment
      labels:
        app: gemma-server
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: gemma-server
      template:
        metadata:
          labels:
            app: gemma-server
            ai.gke.io/model: gemma-2-9b-it
            ai.gke.io/inference-server: text-generation-inference
            examples.ai.gke.io/source: user-guide
            kueue.x-k8s.io/queue-name: lq
            kueue-job: "true"
        spec:
          priorityClassName: high-priority-preempting
          containers:
          - name: inference-server
            image: us-docker.pkg.dev/deeplearning-platform-release/gcr.io/huggingface-text-generation-inference-cu121.2-1.ubuntu2204.py310
            resources:
              requests:
                cpu: "4"
                memory: "30Gi"
                ephemeral-storage: "30Gi"
                nvidia.com/gpu: "2"
              limits:
                cpu: "4"
                memory: "30Gi"
                ephemeral-storage: "30Gi"
                nvidia.com/gpu: "2"
            env:
            - name: AIP_HTTP_PORT
              value: '8000'
            - name: NUM_SHARD
              value: '2'
            - name: MODEL_ID
              value: google/gemma-2-9b-it
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_api_token
            volumeMounts:
            - mountPath: /dev/shm
              name: dshm
          volumes:
          - name: dshm
            emptyDir:
              medium: Memory
          nodeSelector:
            cloud.google.com/gke-accelerator: "nvidia-l4"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: llm-service
    spec:
      selector:
        app: gemma-server
      type: ClusterIP
      ports:
      - protocol: TCP
        port: 8000
        targetPort: 8000
    
  2. 다음 명령어를 실행하여 매니페스트를 적용합니다.

    kubectl apply --filename=tgi-gemma-2-9b-it-hp.yaml --namespace=llm
    

    배포 작업은 완료하는 데 몇 분 정도 걸립니다.

  3. GKE에서 배포를 성공적으로 만들었는지 확인하려면 다음 명령어를 실행합니다.

    kubectl --namespace=llm get deployment
    

    출력은 다음과 비슷하게 표시됩니다.

    NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
    tgi-gemma-deployment   1/1     1            1           5m13s
    

대기열 할당량 관리 확인

이 섹션에서는 Kueue가 배포의 GPU 할당량을 올바르게 적용하는지 확인합니다.

  1. Kueue가 배포를 인식하는지 확인하려면 다음 명령어를 실행하여 워크로드 객체의 상태를 가져옵니다.

    kubectl --namespace=llm get workloads
    

    출력은 다음과 비슷하게 표시됩니다.

    NAME                                              QUEUE   RESERVED IN     ADMITTED   FINISHED   AGE
    pod-tgi-gemma-deployment-6bf9ffdc9b-zcfrh-84f19   lq      cluster-queue   True                  8m23s
    
  2. 할당량 한도 재정의를 테스트하려면 배포를 4개의 복제본으로 확장합니다.

    kubectl scale --replicas=4 deployment/tgi-gemma-deployment --namespace=llm
    
  3. 다음 명령어를 실행하여 GKE에서 배포하는 복제본 수를 확인합니다.

    kubectl get workloads --namespace=llm
    

    출력은 다음과 비슷하게 표시됩니다.

    NAME                                              QUEUE   RESERVED IN     ADMITTED   FINISHED   AGE
    pod-tgi-gemma-deployment-6cb95cc7f5-5thgr-3f7d4   lq      cluster-queue   True                  14s
    pod-tgi-gemma-deployment-6cb95cc7f5-cbxg2-d9fe7   lq      cluster-queue   True                  5m41s
    pod-tgi-gemma-deployment-6cb95cc7f5-tznkl-80f6b   lq                                            13s
    pod-tgi-gemma-deployment-6cb95cc7f5-wd4q9-e4302   lq      cluster-queue   True                  13s
    

    출력에는 Kueue에서 적용하는 리소스 할당량으로 인해 포드 3개만 허용되었다고 표시됩니다.

  4. 다음을 실행하여 llm 네임스페이스에 있는 포드를 표시합니다.

    kubectl get pod --namespace=llm
    

    출력은 다음과 비슷하게 표시됩니다.

    NAME                                    READY   STATUS            RESTARTS   AGE
    tgi-gemma-deployment-7649884d64-6j256   1/1     Running           0          4m45s
    tgi-gemma-deployment-7649884d64-drpvc   0/1     SchedulingGated   0          7s
    tgi-gemma-deployment-7649884d64-thdkq   0/1     Pending           0          7s
    tgi-gemma-deployment-7649884d64-znvpb   0/1     Pending           0          7s
    
  5. 이제 배포를 다시 1로 축소합니다. 이 단계는 미세 조정 작업을 배포하기 전에 필요합니다. 그러지 않으면 추론 작업이 우선순위가 높기 때문에 허용되지 않습니다.

    kubectl scale --replicas=1 deployment/tgi-gemma-deployment --namespace=llm
    

동작 설명

확장 예시에서는 ClusterQueue 구성에서 설정한 GPU 할당량 제한으로 인해 4개로 확장되었음에도 불구하고 3개의 복제본만 생성됩니다. ClusterQueue의 spec.resourceGroups 섹션은 nvidia.com/gpu의 nominalQuota를 '6'으로 정의합니다. 배포에서 각 Pod에 '2'개의 GPU가 필요하다고 지정합니다. 따라서 ClusterQueue는 한 번에 최대 3개의 배포 복제본만 수용할 수 있습니다 (복제본 3개 * 복제본당 GPU 2개 = 총 할당량인 GPU 6개).

복제본 4개로 확장하려고 하면 Kueue는 이 작업이 GPU 할당량을 초과한다는 것을 인식하고 네 번째 복제본이 예약되지 않도록 합니다. 이는 네 번째 Pod의 SchedulingGated 상태로 표시됩니다. 이 동작은 Kueue의 리소스 할당량 적용을 보여줍니다.

학습 작업 배포

이 섹션에서는 두 포드에 GPU 4개가 필요한 Gemma 2 모델에 우선순위가 낮은 미세 조정 작업을 배포합니다. 이 작업은 ClusterQueue의 남은 GPU 할당량을 사용합니다. 작업은 사전 빌드된 이미지를 사용하고 중간 결과에서 다시 시작할 수 있도록 체크포인트를 저장합니다.

미세 조정 작업은 b-mc2/sql-create-context 데이터 세트를 사용합니다. 미세 조정 작업의 소스는 저장소에서 확인할 수 있습니다.

  1. fine-tune-l4.yaml 파일을 봅니다. 이 매니페스트는 미세 조정 작업을 정의합니다.

    apiVersion: v1
    kind: Service
    metadata:
      name: headless-svc-l4
    spec:
      clusterIP: None # clusterIP must be None to create a headless service
      selector:
        job-name: finetune-gemma-l4 # must match Job name
    ---
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: finetune-gemma-l4
      labels:
        kueue.x-k8s.io/queue-name: lq
    spec:
      backoffLimit: 4
      completions: 2
      parallelism: 2
      completionMode: Indexed
      suspend: true # Set to true to allow Kueue to control the Job when it starts
      template:
        metadata:
          labels:
            app: finetune-job
          annotations:
            gke-gcsfuse/volumes: "true"
            gke-gcsfuse/memory-limit: "35Gi"
        spec:
          priorityClassName: low-priority-preempting
          containers:
          - name: gpu-job
            imagePullPolicy: Always
            image: us-docker.pkg.dev/google-samples/containers/gke/gemma-fine-tuning:v1.0.0
            ports:
            - containerPort: 29500
            resources:
              requests:
                nvidia.com/gpu: "2"
              limits:
                nvidia.com/gpu: "2"
            command:
            - bash
            - -c
            - |
              accelerate launch \
              --config_file fsdp_config.yaml \
              --debug \
              --main_process_ip finetune-gemma-l4-0.headless-svc-l4 \
              --main_process_port 29500 \
              --machine_rank ${JOB_COMPLETION_INDEX} \
              --num_processes 4 \
              --num_machines 2 \
              fine_tune.py
            env:
            - name: "EXPERIMENT"
              value: "finetune-experiment"
            - name: MODEL_NAME
              value: "google/gemma-2-2b"
            - name: NEW_MODEL
              value: "gemma-ft"
            - name: MODEL_PATH
              value: "/model-data/model-gemma2/experiment"
            - name: DATASET_NAME
              value: "b-mc2/sql-create-context"
            - name: DATASET_LIMIT
              value: "5000"
            - name: EPOCHS
              value: "1"
            - name: GRADIENT_ACCUMULATION_STEPS
              value: "2"
            - name: CHECKPOINT_SAVE_STEPS
              value: "10"
            - name: HF_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_api_token
            volumeMounts:
            - mountPath: /dev/shm
              name: dshm
            - name: gcs-fuse-csi-ephemeral
              mountPath: /model-data
              readOnly: false
          nodeSelector:
            cloud.google.com/gke-accelerator: nvidia-l4
          restartPolicy: OnFailure
          serviceAccountName: default
          subdomain: headless-svc-l4
          terminationGracePeriodSeconds: 60
          volumes:
          - name: dshm
            emptyDir:
              medium: Memory
          - name: gcs-fuse-csi-ephemeral
            csi:
              driver: gcsfuse.csi.storage.gke.io
              volumeAttributes:
                bucketName: <MODEL_BUCKET>
                mountOptions: "implicit-dirs"
                gcsfuseLoggingSeverity: warning
    
  2. 매니페스트를 적용하여 미세 조정 작업을 만듭니다.

    cd ${EXAMPLE_HOME}/workloads
    
    sed -e "s/<MODEL_BUCKET>/$MODEL_BUCKET/g" \
        -e "s/<PROJECT_ID>/$PROJECT_ID/g" \
        -e "s/<REGION>/$REGION/g" \
        fine-tune-l4.yaml |kubectl apply --filename=- --namespace=llm
    
  3. 배포가 실행 중인지 확인합니다. 워크로드 객체의 상태를 확인하려면 다음 명령어를 실행합니다.

    kubectl get workloads --namespace=llm
    

    출력은 다음과 비슷하게 표시됩니다.

    NAME                                              QUEUE   RESERVED IN     ADMITTED   FINISHED   AGE
    job-finetune-gemma-l4-3316f                       lq      cluster-queue   True                  29m
    pod-tgi-gemma-deployment-6cb95cc7f5-cbxg2-d9fe7   lq      cluster-queue   True                  68m
    

    다음 명령어를 실행하여 llm 네임스페이스의 포드를 확인합니다.

    kubectl get pod --namespace=llm
    

    출력은 다음과 비슷하게 표시됩니다.

    NAME                                    READY   STATUS    RESTARTS   AGE
    finetune-gemma-l4-0-vcxpz               2/2     Running   0          31m
    finetune-gemma-l4-1-9ppt9               2/2     Running   0          31m
    tgi-gemma-deployment-6cb95cc7f5-cbxg2   1/1     Running   0          70m
    

    출력에서 Kueue는 미세 조정 작업과 추론 서버 포드가 모두 실행되도록 허용하고 지정된 할당량 한도에 따라 올바른 리소스를 예약합니다.

  4. 출력 로그를 확인하여 미세 조정 작업이 Cloud Storage 버킷에 체크포인트를 저장하는지 확인합니다. 미세 조정 작업이 첫 번째 체크포인트 저장을 시작하기까지 약 10분이 걸립니다.

    kubectl logs --namespace=llm --follow --selector=app=finetune-job
    

    첫 번째로 저장된 체크포인트의 출력은 다음과 유사합니다.

    {"name": "finetune", "thread": 133763559483200, "threadName": "MainThread", "processName": "MainProcess", "process": 33, "message": "Fine tuning started", "timestamp": 1731002351.0016131, "level": "INFO", "runtime": 451579.89835739136}
    …
    {"name": "accelerate.utils.fsdp_utils", "thread": 136658669348672, "threadName": "MainThread", "processName": "MainProcess", "process": 32, "message": "Saving model to /model-data/model-gemma2/experiment/checkpoint-10/pytorch_model_fsdp_0", "timestamp": 1731002386.1763802, "level": "INFO", "runtime": 486753.8924217224}
    

혼합 워크로드에서 Kueue 선점 및 동적 할당 테스트

이 섹션에서는 추론 서버의 부하가 증가하여 확장이 필요한 시나리오를 시뮬레이션합니다. 이 시나리오는 리소스가 제약된 경우 Kueue가 우선순위가 낮은 미세 조정 작업을 일시중지하고 선점하여 우선순위가 높은 추론 서버에 우선순위를 두는 방법을 보여줍니다.

  1. 다음 명령어를 실행하여 추론 서버의 복제본을 2개로 확장합니다.

    kubectl scale --replicas=2 deployment/tgi-gemma-deployment --namespace=llm
    
  2. 워크로드 객체의 상태를 확인합니다.

    kubectl get workloads --namespace=llm
    

    결과는 다음과 유사합니다.

    NAME                                              QUEUE   RESERVED IN     ADMITTED   FINISHED   AGE
    job-finetune-gemma-l4-3316f                       lq                      False                 32m
    pod-tgi-gemma-deployment-6cb95cc7f5-cbxg2-d9fe7   lq      cluster-queue   True                  70m
    pod-tgi-gemma-deployment-6cb95cc7f5-p49sh-167de   lq      cluster-queue   True                  14s
    

    출력에서 증가된 추론 서버 복제본이 사용 가능한 GPU 할당량을 사용하고 있으므로 미세 조정 작업이 더 이상 허용되지 않음을 알 수 있습니다.

  3. 미세 조정 작업의 상태를 확인합니다.

    kubectl get job --namespace=llm
    

    출력은 다음과 유사하며, 미세 조정 작업 상태가 정지되었음을 나타냅니다.

    NAME                STATUS      COMPLETIONS   DURATION   AGE
    finetune-gemma-l4   Suspended   0/2                      33m
    
  4. 다음 명령어를 실행하여 포드를 검사합니다.

    kubectl get pod --namespace=llm
    

    출력은 다음과 유사하며, 이는 Kueue가 우선순위가 더 높은 추론 서버 배포를 위해 리소스를 확보하기 위해 미세 조정 작업 Pod를 종료했음을 나타냅니다.

    NAME                                    READY   STATUS              RESTARTS   AGE
    tgi-gemma-deployment-6cb95cc7f5-cbxg2   1/1     Running             0          72m
    tgi-gemma-deployment-6cb95cc7f5-p49sh   0/1     ContainerCreating   0          91s
    
  5. 다음으로 추론 서버 부하가 감소하고 Pod가 축소되는 시나리오를 테스트합니다. 다음 명령어를 실행합니다.

    kubectl scale --replicas=1 deployment/tgi-gemma-deployment --namespace=llm
    

    다음 명령어를 실행하여 워크로드 객체를 표시합니다.

    kubectl get workloads --namespace=llm
    

    출력은 다음과 유사하며, 이는 추론 서버 배포 중 하나가 종료되고 미세 조정 작업이 다시 허용되었음을 나타냅니다.

    NAME                                              QUEUE   RESERVED IN     ADMITTED   FINISHED   AGE
    job-finetune-gemma-l4-3316f                       lq      cluster-queue   True                  37m
    pod-tgi-gemma-deployment-6cb95cc7f5-cbxg2-d9fe7   lq      cluster-queue   True                  75m
    
  6. 다음 명령어를 실행하여 작업을 표시합니다.

    kubectl get job --namespace=llm
    

    출력은 다음과 유사하며, 이는 미세 조정 작업이 다시 실행되고 사용 가능한 최신 체크포인트에서 다시 시작되었음을 나타냅니다.

    NAME                STATUS    COMPLETIONS   DURATION   AGE
    finetune-gemma-l4   Running   0/2           2m11s      38m
    

삭제

이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.

배포된 리소스 삭제

이 가이드에서 만든 리소스에 대한 Google Cloud 계정에 요금이 청구되지 않도록 하려면 다음 명령어를 실행합니다.

gcloud storage rm --recursive gs://${MODEL_BUCKET}
gcloud container clusters delete ${CLUSTER_NAME} --location ${REGION}

다음 단계