Configurer l'autoscaling pour les charges de travail LLM sur les TPU


Cette page explique comment configurer votre infrastructure d'autoscaling à l'aide de l'autoscaler horizontal des pods (HPA) de GKE pour déployer le grand modèle de langage (LLM) Gemma à l'aide de JetStream à hôte unique.

Pour en savoir plus sur la sélection de métriques pour l'autoscaling, consultez la page Bonnes pratiques pour l'autoscaling des charges de travail LLM à l'aide de TPU sur GKE.

Avant de commencer

Avant de commencer, effectuez les tâches suivantes :

  • Activez l'API Google Kubernetes Engine.
  • Activer l'API Google Kubernetes Engine
  • Si vous souhaitez utiliser Google Cloud CLI pour cette tâche, installez puis initialisez gcloud CLI. Si vous avez déjà installé gcloud CLI, assurez-vous de disposer de la dernière version en exécutant la commande gcloud components update.

Autoscaling à l'aide de métriques

Vous pouvez utiliser les métriques de performances spécifiques à la charge de travail émises par le serveur d'inférence JetStream ou les métriques de performances TPU pour diriger l'autoscaling pour vos pods.

Pour configurer l'autoscaling à l'aide de métriques, procédez comme suit :

  1. Exportez les métriques du serveur JetStream vers Cloud Monitoring. Vous utilisez Google Cloud Managed Service pour Prometheus, ce qui simplifie le déploiement et la configuration de votre collecteur Prometheus. Google Cloud Managed Service pour Prometheus est activé par défaut dans votre cluster GKE. Vous pouvez aussi l'activer manuellement.

    L'exemple de fichier manifeste suivant montre comment configurer les définitions de ressource PodMonitoring pour demander à Google Cloud Managed Service pour Prometheus de scraper les métriques de vos pods à des intervalles récurrents de 15 secondes :

    Si vous devez scraper des métriques de serveur, utilisez le fichier manifeste suivant. Avec les métriques de serveur, les intervalles de scraping aussi fréquents que 5 secondes sont acceptés.

    apiVersion: monitoring.googleapis.com/v1
    kind: PodMonitoring
    metadata:
      name: jetstream-podmonitoring
    selector:
      matchLabels:
        app: maxengine-server
    spec:
      endpoints:
      - interval: 15s
        path: "/"
        port: PROMETHEUS_PORT
      targetLabels:
        metadata:
        - pod
        - container
        - node
    

    Si vous devez scraper des métriques de TPU, utilisez le fichier manifeste suivant. Avec les métriques système, des intervalles de scraping aussi fréquents que 15 secondes sont acceptés.

    apiVersion: monitoring.googleapis.com/v1
    kind: PodMonitoring
    metadata:
      name: tpu-metrics-exporter
      namespace: kube-system
      labels:
        k8s-app: tpu-device-plugin
    spec:
      endpoints:
        - port: 2112
          interval: 15s
      selector:
        matchLabels:
          k8s-app: tpu-device-plugin
    
  2. Installez un adaptateur de métriques. Cet adaptateur rend les métriques de serveur que vous avez exportées vers Monitoring visibles par le contrôleur HPA. Pour en savoir plus, consultez la page Autoscaling horizontal des pods dans la documentation de Google Cloud Managed Service pour Prometheus.

    Adaptateur de métriques personnalisées Stackdriver

    L'adaptateur de métriques personnalisées Stackdriver permet d'interroger des métriques de Google Cloud Managed Service pour Prometheus à partir de la version 0.13.1 de l'adaptateur.

    Pour installer l'adaptateur de métriques personnalisées Stackdriver, procédez comme suit :

    1. Configurez la collecte gérée pour votre cluster.

    2. Installez l'adaptateur de métriques personnalisées Stackdriver dans votre cluster.

      kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml
      
    3. Si la fédération d'identité de charge de travail pour GKE est activée sur votre cluster Kubernetes et que vous utilisez la fédération d'identité de charge de travail pour GKE, vous devez également attribuer le rôle de lecteur Monitoring au compte de service sous lequel l'adaptateur s'exécute.

    export PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format 'get(projectNumber)')
    gcloud projects add-iam-policy-binding projects/PROJECT_ID \
      --role roles/monitoring.viewer \
      --member=principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/custom-metrics/sa/custom-metrics-stackdriver-adapter
    

    Adaptateur Prometheus

    Tenez compte des points suivants lorsque vous utilisez prometheus-adapter pour le scaling à l'aide de Google Cloud Managed Service pour Prometheus :

    • Acheminez les requêtes via le proxy d'interface Prometheus, de la même manière que lorsque vous interrogez Google Cloud Managed Service pour Prometheus à l'aide de l'API ou de l'interface utilisateur Prometheus. Cette interface est installée à une étape ultérieure.
    • Par défaut, l'argument prometheus-url du déploiement prometheus-adapter est défini sur --prometheus-url=http://frontend.default.svc:9090/, où default est l'espace de noms dans lequel vous avez déployé l'interface. Si vous avez déployé l'interface dans un autre espace de noms, configurez cet argument en conséquence.
    • Dans le champ .seriesQuery de la configuration des règles, vous ne pouvez pas utiliser d'outil de mise en correspondance des expressions régulières (regex) sur un nom de métrique. Vous devez spécifier les noms complets des métriques.

    Les données pouvant prendre un peu plus de temps pour être disponibles dans Google Cloud Managed Service pour Prometheus par rapport à une règle Prometheus en amont, la configuration d'une logique d'autoscaling trop excessive peut entraîner un comportement indésirable. Bien qu'il n'y ait aucune garantie en ce qui concerne la fraîcheur des données, celles-ci sont généralement disponibles pour les requêtes trois à sept secondes après leur envoi à Google Cloud Managed Service pour Prometheus, à l'exception de la latence du réseau.

    Toutes les requêtes émises par prometheus-adapter ont un champ d'application global. Cela signifie que si des applications situées dans deux espaces de noms différents émettent des métriques portant le même nom, le scaling d'une configuration HPA utilisant ces métriques va se baser sur les données des deux applications. Pour éviter un scaling utilisant des données incorrectes, utilisez toujours des filtres namespace ou cluster dans votre requête PromQL.

    Pour définir un exemple de configuration HPA à l'aide de prometheus-adapter et d'une collecte gérée, procédez comme suit :

    1. Configurez la collecte gérée pour votre cluster.
    2. Déployez le proxy d'interface Prometheus dans votre cluster. Créez le fichier manifeste suivant, nommé prometheus-frontend.yaml :

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: frontend
        spec:
          replicas: 2
          selector:
            matchLabels:
              app: frontend
          template:
            metadata:
              labels:
                app: frontend
            spec:
              automountServiceAccountToken: true
              affinity:
                nodeAffinity:
                  requiredDuringSchedulingIgnoredDuringExecution:
                    nodeSelectorTerms:
                    - matchExpressions:
                      - key: kubernetes.io/arch
                        operator: In
                        values:
                        - arm64
                        - amd64
                      - key: kubernetes.io/os
                        operator: In
                        values:
                        - linux
              containers:
              - name: frontend
                image: gke.gcr.io/prometheus-engine/frontend:v0.8.0-gke.4
                args:
                - "--web.listen-address=:9090"
                - "--query.project-id=PROJECT_ID"
                ports:
                - name: web
                  containerPort: 9090
                readinessProbe:
                  httpGet:
                    path: /-/ready
                    port: web
                securityContext:
                  allowPrivilegeEscalation: false
                  capabilities:
                    drop:
                    - all
                  privileged: false
                  runAsGroup: 1000
                  runAsNonRoot: true
                  runAsUser: 1000
                livenessProbe:
                  httpGet:
                    path: /-/healthy
                    port: web
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: prometheus
        spec:
          clusterIP: None
          selector:
            app: frontend
          ports:
          - name: web
            port: 9090
      

      Appliquez ensuite le fichier manifeste :

      kubectl apply -f prometheus-frontend.yaml
      
    3. Assurez-vous que prometheus-adapter est installé dans votre cluster en installant le chart Helm prometheus-community/prometheus-adapter. Créez le fichier values.yaml suivant :

      rules:
        default: false
        external:
        - seriesQuery: 'jetstream_prefill_backlog_size'
          resources:
            template: <<.Resource>>
          name:
            matches: ""
            as: "jetstream_prefill_backlog_size"
          metricsQuery: avg(<<.Series>>{<<.LabelMatchers>>,cluster="CLUSTER_NAME"})
        - seriesQuery: 'jetstream_slots_used_percentage'
          resources:
            template: <<.Resource>>
          name:
            matches: ""
            as: "jetstream_slots_used_percentage"
          metricsQuery: avg(<<.Series>>{<<.LabelMatchers>>,cluster="CLUSTER_NAME"})
        - seriesQuery: 'memory_used'
          resources:
            template: <<.Resource>>
          name:
            matches: ""
            as: "memory_used_percentage"
          metricsQuery: avg(memory_used{cluster="CLUSTER_NAME",exported_namespace="default",container="jetstream-http"}) / avg(memory_total{cluster="CLUSTER_NAME",exported_namespace="default",container="jetstream-http"})
      

      Utilisez ensuite ce fichier comme fichier de valeurs pour le déploiement de votre chart Helm :

      helm repo add prometheus-community https://prometheus-community.github.io/helm-charts && helm repo update && helm install example-release prometheus-community/prometheus-adapter -f values.yaml
      

    Si vous utilisez la fédération d'identité de charge de travail pour GKE, vous devez également configurer et autoriser un compte de service en exécutant les commandes suivantes :

    1. Commencez par créer vos comptes de service intégrés au cluster et Google Cloud :

      gcloud iam service-accounts create prom-frontend-sa && kubectl create sa prom-frontend-sa
      
    2. Associez ensuite les deux comptes de service :

      gcloud iam service-accounts add-iam-policy-binding \
        --role roles/iam.workloadIdentityUser \
        --member "serviceAccount:tpu-vm-gke-testing.svc.id.goog[default/prom-frontend-sa]" \
        jetstream-iam-sa@tpu-vm-gke-testing.iam.gserviceaccount.com \
      &&
      kubectl annotate serviceaccount \
        --namespace default \
        prom-frontend-sa \
        iam.gke.io/gcp-service-account=jetstream-iam-sa@tpu-vm-gke-testing.iam.gserviceaccount.com
      
    3. Attribuez le rôle monitoring.viewer au compte de service Google Cloud :

      gcloud projects add-iam-policy-binding tpu-vm-gke-testing \
        --member=serviceAccount:jetstream-iam-sa@tpu-vm-gke-testing.iam.gserviceaccount.com \
        --role=roles/monitoring.viewer
      
    4. Enfin, définissez le compte de service de vos déploiements d'interface comme nouveau compte de service intégré au cluster :

      kubectl set serviceaccount deployment frontend prom-frontend-sa
      
  3. Configurez la ressource HPA basée sur des métriques. Déployez une ressource HPA basée sur la métrique de serveur de votre choix. Pour en savoir plus, consultez la page Autoscaling horizontal des pods dans la documentation de Google Cloud Managed Service pour Prometheus. La configuration HPA spécifique dépend du type de métrique (serveur ou TPU) et de l'adaptateur de métrique installé.

    Certaines valeurs sont requises dans toutes les configurations HPA et doivent être définies pour créer une ressource HPA :

    • MIN_REPLICAS : nombre minimal d'instances répliquées de pods JetStream autorisé. Si vous ne modifiez pas le fichier manifeste de déploiement JetStream à l'étape Déployer JetStream, nous vous recommandons de définir cette valeur sur 1.
    • MAX_REPLICAS : nombre maximal d'instances répliquées de pods JetStream autorisé. L'exemple de déploiement JetStream nécessite 8 puces par instance répliquée et le pool de nœuds contient 16 puces. Si vous souhaitez maintenir une faible latence de scaling à la hausse, définissez cette valeur sur 2. Des valeurs plus élevées déclenchent la création de nouveaux nœuds dans le pool de nœuds par l'autoscaler de cluster, ce qui augmente la latence de scaling à la hausse.
    • TARGET : moyenne ciblée pour cette métrique pour toutes les instances JetStream. Pour en savoir plus sur la manière dont le nombre d'instances répliquées est déterminé à partir de cette valeur, consultez la documentation de Kubernetes sur l'autoscaling.

    Adaptateur de métriques personnalisées Stackdriver

    L'adaptateur de métriques personnalisées Stackdriver permet de faire évoluer votre charge de travail avec la valeur moyenne de requêtes de métriques individuelles de Google Cloud Managed Service pour Prometheus sur tous les pods. Lorsque vous utilisez l'adaptateur de métriques personnalisées Stackdriver, nous vous conseillons de procéder au scaling à l'aide des métriques de serveur jetstream_prefill_backlog_size et jetstream_slots_used_percentage, et de la métrique de TPU memory_used.

    Pour créer un fichier manifeste HPA permettant d'effectuer le scaling avec des métriques de serveur, créez le fichier hpa.yaml suivant :

    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: jetstream-hpa
      namespace: default
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: maxengine-server
      minReplicas: MIN_REPLICAS
      maxReplicas: MAX_REPLICAS
      metrics:
      - type: Pods
        pods:
          metric:
            name: prometheus.googleapis.com|jetstream_METRIC|gauge
          target:
            type: AverageValue
            averageValue: TARGET
    

    Lorsque vous utilisez l'adaptateur de métriques personnalisées Stackdriver avec des métriques de TPU, nous vous recommandons de n'utiliser que la métrique kubernetes.io|node|accelerator|memory_used pour le scaling. Pour créer un fichier manifeste HPA permettant d'effectuer le scaling avec cette métrique, créez le fichier hpa.yaml suivant :

    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: jetstream-hpa
      namespace: default
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: maxengine-server
      minReplicas: MIN_REPLICAS
      maxReplicas: MAX_REPLICAS
      metrics:
      - type: External
        external:
          metric:
            name: prometheus.googleapis.com|memory_used|gauge
            selector:
              matchLabels:
                metric.labels.container: jetstream-http
                metric.labels.exported_namespace: default
          target:
            type: AverageValue
            averageValue: TARGET
    

    Adaptateur Prometheus

    L'adaptateur Prometheus permet de procéder au scaling de votre charge de travail en fonction de la valeur des requêtes PromQL de Google Cloud Managed Service pour Prometheus. Vous avez précédemment défini les métriques de serveur jetstream_prefill_backlog_size et jetstream_slots_used_percentage, qui représentent la valeur moyenne de tous les pods.

    Pour créer un fichier manifeste HPA permettant d'effectuer le scaling avec des métriques de serveur, créez le fichier hpa.yaml suivant :

    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: jetstream-hpa
      namespace: default
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: maxengine-server
      minReplicas: MIN_REPLICAS
      maxReplicas: MAX_REPLICAS
      metrics:
      - type: External
        external:
          metric:
            name: jetstream_METRIC
          target:
            type: AverageValue
            averageValue: TARGET
    

    Pour créer un fichier manifeste HPA permettant d'effectuer le scaling avec des métriques de TPU, nous vous recommandons de n'utiliser que le pourcentage memory_used_percentage défini dans le fichier de valeurs Helm prometheus-adapter. memory_used_percentage est le nom attribué à la requête PromQL suivante, qui reflète la mémoire moyenne actuellement utilisée sur tous les accélérateurs :

    avg(kubernetes_io:node_accelerator_memory_used{cluster_name="CLUSTER_NAME"}) / avg(kubernetes_io:node_accelerator_memory_total{cluster_name="CLUSTER_NAME"})
    

    Pour créer un fichier manifeste HPA pour le scaling avec memory_used_percentage, créez le fichier hpa.yaml suivant :

    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: jetstream-hpa
      namespace: default
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: maxengine-server
      minReplicas: MIN_REPLICAS
      maxReplicas: MAX_REPLICAS
      metrics:
      - type: External
        external:
          metric:
            name: memory_used_percentage
          target:
            type: AverageValue
            averageValue: TARGET
    

Scaling à l'aide de plusieurs métriques

Vous pouvez également configurer le scaling en fonction de plusieurs métriques. Pour en savoir plus sur la façon dont le nombre d'instances répliquées est déterminé à l'aide de plusieurs métriques, consultez la documentation Kubernetes sur l'autoscaling. Pour créer ce type de fichier manifeste HPA, collectez toutes les entrées du champ spec.metrics de chaque ressource HPA dans une seule ressource HPA. L'extrait de code suivant montre comment regrouper les ressources HPA :

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: jetstream-hpa-multiple-metrics
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: maxengine-server
  minReplicas: MIN_REPLICAS
  maxReplicas: MAX_REPLICAS
  metrics:
  - type: Pods
    pods:
      metric:
        name: jetstream_METRIC
      target:
        type: AverageValue
      averageValue: JETSTREAM_METRIC_TARGET
  - type: External
    external:
      metric:
        name: memory_used_percentage
      target:
        type: AverageValue
      averageValue: EXTERNAL_METRIC_TARGET

Surveiller et tester l'autoscaling

Vous pouvez observer comment vos charges de travail JetStream évoluent en fonction de votre configuration HPA.

Pour observer le nombre d'instances répliquées en temps réel, exécutez la commande suivante :

kubectl get hpa --watch

Le résultat de cette commande devrait ressembler à ceci :

NAME            REFERENCE                     TARGETS      MINPODS   MAXPODS   REPLICAS   AGE
jetstream-hpa   Deployment/maxengine-server   0/10 (avg)   1         2         1          1m

Pour tester la capacité de scaling de votre HPA, exécutez la commande suivante, qui envoie une série de 100 requêtes au point de terminaison du modèle. Cela va épuiser les emplacements de décodage disponibles et entraîner une accumulation de requêtes dans la file d'attente de pré-remplissage, ce qui déclenche l'augmentation de la taille du déploiement du modèle par l'autoscaler horizontal de pods.

seq 100 | xargs -P 100 -n 1 curl --request POST --header "Content-type: application/json" -s localhost:8000/generate --data '{ "prompt": "Can you provide a comprehensive and detailed overview of the history and development of artificial intelligence.", "max_tokens": 200 }'

Étape suivante