Ottimizza l'utilizzo delle risorse GKE per i carichi di lavoro di addestramento e inferenza misti di AI/ML


Questo tutorial mostra come condividere in modo efficiente le risorse dell'acceleratore tra i carichi di lavoro di addestramento e di servizio di inferenza all'interno di un singolo cluster Google Kubernetes Engine (GKE). Distribuendo i carichi di lavoro misti su un singolo cluster, migliori l'utilizzo delle risorse, semplifichi la gestione del cluster, riduci i problemi derivanti dalle limitazioni della quantità di acceleratori e aumenti l'efficacia in termini di costi complessiva.

In questo tutorial, crei un deployment di serving ad alta priorità utilizzando il modello linguistico di grandi dimensioni (LLM) Gemma 2 per l'inferenza e il framework di serving Hugging Face TGI (Text Generation Interface), insieme a un job di perfezionamento dell'LLM a bassa priorità. Entrambi i carichi di lavoro vengono eseguiti su un singolo cluster che utilizza GPU NVIDIA L4. Utilizzi Kueue, un sistema di gestione delle code dei job open source nativo di Kubernetes, per gestire e pianificare i tuoi carichi di lavoro. Kueue ti consente di dare la priorità alle attività di pubblicazione e di interrompere i job di addestramento con priorità inferiore per ottimizzare l'utilizzo delle risorse. Man mano che le richieste di pubblicazione diminuiscono, riassegni gli acceleratori liberati per riprendere l'addestramento dei job. Utilizzi Kueue e le classi di priorità per gestire le quote di risorse durante l'intero processo.

Questo tutorial è rivolto a machine learning engineer, amministratori e operatori di piattaforme e specialisti di dati e AI che vogliono addestrare e ospitare un modello di machine learning (ML) su un cluster GKE e che vogliono anche ridurre i costi e il sovraccarico di gestione, soprattutto quando si ha a che fare con un numero limitato di acceleratori. Per scoprire di più sui ruoli comuni e sulle attività di esempio a cui facciamo riferimento nei contenuti di Google Cloud , consulta la pagina Ruoli e attività comuni degli utenti GKE.

Prima di leggere questa pagina, assicurati di avere familiarità con quanto segue:

Obiettivi

Al termine di questa guida, dovresti essere in grado di eseguire i seguenti passaggi:

  • Configura un'implementazione di deployment ad alta priorità.
  • Configura job di addestramento con priorità inferiore.
  • Implementa strategie di preemptive per far fronte alla domanda variabile.
  • Gestisci l'allocazione delle risorse tra le attività di addestramento e produzione utilizzando Kueue.

Prima di iniziare

  • 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

  • Verify 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

  • Verify 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.

      Vai a IAM
    2. Seleziona il progetto.
    3. Fai clic su Concedi l'accesso.
    4. Nel campo Nuove entità, inserisci il tuo identificatore dell'utente. In genere si tratta dell'indirizzo email di un Account Google.

    5. Nell'elenco Seleziona un ruolo, seleziona un ruolo.
    6. Per concedere altri ruoli, fai clic su Aggiungi un altro ruolo e aggiungi ogni ruolo aggiuntivo.
    7. Fai clic su Salva.
    8. Prepara l'ambiente

      In questa sezione, esegui il provisioning delle risorse necessarie per il deployment di TGI e del modello per i carichi di lavoro di inferenza e addestramento.

      Ottenere l'accesso al modello

      Per accedere ai modelli Gemma per il deployment su GKE, devi prima firmare il contratto di consenso alla licenza, poi generare un token di accesso Hugging Face.

      1. Firma il contratto di consenso alla licenza. Accedi alla pagina del consenso del modello, verifica il consenso utilizzando il tuo account Hugging Face e accetta i termini del modello.
      2. Genera un token di accesso. Per accedere al modello tramite Hugging Face, devi avere un token Hugging Face. Segui questi passaggi per generare un nuovo token se non ne hai già uno:

        1. Fai clic su Il tuo profilo > Impostazioni > Token di accesso.
        2. Seleziona Nuovo token.
        3. Specifica un nome a tua scelta e un ruolo di almeno Read.
        4. Seleziona Genera un token.
        5. Copia il token generato negli appunti.

      Avvia Cloud Shell

      In questo tutorial utilizzerai Cloud Shell per gestire le risorse ospitate su Google Cloud. Cloud Shell include il software necessario per questo tutorial, tra cui kubectl, gcloud CLI e Terraform.

      Per configurare l'ambiente con Cloud Shell:

      1. Nella console Google Cloud , avvia una sessione di Cloud Shell facendo clic su Icona di attivazione di Cloud Shell Attiva Cloud Shell nella Google Cloud console. Viene avviata una sessione nel riquadro inferiore della console Google Cloud .

      2. Imposta le variabili di ambiente predefinite:

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

        Sostituisci PROJECT_ID con l' Google Cloud ID progetto.

      3. Clona il codice campione da GitHub. In Cloud Shell, esegui questi comandi:

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

      Crea un cluster GKE

      Puoi utilizzare un cluster Autopilot o Standard per i tuoi carichi di lavoro misti. Ti consigliamo di utilizzare un cluster Autopilot per un'esperienza Kubernetes completamente gestita. Per scegliere la modalità operativa di GKE più adatta ai tuoi workload, consulta Scegliere una modalità operativa di GKE.

      Autopilot

      1. Imposta le variabili di ambiente predefinite in 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"
        

        Sostituisci i seguenti valori:

        • HF_TOKEN: il token Hugging Face che hai generato in precedenza.
        • REGION: una regione che supporta il tipo di acceleratore che vuoi utilizzare, ad esempio us-central1 per la GPU L4.

        Puoi modificare la variabile MODEL_BUCKET, che rappresenta il bucket Cloud Storage in cui memorizzi i pesi del modello addestrato.

      2. Crea un cluster Autopilot:

        gcloud container clusters create-auto ${CLUSTER_NAME} \
            --project=${PROJECT_ID} \
            --location=${REGION} \
            --release-channel=rapid
        
      3. Crea il bucket Cloud Storage per il job di perfezionamento:

        gcloud storage buckets create gs://${MODEL_BUCKET} \
            --location ${REGION} \
            --uniform-bucket-level-access
        
      4. Per concedere l'accesso al bucket Cloud Storage, esegui questo comando:

        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. Per ottenere le credenziali di autenticazione per il cluster, esegui questo comando:

        gcloud container clusters get-credentials llm-cluster \
            --location=$REGION \
            --project=$PROJECT_ID
        
      6. Crea uno spazio dei nomi per i tuoi deployment. In Cloud Shell, esegui questo comando:

        kubectl create ns llm
        

      Standard

      1. Imposta le variabili di ambiente predefinite in 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"
        

        Sostituisci i seguenti valori:

        • HF_TOKEN: il token Hugging Face che hai generato in precedenza.
        • REGION: la regione che supporta il tipo di acceleratore che vuoi utilizzare, ad esempio us-central1 per la GPU L4.

        Puoi modificare queste variabili:

        • GPU_POOL_MACHINE_TYPE: la serie di macchine del pool di nodi che vuoi utilizzare nella regione selezionata. Questo valore dipende dal tipo di acceleratore che hai selezionato. Per saperne di più, consulta Limitazioni dell'utilizzo delle GPU su GKE. Ad esempio, questo tutorial utilizza g2-standard-24 con due GPU collegate per nodo. Per l'elenco più aggiornato delle GPU disponibili, consulta GPU per i workload di computing.
        • GPU_POOL_ACCELERATOR_TYPE: il tipo di acceleratore supportato nella regione selezionata. Ad esempio, questo tutorial utilizza nvidia-l4. Per l'elenco più recente delle GPU disponibili, consulta GPU per i workload di computing.
        • MODEL_BUCKET: il bucket Cloud Storage in cui memorizzi i pesi del modello addestrato.
      2. Crea un cluster Standard:

        gcloud container clusters create ${CLUSTER_NAME} \
            --project=${PROJECT_ID} \
            --location=${REGION} \
            --workload-pool=${PROJECT_ID}.svc.id.goog \
            --release-channel=rapid \
            --machine-type=e2-standard-4 \
            --addons GcsFuseCsiDriver \
            --num-nodes=1
        
      3. Crea il pool di nodi GPU per i workload di inferenza e messa a punto:

        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. Crea il bucket Cloud Storage per il job di perfezionamento:

        gcloud storage buckets create gs://${MODEL_BUCKET} \
            --location ${REGION} \
            --uniform-bucket-level-access
        
      5. Per concedere l'accesso al bucket Cloud Storage, esegui questo comando:

        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. Per ottenere le credenziali di autenticazione per il cluster, esegui questo comando:

        gcloud container clusters get-credentials llm-cluster \
            --location=$REGION \
            --project=$PROJECT_ID
        
      7. Crea uno spazio dei nomi per i tuoi deployment. In Cloud Shell, esegui questo comando:

        kubectl create ns llm
        

      Crea un secret Kubernetes per le credenziali di Hugging Face

      Per creare un secret Kubernetes che contenga il token Hugging Face, esegui il seguente comando:

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

      Configura Kueue

      In questo tutorial, Kueue è il gestore delle risorse centrale, che consente la condivisione efficiente delle GPU tra i carichi di lavoro di addestramento e serving. Kueue raggiunge questo obiettivo definendo i requisiti delle risorse ("sapori"), dando la priorità ai carichi di lavoro tramite le code (con le attività di servizio che hanno la priorità rispetto all'addestramento) e allocando dinamicamente le risorse in base alla domanda e alla priorità. Questo tutorial utilizza il tipo di risorsa Workload per raggruppare rispettivamente i workload di inferenza e di perfezionamento.

      La funzionalità di preempt di Kueue garantisce che i workload di pubblicazione ad alta priorità abbiano sempre le risorse necessarie mettendo in pausa o eliminando i job di addestramento a priorità inferiore quando le risorse sono scarse.

      Per controllare il deployment del server di inferenza con Kueue, abilita l'integrazione di pod e configura managedJobsNamespaceSelector per escludere gli spazi dei nomi kube-system e kueue-system.

      1. Nella directory /kueue, visualizza il codice in kustomization.yaml. Questo manifest installa il gestore delle risorse Kueue con configurazioni personalizzate.

        apiVersion: kustomize.config.k8s.io/v1beta1
        kind: Kustomization
        resources:
        - https://github.com/kubernetes-sigs/kueue/releases/download/v0.12.3/manifests.yaml
        patches:
        - path: patch.yaml
          target:
            version: v1
            kind: ConfigMap
            name: kueue-manager-config
        
      2. Nella directory /kueue, visualizza il codice in patch.yaml. Questo oggetto ConfigMap personalizza Kueue per escludere la gestione dei pod negli spazi dei nomi kube-system e kueue-system.

        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
            managedJobsNamespaceSelector:
              matchExpressions:
                - key: kubernetes.io/metadata.name
                  operator: NotIn
                  values: [ kube-system, kueue-system ]
            #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/paddlejob"
              - "kubeflow.org/pytorchjob"
              - "kubeflow.org/tfjob"
              - "kubeflow.org/xgboostjob"
              - "kubeflow.org/jaxjob"
              - "workload.codeflare.dev/appwrapper"
              - "pod"
            #  - "deployment" # requires enabling pod integration
            #  - "statefulset" # requires enabling pod integration
            #  - "leaderworkerset.x-k8s.io/leaderworkerset" # requires enabling pod integration
            #  externalFrameworks:
            #  - "Foo.v1.example.com"
            #fairSharing:
            #  enable: true
            #  preemptionStrategies: [LessThanOrEqualToFinalShare, LessThanInitialShare]
            #admissionFairSharing:
            #  usageHalfLifeTime: "168h" # 7 days
            #  usageSamplingInterval: "5m"
            #  resourceWeights: # optional, defaults to 1 for all resources if not specified
            #    cpu: 0    # if you want to completely ignore cpu usage
            #    memory: 0 # ignore completely memory usage
            #    example.com/gpu: 100 # and you care only about GPUs usage
            #resources:
            #  excludeResourcePrefixes: []
            #  transformations:
            #  - input: nvidia.com/mig-4g.5gb
            #    strategy: Replace | Retain
            #    outputs:
            #      example.com/accelerator-memory: 5Gi
            #      example.com/accelerator-gpc: 4
            #objectRetentionPolicies:
            #  workloads:
            #    afterFinished: null # null indicates infinite retention, 0s means no retention at all
            #    afterDeactivatedByKueue: null # null indicates infinite retention, 0s means no retention at all
        
      3. In Cloud Shell, esegui questo comando per installare Kueue:

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

        Attendi che i pod Kueue siano pronti:

        watch kubectl --namespace=kueue-system get pods
        

        L'output dovrebbe essere simile al seguente:

        NAME                                        READY   STATUS    RESTARTS   AGE
        kueue-controller-manager-bdc956fc4-vhcmx    1/1     Running   0          3m15s
        
      4. Nella directory /workloads, visualizza i file flavors.yaml, cluster-queue.yaml e local-queue.yaml. Questi manifest specificano come Kueue gestisce le quote di risorse:

        ResourceFlavor

        Questo manifest definisce un ResourceFlavor predefinito in Kueue per la gestione delle risorse.

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

        ClusterQueue

        Questo manifest configura una ClusterQueue Kueue con limiti di risorse per CPU, memoria e GPU.

        Questo tutorial utilizza nodi con due GPU Nvidia L4 collegate, con il tipo di nodo corrispondente g2-standard-24, che offre 24 vCPU e 96 GB di RAM. Il codice di esempio mostra come limitare l'utilizzo delle risorse del tuo workload a un massimo di sei GPU.

        Il campo preemption nella configurazione di ClusterQueue fa riferimento alle PriorityClass per determinare quali pod possono essere preempted quando le risorse sono scarse.

        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

        Questo manifest crea una LocalQueue di Kueue denominata lq nello spazio dei nomi llm.

        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. Visualizza i file default-priorityclass.yaml, low-priorityclass.yaml e high-priorityclass.yaml. Questi manifest definiscono gli oggetti PriorityClass per la pianificazione di Kubernetes.

        Priorità predefinita

        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."
        

        Priorità bassa

        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."
        

        Priorità elevata

        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. Crea gli oggetti Kueue e Kubernetes eseguendo questi comandi per applicare i manifest corrispondenti.

        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
        

      Esegui il deployment del server di inferenza TGI

      In questa sezione, esegui il deployment del container TGI per pubblicare il modello Gemma 2.

      1. Nella directory /workloads, visualizza il file tgi-gemma-2-9b-it-hp.yaml. Questo manifest definisce un deployment Kubernetes per eseguire il deployment del runtime di pubblicazione TGI e del modello gemma-2-9B-it. Un deployment è un oggetto API Kubernetes che consente di eseguire più repliche di pod distribuite tra i nodi di un cluster.

        Il deployment assegna la priorità alle attività di inferenza e utilizza due GPU per il modello. Utilizza il parallelismo dei tensori, impostando la variabile di ambiente NUM_SHARD, per adattare il modello alla memoria 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
            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. Applica il manifest eseguendo questo comando:

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

        Il completamento dell'operazione di deployment richiede alcuni minuti.

      3. Per verificare se GKE ha creato correttamente il deployment, esegui questo comando:

        kubectl --namespace=llm get deployment
        

        L'output dovrebbe essere simile al seguente:

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

      Verifica la gestione delle quote di Kueue

      In questa sezione, confermi che Kueue applichi correttamente la quota GPU per il tuo deployment.

      1. Per verificare se Kueue è a conoscenza del tuo deployment, esegui questo comando per recuperare lo stato degli oggetti Workload:

        kubectl --namespace=llm get workloads
        

        L'output dovrebbe essere simile al seguente:

        NAME                                              QUEUE   RESERVED IN     ADMITTED   FINISHED   AGE
        pod-tgi-gemma-deployment-6bf9ffdc9b-zcfrh-84f19   lq      cluster-queue   True                  8m23s
        
      2. Per testare l'override dei limiti di quota, scala il deployment a quattro repliche:

        kubectl scale --replicas=4 deployment/tgi-gemma-deployment --namespace=llm
        
      3. Esegui questo comando per visualizzare il numero di repliche che GKE deploy:

        kubectl get workloads --namespace=llm
        

        L'output dovrebbe essere simile al seguente:

        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
        

        L'output mostra che vengono ammessi solo tre pod a causa della quota di risorse applicata da Kueue.

      4. Esegui questo comando per visualizzare i pod nello spazio dei nomi llm:

        kubectl get pod --namespace=llm
        

        L'output dovrebbe essere simile al seguente:

        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. Ora fare lo scale down il deployment a 1. Questo passaggio è obbligatorio prima di eseguire il deployment del job di ottimizzazione, altrimenti non verrà ammesso perché il job di inferenza ha la priorità.

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

      Spiegazione del comportamento

      L'esempio di scalabilità genera solo tre repliche (nonostante la scalabilità a quattro) a causa del limite di quota GPU impostato nella configurazione di ClusterQueue. La sezione ClusterQueue's spec.resourceGroups definisce una quota nominale di "6" per nvidia.com/gpu. Il deployment specifica che ogni pod richiede "2" GPU. Pertanto, ClusterQueue può ospitare al massimo tre repliche del deployment alla volta (poiché 3 repliche * 2 GPU per replica = 6 GPU, che è la quota totale).

      Quando tenti di scalare a quattro repliche, Kueue riconosce che questa azione supererebbe la quota GPU e impedisce la pianificazione della quarta replica. Ciò è indicato dallo stato SchedulingGated del quarto pod. Questo comportamento dimostra l'applicazione della quota di risorse di Kueue.

      Esegui il deployment del job di addestramento

      In questa sezione, esegui il deployment di un job di fine tuning a priorità inferiore per un modello Gemma 2 che richiede quattro GPU in due pod. Un controller Job in Kubernetes crea uno o più pod e assicura che eseguano correttamente un'attività specifica.

      Questo job utilizzerà la quota GPU rimanente in ClusterQueue. Il job utilizza un'immagine predefinita e salva i checkpoint per consentire il riavvio dai risultati intermedi.

      Il job di perfezionamento utilizza il set di dati b-mc2/sql-create-context. L'origine del job di ottimizzazione si trova nel repository.

      1. Visualizza il file fine-tune-l4.yaml. Questo manifest definisce il job di perfezionamento.

        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. Applica il manifest per creare il job di perfezionamento:

        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. Verifica che i deployment siano in esecuzione. Per controllare lo stato degli oggetti Workload, esegui questo comando:

        kubectl get workloads --namespace=llm
        

        L'output dovrebbe essere simile al seguente:

        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
        

        Successivamente, visualizza i pod nello spazio dei nomi llm eseguendo questo comando:

        kubectl get pod --namespace=llm
        

        L'output dovrebbe essere simile al seguente:

        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
        

        L'output mostra che Kueue consente l'esecuzione sia del job di perfezionamento sia dei pod server di inferenza, riservando le risorse corrette in base ai limiti di quota specificati.

      4. Visualizza i log di output per verificare che il job di perfezionamento salvi i checkpoint nel bucket Cloud Storage. Il job di perfezionamento richiede circa 10 minuti prima di iniziare a salvare il primo checkpoint.

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

        L'output del primo checkpoint salvato è simile al seguente:

        {"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}
        

      Testa la preemption e l'allocazione dinamica di Kueue sul tuo workload misto

      In questa sezione simulerai uno scenario in cui il carico del server di inferenza aumenta, richiedendo lo scale up. Questo scenario mostra come Kueue assegna la priorità al server di inferenza ad alta priorità sospendendo e interrompendo il job di perfezionamento a priorità più bassa quando le risorse sono limitate.

      1. Esegui questo comando per scalare le repliche del server di inferenza a due:

        kubectl scale --replicas=2 deployment/tgi-gemma-deployment --namespace=llm
        
      2. Controlla lo stato degli oggetti Workload:

        kubectl get workloads --namespace=llm
        

        L'output è simile al seguente:

        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
        

        L'output mostra che il job di perfezionamento non è più ammesso perché le repliche del server di inferenza aumentate utilizzano la quota GPU disponibile.

      3. Controlla lo stato del job di perfezionamento:

        kubectl get job --namespace=llm
        

        L'output è simile al seguente, il che indica che lo stato del job di perfezionamento è ora sospeso:

        NAME                STATUS      COMPLETIONS   DURATION   AGE
        finetune-gemma-l4   Suspended   0/2                      33m
        
      4. Esegui questo comando per controllare i pod:

        kubectl get pod --namespace=llm
        

        L'output è simile al seguente, il che indica che Kueue ha terminato i pod di job di perfezionamento per liberare risorse per il deployment del server di inferenza con priorità più alta.

        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. Successivamente, testa lo scenario in cui il carico del server di inferenza diminuisce e i relativi pod vengono ridimensionati. Esegui questo comando:

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

        Esegui questo comando per visualizzare gli oggetti Workload:

        kubectl get workloads --namespace=llm
        

        L'output è simile al seguente, il che indica che uno dei deployment del server di inferenza è terminato e il job di perfezionamento è riammesso.

        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. Esegui questo comando per visualizzare i job:

        kubectl get job --namespace=llm
        

        L'output è simile al seguente, il che indica che il job di perfezionamento è in esecuzione di nuovo, riprendendo dall'ultimo checkpoint disponibile.

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

      Esegui la pulizia

      Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

      Elimina le risorse di cui è stato eseguito il deployment

      Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse che hai creato in questa guida, esegui i seguenti comandi:

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

      Passaggi successivi