Implementasi referensi LLM dengan bobot terbuka di GDC dengan air gap

Ringkasan

Dokumen ini memberikan petunjuk langkah demi langkah untuk men-deploy Model Bahasa Besar (LLM) open weight seperti Gemma, Llama, dan DeepSeek di lingkungan air-gapped Google Distributed Cloud (GDC). Codelab ini membahas penggunaan vLLM untuk penyajian dengan throughput tinggi dan Ollama untuk kemudahan penggunaan, dengan memanfaatkan kemampuan platform GDC, termasuk Kubernetes, Harbor, dan resource GPU.

Arsitektur

Solusinya melibatkan deployment backend penyajian LLM dalam container (vLLM, Ollama) sebagai Deployment dalam cluster pengguna. Bobot model disimpan di Persistent Volumes, yang diisi dari image di registry Harbor. Layanan Kubernetes jenis LoadBalancer mengekspos API backend. Kebijakan jaringan project mengamankan akses ke layanan ini.

Diagram arsitektur penerapan referensi LLM berat terbuka.

Sebelum memulai

Pastikan prasyarat berikut terpenuhi:

  • GDC dengan air gap versi 1.15.1 atau yang lebih tinggi.
  • Cluster pengguna dibuat dengan resource yang memadai (CPU, Memori, GPU).
  • Minimal diperlukan 1 GPU NVIDIA A100.
  • Instance Harbor tersedia dan dapat diakses.
  • CLI kubectl dan gdcloud yang dikonfigurasi untuk mengakses cluster pengguna.
  • Klien Docker diinstal dan dikonfigurasi untuk mengirim ke Harbor.
  • Izin IAM yang diperlukan telah diberikan (misalnya, Namespace Admin, Cluster Developer**).
  • Akun dan autentikasi Hugging Face dikonfigurasi jika menggunakan model yang dibatasi.

Bagian 1: Penyiapan umum

1.1 Buat secret penarikan gambar

Untuk mengonfigurasi secret penarikan image untuk workload container di GDC dengan air gap, Anda perlu membuat secret docker-registry Kubernetes yang berisi kredensial untuk mengakses project Harbor pribadi Anda. Secret ini kemudian dirujuk dalam spesifikasi deployment Anda.

Anda harus menggunakan akun robot Harbor untuk akses terprogram ke image dalam project Harbor pribadi.

Ikuti langkah-langkah berikut untuk mengonfigurasi secret penarikan image:

Buat akun robot Harbor:

  • Buka UI instance Harbor Anda.
  • Buka project Harbor Anda.
  • Pilih tab Akun Robot.
  • Klik New Robot Account.
  • Beri nama (misalnya, oss-llm-puller) dan beri izin yang diperlukan (setidaknya akses tarik) hingga waktu habis masa berlaku.
  • Simpan nama akun robot (misalnya, robot$oss-llm-puller) dan token rahasia yang diberikan dengan aman.

Mengautentikasi Docker ke Harbor:

Di komputer Anda yang telah menginstal Docker dan memiliki akses jaringan ke registry Harbor, login menggunakan kredensial akun robot:

export INSTANCE_URL="HARBOR_INSTANCE_URL"
# for example, harbor1-project1.org1.zone1.google.gdc.com

export ROBOT_NAME="ROBOT_ACCOUNT_NAME"
# for example, robot\$oss-llm-puller (note how we escape the $ character)

export ROBOT_SECRET="ROBOT_ACCOUNT_SECRET"

docker login ${INSTANCE_URL} --username ${ROBOT_NAME} --password ${ROBOT_SECRET}

Buat secret penarikan image Kubernetes:

Gunakan kubectl untuk membuat secret jenis docker-registry di namespace project Anda, menggunakan file konfigurasi Docker yang diperbarui pada langkah sebelumnya:

# Log in into GDC environment using the next commands
gdcloud auth login --login-config-cert WEB_TLS_CERT_PATH
gdcloud clusters get-credentials KUBERNETES_CLUSTER
kubectl config set-context --current --namespace=NAMESPACE

export SECRET_NAME="OSS_LLM_PULL_SECRET"
export NAMESPACE="PROJECT_NAMESPACE"
# Assuming default Docker config path. Adjust if necessary.
export DOCKER_CONFIG_PATH="$HOME/.docker/config.json"

kubectl create secret docker-registry ${SECRET_NAME} \
      --from-file=.dockerconfigjson=${DOCKER_CONFIG_PATH} \
      -n ${NAMESPACE}

Bagian 2: Men-deploy dengan vLLM

2.1 Mendapatkan image Docker vLLM

Di komputer dengan akses internet, tarik image Docker vLLM, lalu transfer ke project Harbor Anda:

# Pull and Tag vLLM (v0.13.0 recommended for stability)
docker pull vllm/vllm-openai:v0.13.0
docker tag vllm/vllm-openai:v0.13.0 HARBOR_URL/PROJECT/vllm-openai:v0.13.0
docker push HARBOR_URL/PROJECT/vllm-openai:v0.13.0

Ganti HARBOR_URL dan PROJECT dengan URL instance Harbor dan nama project Anda.

2.2 Menyiapkan bobot model di PVC

Buat file YAML (misalnya, model-pvc.yaml):

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: model-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 500Gi
  storageClassName: standard-rwo
  volumeMode: Filesystem

Terapkan PVC: kubectl apply -f model-pvc.yaml

Download bobot dari Hugging Face:

hf auth login
hf download google/gemma-3-4b-it

Gunakan pod helper (misalnya, helper-pod.yaml) untuk mentransfer bobot ke PVC. Pastikan Anda memiliki image busybox di Harbor.

# Push busybox if not present
docker pull busybox:latest
docker tag busybox HARBOR_URL/PROJECT/busybox:latest
docker push HARBOR_URL/PROJECT/busybox:latest

# Contents of helper-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: model-uploader
spec:
  containers:
  - name: uploader
    image: HARBOR_URL/PROJECT/busybox:latest
    command: ["sleep", "3600"]
    volumeMounts:
    - name: model-data
      mountPath: /data
  imagePullSecrets:
  - name: oss-llm-pull-secret
  volumes:
  - name: model-data
    persistentVolumeClaim:
      claimName: model-pvc

Terapkan pod dan salin file:

kubectl apply -f helper-pod.yaml
# Wait for pod to be Running
kubectl cp ~/.cache/huggingface/hub/ NAMESPACE/model-uploader:/data/
kubectl delete pod model-uploader

2.3 Men-deploy backend vLLM

Buat file deployment vllm-gemma-3-4b-it-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gemma-3-4b-it
  labels:
    app: gemma-3-4b-it
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gemma-3-4b-it
  template:
    metadata:
      labels:
        app: gemma-3-4b-it
    spec:
      volumes:
      - name: cache-volume
        persistentVolumeClaim:
          claimName: model-pvc
      - name: shm
        emptyDir:
          medium: Memory
          sizeLimit: "16Gi"
      containers:
      - name: gemma-3-4b-it
        image: HARBOR_URL/PROJECT/vllm-openai:v0.13.0
        command: ["python3"]
        args: [
          "-m",
          "vllm.entrypoints.openai.api_server",
          "--model",
          "google/gemma-3-4b-it",
          "--max-model-len",
          "32768",
          "--enforce-eager"
        ]
        env:
        - name: HF_HUB_OFFLINE
          value: "1"
        - name: HF_HOME
          value: "/model"
        - name: NCCL_P2P_DISABLE
          value: "1"
        - name: NCCL_IB_DISABLE
          value: "1"
        - name: BORINGSSL_FIPS
          value: "0"
        - name: OPENSSL_FIPS
          value: "0"
        - name: OPENSSL_CONF
          value: "/dev/null"
        - name: FIPS_SIG
          value: "off"
        ports:
        - containerPort: 8000
        securityContext:
          privileged: true
          runAsUser: 0
        resources:
          limits:
            nvidia.com/gpu-pod-NVIDIA_A100_80GB_PCIE: 1
            cpu: "8"
            memory: "64Gi"
          requests:
            nvidia.com/gpu-pod-NVIDIA_A100_80GB_PCIE: 1
            cpu: "8"
            memory: "32Gi"
        volumeMounts:
        - name: cache-volume
          mountPath: /model
        - name: shm
          mountPath: /dev/shm
      imagePullSecrets:
      - name: oss-llm-pull-secret

Buat file layanan vllm-gemma-3-4b-it-service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: gemma-3-4b-it
  namespace: NAMESPACE
spec:
  ports:
  - name: http-gemma-3-4b-it
    port: 80
    protocol: TCP
    targetPort: 8000
  selector:
    app: gemma-3-4b-it
  sessionAffinity: None
  type: LoadBalancer

Terapkan konfigurasi:

kubectl apply -f vllm-gemma-3-4b-it-deployment.yaml
kubectl apply -f vllm-gemma-3-4b-it-service.yaml

2.4 Mengonfigurasi kebijakan jaringan

Terapkan resource ProjectNetworkPolicy untuk mengizinkan traffic masuk ke port layanan vLLM (8000). Buat vllm-netpol.yaml:

apiVersion: networking.gdc.goog/v1
kind: ProjectNetworkPolicy
metadata:
  name: allow-vllm-ingress
  namespace: NAMESPACE
spec:
  subject:
    subjectType: UserWorkload
  policyType: Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 0.0.0.0/0 # Restrict this in production
    ports:
    - protocol: TCP
      port: 8000

Terapkan kebijakan: kubectl apply -f vllm-netpol.yaml

Bagian 3: Men-deploy dengan Ollama

3.1 Siapkan Dockerfile

Buat Dockerfile untuk membangun image Ollama dengan model Anda yang sudah dimuat sebelumnya:

FROM ubuntu

RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates zstd
RUN curl -fsSL https://ollama.com/install.sh -o install.sh
RUN chmod +x install.sh
RUN ./install.sh && \
    rm -rf /var/lib/apt/lists/*

# Pre-pull gemma3 model
RUN ollama serve & \
    sleep 5 && \
    curl --retry 10 --retry-connrefused -s http://localhost:11434 || true && \
    ollama pull gemma3:latest && \
    pkill ollama || true

EXPOSE 11434
CMD ["ollama", "serve"]

3.2 Membangun dan mengirim image

Build dan kirim image:

docker build -t ollama-gemma3 .
docker tag ollama-gemma3 HARBOR_URL/PROJECT/ollama-gemma3:latest
docker push HARBOR_URL/PROJECT/ollama-gemma3:latest

3.3 Deploy backend Ollama

Buat ollama-gemma3.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ollama-gemma3
  namespace: NAMESPACE
  labels:
    app: ollama-gemma3
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ollama-gemma3
  template:
    metadata:
      labels:
        app: ollama-gemma3
    spec:
      containers:
      - name: ollama-gemma3
        image: HARBOR_URL/PROJECT/ollama-gemma3:latest
        env:
        - name: OLLAMA_HOST
          value: "0.0.0.0"
        imagePullPolicy: Always
        ports:
        - containerPort: 11434
        securityContext:
          privileged: true
          runAsUser: 0
        resources:
          limits:
            nvidia.com/gpu-pod-NVIDIA_A100_80GB_PCIE: 1
          requests:
            nvidia.com/gpu-pod-NVIDIA_A100_80GB_PCIE: 1
      imagePullSecrets:
      - name: oss-llm-pull-secret
---
apiVersion: v1
kind: Service
metadata:
  name: ollama-gemma3
  namespace: NAMESPACE
spec:
  type: LoadBalancer
  selector:
    app: ollama-gemma3
  ports:
  - name: ollama-gemma3-port
    port: 11434
    protocol: TCP
    targetPort: 11434

Terapkan manifes: kubectl apply -f ollama-gemma3.yaml

3.4 Mengonfigurasi kebijakan jaringan

Buat ollama-netpol.yaml:

apiVersion: networking.gdc.goog/v1
kind: ProjectNetworkPolicy
metadata:
  name: allow-ollama-ingress
  namespace: NAMESPACE
spec:
  subject:
    subjectType: UserWorkload
  policyType: Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 0.0.0.0/0 # Restrict this for production.
    ports:
    - protocol: TCP
      port: 11434

Terapkan kebijakan: kubectl apply -f ollama-netpol.yaml

Bagian 4: Validasi

Verifikasi deployment dengan memeriksa status pod, alamat IP layanan, dan mengirim permintaan inferensi pengujian menggunakan curl ke alamat IP LoadBalancer untuk vLLM dan Ollama.

Pastikan semua penampung dan layanan Running:

# Login into GDC air-gapped using the next commands
gdcloud auth login --login-config-cert WEB_TLS_CERT_PATH
gdcloud clusters get-credentials KUBERNETES_CLUSTER
kubectl config set-context --current --namespace=NAMESPACE

# Pods
kubectl get pods

# Services
kubectl get services

Menguji vLLM:

export VLLM_IP=$(kubectl get service gemma-3-4b-it -n NAMESPACE -o jsonpath='{.status.loadBalancer.ingress[*].ip}')
curl http://${VLLM_IP}/v1/chat/completion \
  -H "Content-Type: application/json" \
  -d '{
    "model": "google/gemma-3-4b-it",
    "messages": [
      {"role": "user", "content": "What is Google Distributed Cloud air-gapped?"}
    ],
    "max_tokens": 100
  }'

Menguji Ollama:

export OLLAMA_IP=$(kubectl get service ollama-gemma3 -n NAMESPACE -o jsonpath='{.status.loadBalancer.ingress[*].ip}')
# Check if Ollama is running
curl http://${OLLAMA_IP}:11434
# Send a completion request
curl -X POST http://${OLLAMA_IP}:11434/v1/completions \
-H "Content-Type: application/json" \
-d '{
  "model": "gemma3:latest",
  "prompt": "Google Distributed Cloud air-gapped is a",
  "max_tokens": 128,
  "temperature": 0.90,
  "stream": false
}'

Bagian 5: Operasi dan pemecahan masalah

5.1 Operasi vLLM

Periksa Log: kubectl logs -f -n NAMESPACE

Kueri Status Internal (dari pod debug):

wget -qO- http://gemma-3-4b-it/v1/models
wget -qO- http://gemma-3-4b-it/health

5.2 Operasi Ollama

Akses CLI: kubectl exec -it -n NAMESPACE -- sh

Di dalam pod: ollama list, ollama ps

5.3 Penskalaan

Menskalakan solusi dengan backend Ollama

Secara vertikal

  • Alokasikan slice GPU yang lebih besar untuk LLM Anda yang menjadi hambatan hingga Anda menggunakan GPU penuh.
  • Jika ingin menggunakan LLM yang lebih besar untuk akurasi respons yang lebih baik, misalnya, LLM dengan parameter 405B, bukan 7B, Anda mungkin memerlukan lebih dari satu GPU agar dapat berjalan dengan lancar.
  • Model yang cocok dengan lebih dari satu GPU mengalami beberapa latensi terkait dengan komunikasi antar-GPU.

Secara horizontal

  • Deploy pod Ollama sebanyak yang Anda perlukan untuk mencapai target throughput pada LLM tertentu.
    • Untuk melakukannya, tingkatkan jumlah replika dalam file YAML deployment Ollama yang sesuai.
  • Layanan Kubernetes, yang berjenis LoadBalancer, akan mendistribusikan permintaan bantuan kode di antara endpoint (yaitu pod) dan menampilkan respons masing-masing melalui IP eksternal yang terekspos.
  • Ingat, plugin Lanjutkan hanya mengarah ke satu alamat IP per fungsi.

Diagram arsitektur penskalaan LLM dengan bobot terbuka.

Untuk menskalakan backend vLLM dalam lingkungan air-gapped GDC, Anda dapat mengikuti strategi yang serupa dengan yang digunakan untuk Ollama, dengan berfokus pada alokasi resource hardware dan replikasi pod.

Menskalakan solusi dengan backend vLLM

Secara vertikal

  • Mengupgrade Alokasi GPU: Jika throughput inferensi (token/dtk) menjadi penghambat, alokasikan slice GPU yang lebih besar hingga Anda menggunakan GPU NVIDIA A100 penuh.
  • Konfigurasi Multi-GPU: Untuk model berukuran sangat besar (misalnya, parameter 70B hingga 405B) yang tidak muat dalam memori satu A100 80 GB, Anda harus menskalakan ke beberapa GPU menggunakan paralelisme tensor.
  • Pertimbangan Latensi: Perhatikan bahwa model yang mencakup lebih dari satu GPU mungkin mengalami sedikit overhead terkait komunikasi antar-GPU (misalnya, sinkronisasi NCCL).

Secara horizontal

  • Meningkatkan Throughput melalui Replika: Untuk menangani volume permintaan pengguna serentak yang lebih tinggi untuk model yang sama, tingkatkan jumlah replika di YAML deployment vLLM Anda.
  • Instance Model Khusus: Karena vLLM dirancang sebagai mesin penayangan model tunggal dan menyematkan memori cache KV-nya saat inisialisasi, Anda harus men-deploy serangkaian pod terpisah untuk setiap LLM berbeda yang ingin dihosting.
  • Load Balancing: Layanan Kubernetes GDC (jenis LoadBalancer) akan otomatis mendistribusikan permintaan inferensi masuk di antara semua endpoint pod vLLM yang sehat dan terkait dengan layanan tersebut.

5.4 Pemecahan masalah

Error umum dan mitigasi.

Error Mitigasi
FIPS SELFTEST FAILURE Terjadi saat library seperti BoringSSL tidak memiliki tanda tangan integritas. Perbaiki dengan menyetel BORINGSSL_FIPS=0 dan menggunakan image vLLM resmi
Pemuatan Berat yang Tertunda Periksa apakah ada throttling IOPS pada PVC kecil. Volume 500 GiB diperlukan untuk inisialisasi model performa.
Koneksi Ditolak Pastikan PNP secara eksplisit mengizinkan targetPort (8000/8080). Firewall GDC tidak otomatis memberikan akses ke port backend untuk VIP Load Balancer.