Menyajikan LLM dengan beberapa GPU di GKE


Tutorial ini menunjukkan cara menayangkan model bahasa besar (LLM) dengan GPU di Google Kubernetes Engine (GKE) menggunakan beberapa GPU untuk inferensi yang efisien dan skalabel. Tutorial ini membuat cluster GKE yang menggunakan beberapa GPU L4 dan menyiapkan infrastruktur GKE untuk menayangkan salah satu model berikut:

Bergantung pada format data model, jumlah GPU bervariasi. Dalam tutorial ini, setiap model menggunakan dua GPU L4. Untuk mempelajari lebih lanjut, lihat Menghitung jumlah GPU.

Sebelum menyelesaikan tutorial ini di GKE, sebaiknya pelajari Tentang GPU di GKE.

Tujuan

Tutorial ini ditujukan untuk engineer MLOps atau DevOps atau administrator platform yang ingin menggunakan kemampuan orkestrasi GKE untuk menayangkan LLM.

Tutorial ini membahas langkah-langkah berikut:

  1. Buat cluster dan node pool.
  2. Siapkan workload Anda.
  3. Men-deploy workload Anda.
  4. Berinteraksi dengan antarmuka LLM.

Sebelum memulai

Sebelum memulai, pastikan Anda telah menjalankan tugas berikut:

  • Aktifkan Google Kubernetes Engine API.
  • Aktifkan Google Kubernetes Engine API
  • Jika ingin menggunakan Google Cloud CLI untuk tugas ini, instal lalu lakukan inisialisasi gcloud CLI. Jika sebelumnya Anda telah menginstal gcloud CLI, dapatkan versi terbaru dengan menjalankan gcloud components update.
  • Beberapa model memiliki persyaratan tambahan. Pastikan Anda memenuhi persyaratan berikut:

Menyiapkan lingkungan Anda

  1. Di konsol Google Cloud, mulai instance Cloud Shell:
    Buka Cloud Shell

  2. Tetapkan variabel lingkungan default:

    gcloud config set project PROJECT_ID
    export PROJECT_ID=$(gcloud config get project)
    export REGION=us-central1
    

    Ganti PROJECT_ID dengan project ID Google Cloud Anda.

Membuat cluster dan node pool GKE

Anda dapat menayangkan LLM di GPU dalam cluster GKE Autopilot atau Standard. Sebaiknya gunakan cluster Autopilot untuk pengalaman Kubernetes yang dikelola sepenuhnya. Untuk memilih mode operasi GKE yang paling sesuai untuk workload Anda, lihat Memilih mode operasi GKE.

Autopilot

  1. Jalankan perintah berikut di Cloud Shell:

    gcloud container clusters create-auto l4-demo \
      --project=${PROJECT_ID} \
      --region=${REGION} \
      --release-channel=rapid
    

    GKE membuat cluster Autopilot dengan node CPU dan GPU seperti yang diminta oleh beban kerja yang di-deploy.

  2. Konfigurasi kubectl untuk berkomunikasi dengan cluster Anda:

    gcloud container clusters get-credentials l4-demo --region=${REGION}
    

Standard

  1. Di Cloud Shell, jalankan perintah berikut untuk membuat cluster Standard yang menggunakan Workload Identity Federation untuk GKE:

    gcloud container clusters create l4-demo --location ${REGION} \
      --workload-pool ${PROJECT_ID}.svc.id.goog \
      --enable-image-streaming \
      --node-locations=$REGION-a \
      --workload-pool=${PROJECT_ID}.svc.id.goog \
      --machine-type n2d-standard-4 \
      --num-nodes 1 --min-nodes 1 --max-nodes 5 \
      --release-channel=rapid
    

    Pembuatan cluster mungkin memerlukan waktu beberapa menit.

  2. Jalankan perintah berikut untuk membuat node pool untuk cluster Anda:

    gcloud container node-pools create g2-standard-24 --cluster l4-demo \
      --accelerator type=nvidia-l4,count=2,gpu-driver-version=latest \
      --machine-type g2-standard-24 \
      --enable-autoscaling --enable-image-streaming \
      --num-nodes=0 --min-nodes=0 --max-nodes=3 \
      --node-locations $REGION-a,$REGION-c --region $REGION --spot
    

    GKE membuat resource berikut untuk LLM:

    • Cluster edisi Standard Google Kubernetes Engine (GKE) publik.
    • Node pool dengan jenis mesin g2-standard-24 yang diskalakan ke 0 node. Anda tidak akan dikenai biaya untuk GPU apa pun hingga meluncurkan Pod yang meminta GPU. Node pool ini menyediakan Spot VM, yang harganya lebih rendah daripada VM Compute Engine standar default dan tidak memberikan jaminan ketersediaan. Anda dapat menghapus tanda --spot dari perintah ini, dan pemilih node cloud.google.com/gke-spot di konfigurasi text-generation-inference.yaml untuk menggunakan VM on-demand.
  3. Konfigurasi kubectl untuk berkomunikasi dengan cluster Anda:

    gcloud container clusters get-credentials l4-demo --region=${REGION}
    

Menyiapkan workload

Bagian berikut menunjukkan cara menyiapkan beban kerja, bergantung pada model yang ingin Anda gunakan:

Llama 3 70b

  1. Tetapkan variabel lingkungan default:

    export HF_TOKEN=HUGGING_FACE_TOKEN
    

    Ganti HUGGING_FACE_TOKEN dengan token HuggingFace Anda.

  2. Buat secret Kubernetes untuk token HuggingFace:

    kubectl create secret generic l4-demo \
        --from-literal=HUGGING_FACE_TOKEN=${HF_TOKEN} \
        --dry-run=client -o yaml | kubectl apply -f -
    
  3. Buat manifes text-generation-inference.yaml berikut:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: llm
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: llm
      template:
        metadata:
          labels:
            app: llm
        spec:
          containers:
          - name: llm
            image: us-docker.pkg.dev/deeplearning-platform-release/gcr.io/huggingface-text-generation-inference-cu124.2-3.ubuntu2204.py311
            resources:
              requests:
                cpu: "10"
                memory: "60Gi"
                nvidia.com/gpu: "2"
              limits:
                cpu: "10"
                memory: "60Gi"
                nvidia.com/gpu: "2"
            env:
            - name: MODEL_ID
              value: meta-llama/Meta-Llama-3-70B-Instruct
            - name: NUM_SHARD
              value: "2"
            - name: MAX_INPUT_TOKENS
              value: "2048"
            - name: PORT
              value: "8080"
            - name: QUANTIZE
              value: bitsandbytes-nf4
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: l4-demo
                  key: HUGGING_FACE_TOKEN
            volumeMounts:
              - mountPath: /dev/shm
                name: dshm
              - mountPath: /data
                name: ephemeral-volume
          volumes:
            - name: dshm
              emptyDir:
                  medium: Memory
            - name: ephemeral-volume
              ephemeral:
                volumeClaimTemplate:
                  metadata:
                    labels:
                      type: ephemeral
                  spec:
                    accessModes: ["ReadWriteOnce"]
                    storageClassName: "premium-rwo"
                    resources:
                      requests:
                        storage: 150Gi
          nodeSelector:
            cloud.google.com/gke-accelerator: "nvidia-l4"
            cloud.google.com/gke-spot: "true"

    Dalam manifes ini:

    • NUM_SHARD harus 2 karena model memerlukan dua GPU NVIDIA L4.
    • QUANTIZE ditetapkan ke bitsandbytes-nf4 yang berarti model dimuat dalam 4 bit, bukan 32 bit. Hal ini memungkinkan GKE untuk mengurangi jumlah memori GPU yang diperlukan dan meningkatkan kecepatan inferensi. Namun, akurasi model dapat menurun. Untuk mempelajari cara menghitung GPU yang akan diminta, lihat Menghitung jumlah GPU.
  4. Terapkan manifes:

    kubectl apply -f text-generation-inference.yaml
    

    Outputnya mirip dengan hal berikut ini:

    deployment.apps/llm created
    
  5. Verifikasi status model:

    kubectl get deploy
    

    Outputnya mirip dengan hal berikut ini:

    NAME          READY   UP-TO-DATE   AVAILABLE   AGE
    llm           1/1     1            1           20m
    
  6. Lihat log dari deployment yang berjalan:

    kubectl logs -l app=llm
    

    Outputnya mirip dengan hal berikut ini:

    {"timestamp":"2024-03-09T05:08:14.751646Z","level":"INFO","message":"Warming up model","target":"text_generation_router","filename":"router/src/main.rs","line_number":291}
    {"timestamp":"2024-03-09T05:08:19.961136Z","level":"INFO","message":"Setting max batch total tokens to 133696","target":"text_generation_router","filename":"router/src/main.rs","line_number":328}
    {"timestamp":"2024-03-09T05:08:19.961164Z","level":"INFO","message":"Connected","target":"text_generation_router","filename":"router/src/main.rs","line_number":329}
    {"timestamp":"2024-03-09T05:08:19.961171Z","level":"WARN","message":"Invalid hostname, defaulting to 0.0.0.0","target":"text_generation_router","filename":"router/src/main.rs","line_number":343}
    

Mixtral 8x7b

  1. Tetapkan variabel lingkungan default:

    export HF_TOKEN=HUGGING_FACE_TOKEN
    

    Ganti HUGGING_FACE_TOKEN dengan token HuggingFace Anda.

  2. Buat secret Kubernetes untuk token HuggingFace:

    kubectl create secret generic l4-demo \
        --from-literal=HUGGING_FACE_TOKEN=${HF_TOKEN} \
        --dry-run=client -o yaml | kubectl apply -f -
    
  3. Buat manifes text-generation-inference.yaml berikut:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: llm
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: llm
      template:
        metadata:
          labels:
            app: llm
        spec:
          containers:
          - name: llm
            image: us-docker.pkg.dev/deeplearning-platform-release/gcr.io/huggingface-text-generation-inference-cu124.2-3.ubuntu2204.py311
            resources:
              requests:
                cpu: "5"
                memory: "40Gi"
                nvidia.com/gpu: "2"
              limits:
                cpu: "5"
                memory: "40Gi"
                nvidia.com/gpu: "2"
            env:
            - name: MODEL_ID
              value: mistralai/Mixtral-8x7B-Instruct-v0.1
            - name: NUM_SHARD
              value: "2"
            - name: PORT
              value: "8080"
            - name: QUANTIZE
              value: bitsandbytes-nf4
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: l4-demo
                  key: HUGGING_FACE_TOKEN          
            volumeMounts:
              - mountPath: /dev/shm
                name: dshm
              - mountPath: /data
                name: ephemeral-volume
          volumes:
            - name: dshm
              emptyDir:
                  medium: Memory
            - name: ephemeral-volume
              ephemeral:
                volumeClaimTemplate:
                  metadata:
                    labels:
                      type: ephemeral
                  spec:
                    accessModes: ["ReadWriteOnce"]
                    storageClassName: "premium-rwo"
                    resources:
                      requests:
                        storage: 100Gi
          nodeSelector:
            cloud.google.com/gke-accelerator: "nvidia-l4"
            cloud.google.com/gke-spot: "true"

    Dalam manifes ini:

    • NUM_SHARD harus 2 karena model memerlukan dua GPU NVIDIA L4.
    • QUANTIZE ditetapkan ke bitsandbytes-nf4 yang berarti model dimuat dalam 4 bit, bukan 32 bit. Hal ini memungkinkan GKE untuk mengurangi jumlah memori GPU yang diperlukan dan meningkatkan kecepatan inferensi. Namun, hal ini dapat mengurangi akurasi model. Untuk mempelajari cara menghitung GPU yang akan diminta, lihat Menghitung jumlah GPU.
  4. Terapkan manifes:

    kubectl apply -f text-generation-inference.yaml
    

    Outputnya mirip dengan hal berikut ini:

    deployment.apps/llm created
    
  5. Verifikasi status model:

    watch kubectl get deploy
    

    Output-nya mirip dengan yang berikut ini saat deployment siap. Untuk keluar dari smartwatch, ketik CTRL + C:

    NAME          READY   UP-TO-DATE   AVAILABLE   AGE
    llm           1/1     1            1           10m
    
  6. Lihat log dari deployment yang berjalan:

    kubectl logs -l app=llm
    

    Outputnya mirip dengan hal berikut ini:

    {"timestamp":"2024-03-09T05:08:14.751646Z","level":"INFO","message":"Warming up model","target":"text_generation_router","filename":"router/src/main.rs","line_number":291}
    {"timestamp":"2024-03-09T05:08:19.961136Z","level":"INFO","message":"Setting max batch total tokens to 133696","target":"text_generation_router","filename":"router/src/main.rs","line_number":328}
    {"timestamp":"2024-03-09T05:08:19.961164Z","level":"INFO","message":"Connected","target":"text_generation_router","filename":"router/src/main.rs","line_number":329}
    {"timestamp":"2024-03-09T05:08:19.961171Z","level":"WARN","message":"Invalid hostname, defaulting to 0.0.0.0","target":"text_generation_router","filename":"router/src/main.rs","line_number":343}
    

Falcon 40b

  1. Buat manifes text-generation-inference.yaml berikut:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: llm
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: llm
      template:
        metadata:
          labels:
            app: llm
        spec:
          containers:
          - name: llm
            image: us-docker.pkg.dev/deeplearning-platform-release/gcr.io/huggingface-text-generation-inference-cu124.2-3.ubuntu2204.py311
            resources:
              requests:
                cpu: "10"
                memory: "60Gi"
                nvidia.com/gpu: "2"
              limits:
                cpu: "10"
                memory: "60Gi"
                nvidia.com/gpu: "2"
            env:
            - name: MODEL_ID
              value: tiiuae/falcon-40b-instruct
            - name: NUM_SHARD
              value: "2"
            - name: PORT
              value: "8080"
            - name: QUANTIZE
              value: bitsandbytes-nf4
            volumeMounts:
              - mountPath: /dev/shm
                name: dshm
              - mountPath: /data
                name: ephemeral-volume
          volumes:
            - name: dshm
              emptyDir:
                  medium: Memory
            - name: ephemeral-volume
              ephemeral:
                volumeClaimTemplate:
                  metadata:
                    labels:
                      type: ephemeral
                  spec:
                    accessModes: ["ReadWriteOnce"]
                    storageClassName: "premium-rwo"
                    resources:
                      requests:
                        storage: 175Gi
          nodeSelector:
            cloud.google.com/gke-accelerator: "nvidia-l4"
            cloud.google.com/gke-spot: "true"

    Dalam manifes ini:

    • NUM_SHARD harus 2 karena model memerlukan dua GPU NVIDIA L4.
    • QUANTIZE ditetapkan ke bitsandbytes-nf4 yang berarti model dimuat dalam 4 bit, bukan 32 bit. Hal ini memungkinkan GKE untuk mengurangi jumlah memori GPU yang diperlukan dan meningkatkan kecepatan inferensi. Namun, akurasi model dapat menurun. Untuk mempelajari cara menghitung GPU yang akan diminta, lihat Menghitung jumlah GPU.
  2. Terapkan manifes:

    kubectl apply -f text-generation-inference.yaml
    

    Outputnya mirip dengan hal berikut ini:

    deployment.apps/llm created
    
  3. Verifikasi status model:

    watch kubectl get deploy
    

    Output-nya mirip dengan yang berikut ini saat deployment siap. Untuk keluar dari smartwatch, ketik CTRL + C:

    NAME          READY   UP-TO-DATE   AVAILABLE   AGE
    llm           1/1     1            1           10m
    
  4. Lihat log dari deployment yang berjalan:

    kubectl logs -l app=llm
    

    Outputnya mirip dengan hal berikut ini:

    {"timestamp":"2024-03-09T05:08:14.751646Z","level":"INFO","message":"Warming up model","target":"text_generation_router","filename":"router/src/main.rs","line_number":291}
    {"timestamp":"2024-03-09T05:08:19.961136Z","level":"INFO","message":"Setting max batch total tokens to 133696","target":"text_generation_router","filename":"router/src/main.rs","line_number":328}
    {"timestamp":"2024-03-09T05:08:19.961164Z","level":"INFO","message":"Connected","target":"text_generation_router","filename":"router/src/main.rs","line_number":329}
    {"timestamp":"2024-03-09T05:08:19.961171Z","level":"WARN","message":"Invalid hostname, defaulting to 0.0.0.0","target":"text_generation_router","filename":"router/src/main.rs","line_number":343}
    

Membuat Service jenis ClusterIP

  1. Buat manifes llm-service.yaml berikut:

    apiVersion: v1
    kind: Service
    metadata:
      name: llm-service
    spec:
      selector:
        app: llm
      type: ClusterIP
      ports:
        - protocol: TCP
          port: 80
          targetPort: 8080
    
  2. Terapkan manifes:

    kubectl apply -f llm-service.yaml
    

Men-deploy antarmuka chat

Gunakan Gradio untuk mem-build aplikasi web yang memungkinkan Anda berinteraksi dengan model. Gradio adalah library Python yang memiliki wrapper ChatInterface yang membuat antarmuka pengguna untuk chatbot.

Llama 3 70b

  1. Buat file bernama gradio.yaml:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: gradio
      labels:
        app: gradio
    spec:
      strategy: 
        type: Recreate
      replicas: 1
      selector:
        matchLabels:
          app: gradio
      template:
        metadata:
          labels:
            app: gradio
        spec:
          containers:
          - name: gradio
            image: us-docker.pkg.dev/google-samples/containers/gke/gradio-app:v1.0.3
            resources:
              requests:
                cpu: "512m"
                memory: "512Mi"
              limits:
                cpu: "1"
                memory: "512Mi"
            env:
            - name: CONTEXT_PATH
              value: "/generate"
            - name: HOST
              value: "http://llm-service"
            - name: LLM_ENGINE
              value: "tgi"
            - name: MODEL_ID
              value: "meta-llama/Meta-Llama-3-70B-Instruct"
            - name: USER_PROMPT
              value: "<|begin_of_text|><|start_header_id|>user<|end_header_id|> prompt <|eot_id|><|start_header_id|>assistant<|end_header_id|>"
            - name: SYSTEM_PROMPT
              value: "prompt <|eot_id|>"
            ports:
            - containerPort: 7860
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: gradio-service
    spec:
      type: LoadBalancer
      selector:
        app: gradio
      ports:
      - port: 80
        targetPort: 7860
  2. Terapkan manifes:

    kubectl apply -f gradio.yaml
    
  3. Temukan alamat IP eksternal Layanan:

    kubectl get svc
    

    Outputnya mirip dengan hal berikut ini:

    NAME             TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
    gradio-service   LoadBalancer   10.24.29.197   34.172.115.35   80:30952/TCP   125m
    
  4. Salin alamat IP eksternal dari kolom EXTERNAL-IP.

  5. Lihat antarmuka model dari browser web dengan menggunakan alamat IP eksternal dengan port yang diekspos:

    http://EXTERNAL_IP
    

Mixtral 8x7b

  1. Buat file bernama gradio.yaml:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: gradio
      labels:
        app: gradio
    spec:
      strategy: 
        type: Recreate
      replicas: 1
      selector:
        matchLabels:
          app: gradio
      template:
        metadata:
          labels:
            app: gradio
        spec:
          containers:
          - name: gradio
            image: us-docker.pkg.dev/google-samples/containers/gke/gradio-app:v1.0.0
            resources:
              requests:
                cpu: "512m"
                memory: "512Mi"
              limits:
                cpu: "1"
                memory: "512Mi"
            env:
            - name: CONTEXT_PATH
              value: "/generate"
            - name: HOST
              value: "http://llm-service"
            - name: LLM_ENGINE
              value: "tgi"
            - name: MODEL_ID
              value: "mixtral-8x7b"
            - name: USER_PROMPT
              value: "[INST] prompt [/INST]"
            - name: SYSTEM_PROMPT
              value: "prompt"
            ports:
            - containerPort: 7860
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: gradio-service
    spec:
      type: LoadBalancer
      selector:
        app: gradio
      ports:
      - port: 80
        targetPort: 7860
  2. Terapkan manifes:

    kubectl apply -f gradio.yaml
    
  3. Temukan alamat IP eksternal Layanan:

    kubectl get svc
    

    Outputnya mirip dengan hal berikut ini:

    NAME             TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
    gradio-service   LoadBalancer   10.24.29.197   34.172.115.35   80:30952/TCP   125m
    
  4. Salin alamat IP eksternal dari kolom EXTERNAL-IP.

  5. Lihat antarmuka model dari browser web Anda menggunakan alamat IP eksternal dengan port yang diekspos:

    http://EXTERNAL_IP
    

Falcon 40b

  1. Buat file bernama gradio.yaml:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: gradio
      labels:
        app: gradio
    spec:
      strategy: 
        type: Recreate
      replicas: 1
      selector:
        matchLabels:
          app: gradio
      template:
        metadata:
          labels:
            app: gradio
        spec:
          containers:
          - name: gradio
            image: us-docker.pkg.dev/google-samples/containers/gke/gradio-app:v1.0.0
            resources:
              requests:
                cpu: "512m"
                memory: "512Mi"
              limits:
                cpu: "1"
                memory: "512Mi"
            env:
            - name: CONTEXT_PATH
              value: "/generate"
            - name: HOST
              value: "http://llm-service"
            - name: LLM_ENGINE
              value: "tgi"
            - name: MODEL_ID
              value: "falcon-40b-instruct"
            - name: USER_PROMPT
              value: "User: prompt"
            - name: SYSTEM_PROMPT
              value: "Assistant: prompt"
            ports:
            - containerPort: 7860
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: gradio-service
    spec:
      type: LoadBalancer
      selector:
        app: gradio
      ports:
      - port: 80
        targetPort: 7860
  2. Terapkan manifes:

    kubectl apply -f gradio.yaml
    
  3. Temukan alamat IP eksternal Layanan:

    kubectl get svc
    

    Outputnya mirip dengan hal berikut ini:

    NAME             TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
    gradio-service   LoadBalancer   10.24.29.197   34.172.115.35   80:30952/TCP   125m
    
  4. Salin alamat IP eksternal dari kolom EXTERNAL-IP.

  5. Lihat antarmuka model dari browser web menggunakan alamat IP eksternal dengan port yang diekspos:

    http://EXTERNAL_IP
    

Menghitung jumlah GPU

Jumlah GPU bergantung pada nilai flag QUANTIZE. Dalam tutorial ini, QUANTIZE disetel ke bitsandbytes-nf4, yang berarti model dimuat dalam 4 bit.

Model parameter 70 miliar akan memerlukan memori GPU minimal 40 GB yang setara dengan 70 miliar kali 4 bit (70 miliar x 4 bit= 35 GB) dan mempertimbangkan overhead 5 GB. Dalam hal ini, satu GPU L4 tidak akan memiliki memori yang cukup. Oleh karena itu, contoh dalam tutorial ini menggunakan dua memori GPU L4 (2 x 24 = 48 GB). Konfigurasi ini sudah memadai untuk menjalankan Falcon 40b atau Llama 3 70b di GPU L4.

Pembersihan

Agar tidak perlu membayar biaya pada akun Google Cloud Anda untuk resource yang digunakan dalam tutorial ini, hapus project yang berisi resource tersebut, atau simpan project dan hapus setiap resource.

Menghapus cluster

Agar tidak menimbulkan biaya pada akun Google Cloud Anda untuk resource yang dibuat dalam panduan ini, hapus cluster GKE:

gcloud container clusters delete l4-demo --region ${REGION}

Langkah selanjutnya