Ray Serve-Anwendung mit einem Stable Diffusion-Modell in Google Kubernetes Engine (GKE) mit TPUs bereitstellen

In diesem Leitfaden wird beschrieben, wie Sie ein Stable Diffusion-Modell mithilfe von TPUs, Ray Serve und dem Ray Operator-Add-on in Google Kubernetes Engine (GKE) bereitstellen und ausführen.

Dieser Leitfaden richtet sich an Kunden von generativer KI, neue oder bestehende Nutzer von GKE, ML-Entwickler, MLOps-Entwickler (DevOps) oder Plattformadministratoren, die daran interessiert sind, Funktionen zur Kubernetes-Containerorchestrierung für die Bereitstellung von Modellen mit Ray zu nutzen.

Ray und Ray Serve

Ray ist ein skalierbares Open-Source-Computing-Framework für KI/ML-Anwendungen. Ray Serve ist eine Modellbereitstellungsbibliothek für Ray, die zur Skalierung und Bereitstellung von Modellen in einer verteilten Umgebung verwendet wird. Weitere Informationen finden Sie unter Ray Serve in der Ray-Dokumentation.

TPUs

Tensor Processing Units (TPUs) sind spezielle Hardwarebeschleuniger, die das Training und die Inferenz von großen Modellen für maschinelles Lernen erheblich beschleunigen. Mit Rays und TPUs können Sie leistungsstarke ML-Anwendungen nahtlos skalieren. Weitere Informationen zu TPUs finden Sie unter Einführung in Cloud TPU in der Cloud TPU-Dokumentation.

KubeRay-Webhook zur TPU-Initialisierung

Als Teil des Ray Operator-Add-ons bietet GKE das Validieren und Mutieren von Webhooks, die die TPU-Pod-Planung und bestimmte TPU-Umgebungsvariablen verarbeiten, die für Frameworks wie JAX für die Containerinitialisierung erforderlich sind. Der KubeRay TPU-Webhook mutiert Pods mit dem Label app.kubernetes.io/name: kuberay und fordert TPUs mit den folgenden Attributen an:

  • TPU_WORKER_ID: Eine eindeutige Ganzzahl für jeden Worker-Pod im TPU-Slice.
  • TPU_WORKER_HOSTNAMES: Eine Liste von DNS-Hostnamen für alle TPU-Worker, die innerhalb des Slices miteinander kommunizieren müssen. Diese Variable wird nur für TPU-Pods in einer Gruppe mit mehreren Hosts eingefügt.
  • replicaIndex: Ein Pod-Label, das eine eindeutige Kennung für das Replikat der Worker-Gruppe enthält, zu der der Pod gehört. Dies ist nützlich für Arbeitsgruppen mit mehreren Hosts, bei denen mehrere Worker-Pods zum selben Replikat gehören können. Ray verwendet diese Funktion, um das Multi-Host-Autoscaling zu ermöglichen.
  • TPU_NAME: Ein String, der den GKE TPU-PodSlice darstellt, zu dem dieser Pod gehört, und für den gleichen Wert wie das Label replicaIndex festgelegt.
  • podAffinity: Sorgt dafür, dass GKE TPU-Pods mit übereinstimmenden replicaIndex-Labels im selben Knotenpool plant. Dadurch kann GKE TPUs mit mehreren Hosts atomar nach Knotenpools statt nach einzelnen Knoten skalieren.

Umgebung vorbereiten

So bereiten Sie die Umgebung vor:

  1. Starten Sie eine Cloud Shell-Sitzung über die Google Cloud Console. 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 Umgebungsvariablen fest:

    export PROJECT_ID=PROJECT_ID
    export CLUSTER_NAME=ray-cluster
    export COMPUTE_REGION=us-central2-b
    export CLUSTER_VERSION=CLUSTER_VERSION
    

    Ersetzen Sie Folgendes:

    • PROJECT_ID: Ihre Google Cloud Projekt-ID.
    • CLUSTER_VERSION ist die zu verwendende GKE-Version. Es muss 1.30.1 oder höher sein.
  3. Klonen Sie das GitHub-Repository:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  4. Wechseln Sie in das Arbeitsverzeichnis:

    cd kubernetes-engine-samples/ai-ml/gke-ray/rayserve/stable-diffusion
    

Cluster mit TPU-Knotenpool erstellen

Erstellen Sie einen Standard-GKE-Cluster mit einem TPU-Knotenpool:

  1. Erstellen Sie einen Cluster im Standardmodus mit aktiviertem Ray-Operator:

    gcloud container clusters create ${CLUSTER_NAME} \
        --addons=RayOperator \
        --machine-type=n1-standard-8 \
        --cluster-version=${CLUSTER_VERSION} \
        --location=${COMPUTE_REGION}
    
  2. So erstellen Sie einen TPU-Knotenpool mit einem Host:

    gcloud container node-pools create tpu-pool \
        --location=${COMPUTE_REGION} \
        --cluster=${CLUSTER_NAME} \
        --machine-type=ct4p-hightpu-4t \
        --num-nodes=1
    

Wenn Sie TPUs mit dem Standardmodus verwenden möchten, müssen Sie Folgendes auswählen:

  • Ein Compute Engine-Standort mit Kapazität für TPU-Beschleuniger
  • Einen kompatiblen Maschinentyp für die TPU und
  • Die physische Topologie des TPU-Pod-Slices

RayCluster-Ressource mit TPUs konfigurieren

Konfigurieren Sie das RayCluster-Manifest, um Ihre TPU-Arbeitslast vorzubereiten:

TPU nodeSelector konfigurieren

GKE verwendet Kubernetes-nodeSelectors, um dafür zu sorgen, dass TPU-Arbeitslasten auf der entsprechenden TPU-Topologie und dem entsprechenden Beschleuniger geplant werden. Weitere Informationen zum Auswählen von TPU nodeSelectors finden Sie unter TPU-Arbeitslasten in GKE Standard bereitstellen.

Aktualisieren Sie das Manifest ray-cluster.yaml, um Ihren Pod auf einem v4-TPU-Podslice mit einer 2x2x1-Topologie zu planen:

nodeSelector:
  cloud.google.com/gke-tpu-accelerator: tpu-v4-podslice
  cloud.google.com/gke-tpu-topology: 2x2x1

TPU-Containerressource konfigurieren

Wenn Sie einen TPU-Beschleuniger verwenden möchten, müssen Sie die Anzahl der TPU-Chips angeben, die GKE jedem Pod zuweisen soll. Konfigurieren Sie dazu die google.com/tpu-Ressource limits und requests im TPU-Containerfeld Ihres RayCluster-Manifests workerGroupSpecs.

Aktualisieren Sie das ray-cluster.yaml-Manifest mit Ressourcenlimits und ‑anfragen:

resources:
  limits:
    cpu: "1"
    ephemeral-storage: 10Gi
    google.com/tpu: "4"
    memory: "2G"
   requests:
    cpu: "1"
    ephemeral-storage: 10Gi
    google.com/tpu: "4"
    memory: "2G"

Worker-Gruppe numOfHosts konfigurieren

KubeRay v1.1.0 fügt der benutzerdefinierten RayCluster-Ressource das Feld numOfHosts hinzu, das die Anzahl der TPU-Hosts angibt, die pro Worker-Gruppen-Replikat erstellt werden sollen. Bei Worker-Gruppen mit mehreren Hosts werden Replikate als PodSlices und nicht als einzelne Worker behandelt. Pro Replikate werden numOfHosts Worker-Knoten erstellt.

Aktualisieren Sie das Manifest ray-cluster.yaml so:

workerGroupSpecs:
  # Several lines omitted
  numOfHosts: 1 # the number of "hosts" or workers per replica

Benutzerdefinierte RayService-Ressource erstellen

Erstellen Sie eine benutzerdefinierte RayService-Ressource:

  1. Prüfen Sie das folgende Manifest:

    apiVersion: ray.io/v1
    kind: RayService
    metadata:
      name: stable-diffusion-tpu
    spec:
      serveConfigV2: |
        applications:
          - name: stable_diffusion
            import_path: ai-ml.gke-ray.rayserve.stable-diffusion.stable_diffusion_tpu:deployment
            runtime_env:
              working_dir: "https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/archive/refs/heads/main.zip"
              pip:
                - diffusers==0.7.2
                - flax
                - jax[tpu]==0.4.11
                - -f https://storage.googleapis.com/jax-releases/libtpu_releases.html
                - fastapi
      rayClusterConfig:
        rayVersion: '2.9.0'
        headGroupSpec:
          rayStartParams: {}
          template:
            spec:
              containers:
              - name: ray-head
                image: rayproject/ray-ml:2.9.0-py310
                ports:
                - containerPort: 6379
                  name: gcs
                - containerPort: 8265
                  name: dashboard
                - containerPort: 10001
                  name: client
                - containerPort: 8000
                  name: serve
                resources:
                  limits:
                    cpu: "2"
                    memory: "8G"
                  requests:
                    cpu: "2"
                    memory: "8G"
        workerGroupSpecs:
        - replicas: 1
          minReplicas: 1
          maxReplicas: 10
          numOfHosts: 1
          groupName: tpu-group
          rayStartParams: {}
          template:
            spec:
              containers:
              - name: ray-worker
                image: rayproject/ray-ml:2.9.0-py310
                resources:
                  limits:
                    cpu: "100"
                    ephemeral-storage: 20Gi
                    google.com/tpu: "4"
                    memory: 200G
                  requests:
                    cpu: "100"
                    ephemeral-storage: 20Gi
                    google.com/tpu: "4"
                    memory: 200G
              nodeSelector:
                cloud.google.com/gke-tpu-accelerator: tpu-v4-podslice
                cloud.google.com/gke-tpu-topology: 2x2x1

    Dieses Manifest beschreibt eine benutzerdefinierte RayService-Ressource, die eine RayCluster-Ressource mit einem Hauptknoten und einer TPU-Worker-Gruppe mit einer 2x2x1-Topologie erstellt. Dies bedeutet, dass jeder Worker-Knoten 4 v4-TPU-Chips hat.

    Der TPU-Knoten gehört zu einem einzelnen v4-TPU-Podslice mit einer 2x2x1-Topologie. Wenn Sie eine Multi-Host-Worker-Gruppe erstellen möchten, ersetzen Sie die gke-tpu nodeSelector-Werte, die google.com/tpu-Containerlimits und ‑anfragen sowie die numOfHosts-Werte durch Ihre Multi-Host-Konfiguration. Weitere Informationen zu TPU-Topologien mit mehreren Hosts finden Sie in der Cloud TPU-Dokumentation unter Systemarchitektur.

  2. Wenden Sie das Manifest auf Ihren Cluster an:

    kubectl apply -f ray-service-tpu.yaml
    
  3. Prüfen Sie, ob die RayService-Ressource ausgeführt wird:

    kubectl get rayservices
    

    Die Ausgabe sieht in etwa so aus:

    NAME                   SERVICE STATUS   NUM SERVE ENDPOINTS
    stable-diffusion-tpu   Running          2
    

    In dieser Ausgabe gibt Running in der Spalte SERVICE STATUS an, dass die RayService-Ressource bereit ist.

(Optional) Ray-Dashboard aufrufen

Sie können Ihre Ray Serve-Bereitstellung und relevante Logs im Ray-Dashboard aufrufen.

  1. Richten Sie eine Portweiterleitungssitzung vom Ray-Head-Dienst zum Ray-Dashboard ein:

    kubectl port-forward svc/stable-diffusion-tpu-head-svc 8265:8265
    
  2. Rufe in einem Webbrowser folgende Seite auf:http://localhost:8265/

  3. Klicken Sie auf den Tab Bereitstellen.

Prompts an den Modellserver senden

  1. Richten Sie eine Portweiterleitungssitzung zum Serve-Endpunkt aus dem Ray-Head-Dienst ein:

    kubectl port-forward svc/stable-diffusion-tpu-serve-svc 8000
    
  2. Öffnen Sie eine neue Cloud Shell-Sitzung.

  3. Senden Sie einen Text-zu-Bild-Prompt an den Server des Stable Diffusion-Modells:

    python stable_diffusion_tpu_req.py  --save_pictures
    

    Die Ergebnisse der stabilen Diffusionsinferenz werden in einer Datei mit dem Namen diffusion_results.png gespeichert.

    Von Stable Diffusion das mit acht Abschnitten generierte Bild: ein grüner Stuhl, ein Mann vor einem Haus, ein Roboter auf der Straße, eine Familie an einem Tisch, ein Dokument, das im Park läuft, ein fließender Drain, ein Porträt von Bären und ein Wasserfall.