GKE-Ressourcennutzung für gemischte KI/ML-Trainings- und Inferenzarbeitslasten optimieren


In dieser Anleitung wird gezeigt, wie Sie Beschleunigerressourcen in einem einzelnen Google Kubernetes Engine-Cluster (GKE) effizient zwischen Arbeitslasten für Training und Inferenzbereitstellung aufteilen. Wenn Sie Ihre gemischten Arbeitslasten auf einen einzelnen Cluster verteilen, verbessern Sie die Ressourcennutzung, vereinfachen die Clusterverwaltung, reduzieren Probleme aufgrund von Einschränkungen bei der Anzahl der Beschleuniger und verbessern die allgemeine Kosteneffizienz.

In dieser Anleitung erstellen Sie eine Bereitstellung mit hoher Priorität für die Bereitstellung mit dem Large Language Model (LLM) Gemma 2 für die Inferenz und dem Hugging Face TGI (Text Generation Interface)-Serving-Framework sowie einen Job zum Feinabstimmen von LLM mit niedriger Priorität. Beide Arbeitslasten werden in einem einzelnen Cluster mit NVIDIA L4-GPUs ausgeführt. Sie verwenden Kueue, ein natives Kubernetes-Jobwarteschlangensystem, um Ihre Arbeitslasten zu verwalten und zu planen. Mit Kueue können Sie Serving-Aufgaben priorisieren und Trainingsjobs mit niedrigerer Priorität vorzeitig beenden, um die Ressourcennutzung zu optimieren. Wenn die Anforderungen an die Bereitstellung sinken, weisen Sie die freigegebenen Beschleuniger neu zu, um Trainingsjobs fortzusetzen. Sie verwenden Kueue und Prioritätsklassen, um Ressourcenkontingente während des gesamten Prozesses zu verwalten.

Diese Anleitung richtet sich an ML-Entwickler, Plattformadministratoren und ‑operatoren sowie Daten- und KI-Experten, die ein ML-Modell in einem GKE-Cluster trainieren und hosten möchten und die Kosten und den Verwaltungsaufwand reduzieren möchten, insbesondere bei einer begrenzten Anzahl von Beschleunigern. Weitere Informationen zu gängigen Rollen und Beispielaufgaben, auf die wir in Google Cloud -Inhalten verweisen, finden Sie unter Häufig verwendete GKE-Nutzerrollen und -Aufgaben.

Machen Sie sich vor dem Lesen dieser Seite mit den folgenden Themen vertraut:

Ziele

Sie sollten am Ende dieses Leitfadens in der Lage sein, die folgenden Schritte auszuführen:

  • Konfigurieren Sie ein Bereitstellungsmodell mit hoher Priorität.
  • Richten Sie Trainingsjobs mit niedrigerer Priorität ein.
  • Unterbrechungsstrategien implementieren, um auf unterschiedliche Nachfrage zu reagieren
  • Mit Kueue können Sie die Ressourcenzuweisung zwischen Trainings- und Serving-Aufgaben verwalten.

Hinweise

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

      IAM aufrufen
    2. Wählen Sie das Projekt aus.
    3. Klicken Sie auf Zugriffsrechte erteilen.
    4. Geben Sie im Feld Neue Hauptkonten Ihre Nutzer-ID ein. Das ist in der Regel die E‑Mail-Adresse eines Google-Kontos.

    5. Wählen Sie in der Liste Rolle auswählen eine Rolle aus.
    6. Klicken Sie auf Weitere Rolle hinzufügen, wenn Sie weitere Rollen zuweisen möchten.
    7. Klicken Sie auf Speichern.
      • Erstellen Sie ein Hugging Face-Konto, falls Sie noch keines haben.
      • Prüfen Sie, ob Ihr Projekt ein ausreichendes Kontingent für L4-GPUs hat. Weitere Informationen finden Sie unter GPUs und Zuteilungskontingente.

      Umgebung vorbereiten

      In diesem Abschnitt stellen Sie die Ressourcen bereit, die Sie zum Bereitstellen von TGI und des Modells für Ihre Inferenz- und Trainingsarbeitslasten benötigen.

      Zugriff auf das Modell erhalten

      Wenn Sie Zugriff auf die Gemma-Modelle für die Bereitstellung in GKE erhalten möchten, müssen Sie zuerst die Lizenzeinwilligungsvereinbarung unterzeichnen und dann ein Hugging-Face-Zugriffstoken generieren.

      1. Lizenz-Einwilligungsvereinbarung unterzeichnen Rufen Sie die Seite zur Modelleinwilligung auf, bestätigen Sie die Einwilligung mit Ihrem Hugging Face-Konto und akzeptieren Sie die Modellbedingungen.
      2. Zugriffstoken generieren Für den Zugriff auf das Modell über Hugging Face benötigen Sie ein Hugging Face-Token. Führen Sie die folgenden Schritte aus, um ein neues Token zu generieren, falls Sie noch keines haben:

        1. Klicken Sie auf Profil > Einstellungen > Zugriffstokens.
        2. Wählen Sie Neues Token aus.
        3. Geben Sie einen Namen Ihrer Wahl und eine Rolle von mindestens Read an.
        4. Wählen Sie Token generieren aus.
        5. Kopieren Sie das Token in die Zwischenablage.

      Cloud Shell starten

      In dieser Anleitung verwenden Sie Cloud Shell zum Verwalten von Ressourcen, die inGoogle Cloudgehostet werden. Die Software, die Sie für diese Anleitung benötigen, ist in Cloud Shell vorinstalliert, einschließlich kubectl, gcloud CLI und Terraform.

      So richten Sie Ihre Umgebung mit Cloud Shell ein:

      1. Starten Sie in der Google Cloud Console eine Cloud Shell-Sitzung. Klicken Sie dazu in der Google Cloud Console auf Symbol für die Cloud Shell-Aktivierung Cloud Shell aktivieren. Dadurch wird im unteren Bereich der Google Cloud console eine Sitzung gestartet.

      2. Legen Sie die Standardumgebungsvariablen fest:

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

        Ersetzen Sie PROJECT_ID durch Ihre Google Cloud Projekt-ID.

      3. Klonen Sie den Beispielcode aus GitHub. Führen Sie in Cloud Shell die folgenden Befehle aus:

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

      GKE-Cluster erstellen

      Sie können einen Autopilot- oder Standardcluster für Ihre gemischten Arbeitslasten verwenden. Für eine vollständig verwaltete Kubernetes-Umgebung empfehlen wir die Verwendung eines Autopilot-Clusters. Informationen zum Auswählen des GKE-Betriebsmodus, der für Ihre Arbeitslasten am besten geeignet ist, finden Sie unter GKE-Betriebsmodus auswählen.

      Autopilot

      1. Legen Sie die Standardumgebungsvariablen in Cloud Shell fest:

        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"
        

        Ersetzen Sie die folgenden Werte:

        • HF_TOKEN: das Hugging Face-Token, das Sie zuvor generiert haben.
        • REGION: eine Region, die den Beschleunigertyp unterstützt, den Sie verwenden möchten, z. B. us-central1 für die L4-GPU.

        Sie können die Variable MODEL_BUCKET anpassen. Sie steht für den Cloud Storage-Bucket, in dem Sie die Gewichte Ihres trainierten Modells speichern.

      2. Autopilot-Cluster erstellen:

        gcloud container clusters create-auto ${CLUSTER_NAME} \
            --project=${PROJECT_ID} \
            --location=${REGION} \
            --release-channel=rapid
        
      3. Erstellen Sie den Cloud Storage-Bucket für den Feinabstimmungsjob:

        gcloud storage buckets create gs://${MODEL_BUCKET} \
            --location ${REGION} \
            --uniform-bucket-level-access
        
      4. Führen Sie den folgenden Befehl aus, um Zugriff auf den Cloud Storage-Bucket zu gewähren:

        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. Führen Sie den folgenden Befehl aus, um Authentifizierungsdaten für den Cluster abzurufen:

        gcloud container clusters get-credentials llm-cluster \
            --location=$REGION \
            --project=$PROJECT_ID
        
      6. Erstellen Sie einen Namespace für Ihre Deployments. Führen Sie in Cloud Shell den folgenden Befehl aus:

        kubectl create ns llm
        

      Standard

      1. Legen Sie die Standardumgebungsvariablen in Cloud Shell fest:

        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"
        

        Ersetzen Sie die folgenden Werte:

        • HF_TOKEN: das Hugging Face-Token, das Sie zuvor generiert haben.
        • REGION: die Region, die den Beschleunigertyp unterstützt, den Sie verwenden möchten, z. B. us-central1 für die L4-GPU.

        Sie können die folgenden Variablen anpassen:

        • GPU_POOL_MACHINE_TYPE: Die Maschinenserie des Knotenpools, die Sie in der ausgewählten Region verwenden möchten. Dieser Wert hängt vom ausgewählten Beschleunigertyp ab. Weitere Informationen finden Sie unter Einschränkungen bei der Verwendung von GPUs in GKE. In dieser Anleitung wird beispielsweise g2-standard-24 mit zwei GPUs pro Knoten verwendet. Die aktuelle Liste der verfügbaren GPUs finden Sie unter GPUs für Compute-Arbeitslasten.
        • GPU_POOL_ACCELERATOR_TYPE: Der Beschleunigertyp, der in der ausgewählten Region unterstützt wird. In dieser Anleitung wird beispielsweise nvidia-l4 verwendet. Die aktuelle Liste der verfügbaren GPUs finden Sie unter GPUs für Compute-Arbeitslasten.
        • MODEL_BUCKET: Der Cloud Storage-Bucket, in dem Sie die Gewichte Ihres trainierten Modells speichern.
      2. Standardcluster erstellen:

        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. Erstellen Sie den GPU-Knotenpool für Inferenz- und Feinabstimmungsarbeitslasten:

        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. Erstellen Sie den Cloud Storage-Bucket für den Feinabstimmungsjob:

        gcloud storage buckets create gs://${MODEL_BUCKET} \
            --location ${REGION} \
            --uniform-bucket-level-access
        
      5. Führen Sie den folgenden Befehl aus, um Zugriff auf den Cloud Storage-Bucket zu gewähren:

        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. Führen Sie den folgenden Befehl aus, um Authentifizierungsdaten für den Cluster abzurufen:

        gcloud container clusters get-credentials llm-cluster \
            --location=$REGION \
            --project=$PROJECT_ID
        
      7. Erstellen Sie einen Namespace für Ihre Deployments. Führen Sie in Cloud Shell den folgenden Befehl aus:

        kubectl create ns llm
        

      Kubernetes-Secret für Hugging Face-Anmeldedaten erstellen

      Führen Sie den folgenden Befehl aus, um ein Kubernetes-Secret zu erstellen, das das Hugging Face-Token enthält:

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

      Kueue konfigurieren

      In dieser Anleitung ist Kueue der zentrale Ressourcenmanager, der eine effiziente gemeinsame Nutzung von GPUs zwischen Ihren Trainings- und Bereitstellungsarbeitslasten ermöglicht. Kueue erreicht dies, indem es Ressourcenanforderungen („Flavors“) definiert, Arbeitslasten über Warteschlangen priorisiert (mit Priorisierung von Serving-Aufgaben gegenüber Training) und Ressourcen basierend auf Bedarf und Priorität dynamisch zuweist. In dieser Anleitung wird der Ressourcentyp Workload verwendet, um die Inferenz- und Feinabstimmungsarbeitslasten zu gruppieren.

      Die Preemption-Funktion von Kueue sorgt dafür, dass Serving-Arbeitslasten mit hoher Priorität immer die erforderlichen Ressourcen haben. Dazu werden Trainingsjobs mit niedrigerer Priorität angehalten oder beendet, wenn Ressourcen knapp sind.

      Wenn Sie die Bereitstellung des Inferenzservers mit Kueue steuern möchten, aktivieren Sie die pod-Integration und konfigurieren Sie managedJobsNamespaceSelector so, dass die Namespaces kube-system und kueue-system ausgeschlossen werden.

      1. Sehen Sie sich im Verzeichnis /kueue den Code in kustomization.yaml an. Mit diesem Manifest wird der Kueue-Ressourcenmanager mit benutzerdefinierten Konfigurationen installiert.

        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. Sehen Sie sich im Verzeichnis /kueue den Code in patch.yaml an. Diese ConfigMap passt Kueue so an, dass die Verwaltung von Pods in den Namespaces kube-system und kueue-system ausgeschlossen wird.

        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. Führen Sie in Cloud Shell den folgenden Befehl aus, um Kueue zu installieren:

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

        Warten Sie, bis die Kueue-Pods bereit sind:

        watch kubectl --namespace=kueue-system get pods
        

        Die Ausgabe sollte in etwa so aussehen:

        NAME                                        READY   STATUS    RESTARTS   AGE
        kueue-controller-manager-bdc956fc4-vhcmx    1/1     Running   0          3m15s
        
      4. Sehen Sie sich im Verzeichnis /workloads die Dateien flavors.yaml, cluster-queue.yaml und local-queue.yaml an. In diesen Manifesten wird angegeben, wie Kueue Ressourcenkontingente verwaltet:

        ResourceFlavor

        Dieses Manifest definiert ein Standard-ResourceFlavor in Kueue für die Ressourcenverwaltung.

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

        ClusterQueue

        In diesem Manifest wird eine Kueue-ClusterQueue mit Ressourcenlimits für CPU, Arbeitsspeicher und GPU eingerichtet.

        In dieser Anleitung werden Knoten mit zwei angehängten Nvidia L4-GPUs verwendet. Der entsprechende Knotentyp ist g2-standard-24 mit 24 vCPUs und 96 GB RAM. Im Beispielcode wird gezeigt, wie die Ressourcennutzung Ihres Arbeitslast auf maximal sechs GPUs begrenzt wird.

        Das Feld preemption in der ClusterQueue-Konfiguration verweist auf die PriorityClasses, um zu bestimmen, welche Pods bei Ressourcenknappheit vorzeitig beendet werden können.

        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

        Mit diesem Manifest wird eine Kueue-LocalQueue mit dem Namen lq im Namespace llm erstellt.

        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. Sehen Sie sich die Dateien default-priorityclass.yaml, low-priorityclass.yaml und high-priorityclass.yaml an. Diese Manifeste definieren die PriorityClass-Objekte für die Kubernetes-Planung.

        Standardpriorität

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

        Niedrige Priorität

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

        Hohe Priorität

        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. Erstellen Sie die Kueue- und Kubernetes-Objekte, indem Sie die entsprechenden Manifeste mit diesen Befehlen anwenden.

        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-Inferenzserver bereitstellen

      In diesem Abschnitt stellen Sie den TGI-Container für das Gemma 2-Modell bereit.

      1. Sehen Sie sich im Verzeichnis /workloads die Datei tgi-gemma-2-9b-it-hp.yaml an. Dieses Manifest definiert ein Kubernetes-Deployment zum Bereitstellen der TGI-Laufzeit und des gemma-2-9B-it-Modells. Ein Deployment ist ein Kubernetes-API-Objekt, mit dem Sie mehrere Replikate von Pods ausführen können, die auf die Knoten in einem Cluster verteilt sind.

        Bei der Bereitstellung werden Inferenzen priorisiert und zwei GPUs für das Modell verwendet. Dazu wird Tensor-Parallelismus verwendet, indem die Umgebungsvariable NUM_SHARD festgelegt wird, damit das Modell in den GPU-Arbeitsspeicher passt.

        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. Wenden Sie das Manifest mit dem folgenden Befehl an:

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

        Die Bereitstellung dauert einige Minuten.

      3. Führen Sie den folgenden Befehl aus, um zu prüfen, ob GKE das Deployment erfolgreich erstellt hat:

        kubectl --namespace=llm get deployment
        

        Die Ausgabe sollte in etwa so aussehen:

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

      Kueue-Kontingentverwaltung prüfen

      In diesem Abschnitt bestätigen Sie, dass Kueue das GPU-Kontingent für Ihre Bereitstellung korrekt erzwingt.

      1. Führen Sie den folgenden Befehl aus, um den Status der Workload-Objekte abzurufen und zu prüfen, ob Kueue Ihr Deployment kennt:

        kubectl --namespace=llm get workloads
        

        Die Ausgabe sollte in etwa so aussehen:

        NAME                                              QUEUE   RESERVED IN     ADMITTED   FINISHED   AGE
        pod-tgi-gemma-deployment-6bf9ffdc9b-zcfrh-84f19   lq      cluster-queue   True                  8m23s
        
      2. Um das Überschreiben der Kontingentlimits zu testen, skalieren Sie das Deployment auf vier Replikate:

        kubectl scale --replicas=4 deployment/tgi-gemma-deployment --namespace=llm
        
      3. Führen Sie den folgenden Befehl aus, um die Anzahl der Replikate zu sehen, die in GKE bereitgestellt werden:

        kubectl get workloads --namespace=llm
        

        Die Ausgabe sollte in etwa so aussehen:

        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
        

        Die Ausgabe zeigt, dass aufgrund des von Kueue erzwungenen Ressourcenkontingents nur drei Pods zugelassen werden.

      4. Führen Sie Folgendes aus, um die Pods im Namespace llm aufzurufen:

        kubectl get pod --namespace=llm
        

        Die Ausgabe sollte in etwa so aussehen:

        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. Skalieren Sie das Deployment nun wieder auf 1 herunter. Dieser Schritt ist erforderlich, bevor Sie den Feinabstimmungsjob bereitstellen. Andernfalls wird er nicht zugelassen, da der Inferenzjob Priorität hat.

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

      Erläuterung des Verhaltens

      Das Skalierungsbeispiel führt aufgrund des GPU-Kontingentlimits, das Sie in der ClusterQueue-Konfiguration festgelegt haben, zu nur drei Replikaten (obwohl auf vier skaliert wird). Im spec.resourceGroups-Abschnitt von ClusterQueue wird ein nominalQuota von „6“ für nvidia.com/gpu definiert. Das Deployment gibt an, dass für jeden Pod zwei GPUs erforderlich sind. Daher kann die ClusterQueue nur maximal drei Replikate der Bereitstellung gleichzeitig aufnehmen (da 3 Replikate × 2 GPUs pro Replikat = 6 GPUs, was dem Gesamtkontingent entspricht).

      Wenn Sie versuchen, auf vier Replikate zu skalieren, erkennt Kueue, dass diese Aktion das GPU-Kontingent überschreiten würde, und verhindert, dass das vierte Replikat geplant wird. Dies wird durch den SchedulingGated-Status des vierten Pods angezeigt. Dieses Verhalten zeigt die Durchsetzung von Ressourcenkontingenten durch Kueue.

      Trainingsjob bereitstellen

      In diesem Abschnitt stellen Sie einen Fine-Tuning-Job mit niedrigerer Priorität für ein Gemma 2-Modell bereit, für das vier GPUs in zwei Pods erforderlich sind. Ein Jobcontroller in Kubernetes erstellt einen oder mehrere Pods und sorgt dafür, dass sie eine bestimmte Aufgabe erfolgreich ausführen.

      Für diesen Job wird das verbleibende GPU-Kontingent in der ClusterQueue verwendet. Für den Job wird ein vordefiniertes Image verwendet und es werden Checkpoints gespeichert, damit der Job anhand von Zwischenergebnissen neu gestartet werden kann.

      Für den Feinabstimmungsjob wird das Dataset b-mc2/sql-create-context verwendet. Die Quelle für den Feinabstimmungsjob finden Sie im Repository.

      1. Sehen Sie sich die Datei fine-tune-l4.yaml an. Dieses Manifest definiert den Job für die Feinabstimmung.

        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. Wenden Sie das Manifest an, um den Job für die Feinabstimmung zu erstellen:

        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. Prüfen Sie, ob Ihre Deployments ausgeführt werden. Führen Sie den folgenden Befehl aus, um den Status der Arbeitslastobjekte zu prüfen:

        kubectl get workloads --namespace=llm
        

        Die Ausgabe sollte in etwa so aussehen:

        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
        

        Sehen Sie sich als Nächstes die Pods im Namespace llm an, indem Sie diesen Befehl ausführen:

        kubectl get pod --namespace=llm
        

        Die Ausgabe sollte in etwa so aussehen:

        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
        

        Die Ausgabe zeigt, dass Kueue sowohl Ihren Fine-Tuning-Job als auch die Pods des Inferenzservers zulässt und die richtigen Ressourcen basierend auf den von Ihnen angegebenen Kontingentlimits reserviert.

      4. Sehen Sie sich die Ausgabelogs an, um zu prüfen, ob in Ihrem Fine-Tuning-Job Checkpoints im Cloud Storage-Bucket gespeichert werden. Es dauert etwa 10 Minuten, bis der Job zum Feinabstimmen den ersten Prüfpunkt speichert.

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

        Die Ausgabe für den ersten gespeicherten Prüfpunkt sieht in etwa so aus:

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

      Vorzeitiges Beenden von Kueue und dynamische Zuweisung für Ihre gemischte Arbeitslast testen

      In diesem Abschnitt simulieren Sie ein Szenario, in dem die Last des Inferenzservers zunimmt und er skaliert werden muss. In diesem Szenario wird gezeigt, wie Kueue den Inferenzserver mit hoher Priorität priorisiert, indem der Job zum Feinabstimmen mit niedrigerer Priorität bei Ressourcenengpässen angehalten und vorzeitig beendet wird.

      1. Führen Sie den folgenden Befehl aus, um die Replikate des Inferenzservers auf zwei zu skalieren:

        kubectl scale --replicas=2 deployment/tgi-gemma-deployment --namespace=llm
        
      2. Prüfen Sie den Status der Arbeitslastobjekte:

        kubectl get workloads --namespace=llm
        

        Die Ausgabe sieht dann ungefähr so aus:

        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
        

        Die Ausgabe zeigt, dass der Fine-Tuning-Job nicht mehr zugelassen wird, da die erhöhte Anzahl von Inferencing-Server-Replikaten das verfügbare GPU-Kontingent nutzt.

      3. Prüfen Sie den Status des Jobs zum Feinabstimmen:

        kubectl get job --namespace=llm
        

        Die Ausgabe sieht dann ungefähr so aus. Sie zeigt, dass der Status des Fine-Tune-Jobs jetzt „suspended“ (angehalten) ist:

        NAME                STATUS      COMPLETIONS   DURATION   AGE
        finetune-gemma-l4   Suspended   0/2                      33m
        
      4. Führen Sie den folgenden Befehl aus, um Ihre Pods zu prüfen:

        kubectl get pod --namespace=llm
        

        Die Ausgabe sieht in etwa so aus. Sie zeigt, dass Kueue die Pods des Fine-Tuning-Jobs beendet hat, um Ressourcen für die Bereitstellung des Inferenzservers mit höherer Priorität freizugeben.

        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. Testen Sie als Nächstes das Szenario, in dem die Last des Inferenzservers abnimmt und die zugehörigen Pods skaliert werden. Führen Sie dazu diesen Befehl aus:

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

        Führen Sie den folgenden Befehl aus, um die Arbeitslastobjekte aufzurufen:

        kubectl get workloads --namespace=llm
        

        Die Ausgabe sieht etwa so aus. Das weist darauf hin, dass eine der Inferenzserver-Bereitstellungen beendet und der Feinabstimmungsjob neu zugelassen wurde.

        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. Führen Sie diesen Befehl aus, um die Jobs aufzurufen:

        kubectl get job --namespace=llm
        

        Die Ausgabe sieht in etwa so aus. Das bedeutet, dass der Fine-Tuning-Job wieder ausgeführt wird und am letzten verfügbaren Prüfpunkt fortgesetzt wird.

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

      Bereinigen

      Damit Ihrem Google Cloud-Konto die in dieser Anleitung verwendeten Ressourcen nicht in Rechnung gestellt werden, löschen Sie entweder das Projekt, das die Ressourcen enthält, oder Sie behalten das Projekt und löschen die einzelnen Ressourcen.

      Bereitgestellte Ressourcen löschen

      Mit den folgenden Befehlen vermeiden Sie, dass Ihrem Google Cloud -Konto die in dieser Anleitung erstellten Ressourcen in Rechnung gestellt werden:

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

      Nächste Schritte