Übersicht
In diesem Dokument finden Sie eine detaillierte Anleitung zum Bereitstellen von Open-Weight-LLMs (Large Language Models) wie Gemma, Llama und DeepSeek in GDC-Umgebungen (Google Distributed Cloud) mit Air Gap. Dabei wird sowohl vLLM für die Bereitstellung mit hohem Durchsatz als auch Ollama für die einfache Verwendung genutzt. Außerdem werden die Funktionen der GDC-Plattform genutzt, darunter Kubernetes, Harbor und GPU-Ressourcen.
Architektur
Die Lösung umfasst die Bereitstellung von containerisierten LLM-Bereitstellungs-Back-Ends (vLLM, Ollama) als Deployments in einem Nutzercluster. Modellgewichte werden auf nichtflüchtigen Volumes gespeichert, die aus Bildern in der Harbor-Registry gefüllt werden. Kubernetes-Dienste vom Typ „LoadBalancer“ machen die APIs der Back-Ends verfügbar. Richtlinien für das Projektnetzwerk sichern den Zugriff auf diese Dienste.

Hinweis
Prüfen Sie, ob die folgenden Voraussetzungen erfüllt sind:
- GDC mit Air Gap, Version 1.15.1 oder höher.
- Der Nutzercluster wurde mit ausreichenden Ressourcen (CPU, Arbeitsspeicher, GPU) erstellt.
- Es ist mindestens eine NVIDIA A100-GPU erforderlich.
- Die Harbor-Instanz ist verfügbar und zugänglich.
- Die
kubectl- undgdcloud-Befehlszeilen sind für den Zugriff auf den Nutzercluster konfiguriert. - Der Docker-Client ist installiert und für das Pushen in Harbor konfiguriert.
- Die erforderlichen IAM-Berechtigungen wurden gewährt (z. B. Namespace-Administrator, Cluster Developer**).
- Wenn Sie Modelle mit eingeschränktem Zugriff verwenden, müssen Sie ein Hugging Face-Konto haben und die Authentifizierung muss konfiguriert sein.
Abschnitt 1: Gemeinsame Einrichtung
1.1 Image-Pull-Secret erstellen
Wenn Sie ein Image-Pull-Secret für eine Containerarbeitslast in GDC mit Air Gap konfigurieren möchten, müssen Sie ein Kubernetes-Secret vom Typ „docker-registry“ erstellen, das Anmeldedaten für den Zugriff auf Ihr privates Harbor-Projekt enthält. Auf dieses Secret wird dann in Ihrer Bereitstellungsspezifikation verwiesen.
Für den programmatischen Zugriff auf Bilder in privaten Harbor-Projekten sollten Sie ein Harbor-Roboterkonto verwenden.
So konfigurieren Sie das Image-Pull-Secret:
Harbor-Roboterkonto erstellen:
- Rufen Sie die Benutzeroberfläche Ihrer Harbor-Instanz auf.
- Rufen Sie Ihr Harbor-Projekt auf.
- Klicken Sie auf den Tab Robot Accounts (Roboterkonten).
- Klicken Sie auf Neues Roboterkonto.
- Geben Sie einen Namen an (z. B.
oss-llm-puller) und gewähren Sie die erforderlichen Berechtigungen (mindestens pull-Zugriff) bis zu einem Ablaufdatum. - Bewahren Sie den Namen des Roboterkontos (z. B.
robot$oss-llm-puller) und das bereitgestellte geheime Token sicher auf.
Docker für Harbor authentifizieren:
Melden Sie sich auf Ihrem Computer, auf dem Docker installiert ist und der Netzwerkzugriff auf die Harbor-Registry besteht, mit den Anmeldedaten des Roboter-Kontos an:
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}
Kubernetes-Secret zum Abrufen von Images erstellen:
Erstellen Sie mit kubectl ein Secret vom Typ „docker-registry“ in Ihrem Projektnamespace. Verwenden Sie dazu die im vorherigen Schritt aktualisierte Docker-Konfigurationsdatei:
# 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}
Abschnitt 2: Mit vLLM bereitstellen
2.1 vLLM-Docker-Image abrufen
Rufen Sie auf einem Computer mit Internetzugriff das vLLM-Docker-Image ab und übertragen Sie es dann in Ihr Harbor-Projekt:
# 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
Ersetzen Sie HARBOR_URL und PROJECT durch die URL Ihrer Harbor-Instanz und den Projektnamen.
2.2 Modellgewichte im PVC vorbereiten
Erstellen Sie eine YAML-Datei (z. B. model-pvc.yaml):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: model-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Gi
storageClassName: standard-rwo
volumeMode: Filesystem
Wenden Sie das PVC an: kubectl apply -f model-pvc.yaml
Gewichtungen von Hugging Face herunterladen:
hf auth login
hf download google/gemma-3-4b-it
Verwenden Sie einen Hilfspod (z. B. helper-pod.yaml), um Gewichte auf den PVC zu übertragen.
Prüfen Sie, ob Sie ein busybox-Image in Harbor haben.
# 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
Pod anbringen und Dateien kopieren:
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 vLLM-Back-End bereitstellen
Erstellen Sie die Bereitstellungsdatei 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
Erstellen Sie die Dienstdatei 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
Wenden Sie die Konfigurationen an:
kubectl apply -f vllm-gemma-3-4b-it-deployment.yaml
kubectl apply -f vllm-gemma-3-4b-it-service.yaml
2.4 Netzwerkrichtlinie konfigurieren
Wenden Sie eine ProjectNetworkPolicy-Ressource an, um eingehenden Traffic zum vLLM-Dienstport (8000) zuzulassen. vllm-netpol.yaml erstellen:
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
Wenden Sie die Richtlinie an: kubectl apply -f vllm-netpol.yaml
Abschnitt 3: Mit Ollama bereitstellen
3.1 Dockerfile vorbereiten
Erstellen Sie ein Dockerfile, um das Ollama-Image mit den vorab geladenen Modellen zu erstellen:
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 Image erstellen und per Push übertragen
Erstellen Sie das Image und übertragen Sie es per Push:
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 Ollama-Back-End bereitstellen
ollama-gemma3.yaml erstellen:
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
Wenden Sie das Manifest an: kubectl apply -f ollama-gemma3.yaml
3.4 Netzwerkrichtlinie konfigurieren
ollama-netpol.yaml erstellen:
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
Wenden Sie die Richtlinie an: kubectl apply -f ollama-netpol.yaml
Abschnitt 4: Validierung
Prüfen Sie die Bereitstellungen, indem Sie den Pod-Status und die Service-IP-Adressen prüfen und Testanfragen mit curl an die LoadBalancer-IP-Adressen für vLLM und Ollama senden.
Prüfen Sie, ob alle Container und Dienste Running sind:
# 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
vLLM testen:
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
}'
Ollama testen:
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
}'
Abschnitt 5: Betrieb und Fehlerbehebung
5.1 vLLM-Vorgänge
Logs prüfen: kubectl logs -f -n NAMESPACE
Internen Status abfragen (über einen Debug-Pod):
wget -qO- http://gemma-3-4b-it/v1/models
wget -qO- http://gemma-3-4b-it/health
5.2 Ollama-Vorgänge
Auf die CLI zugreifen: kubectl exec -it -n NAMESPACE -- sh
Im Pod: ollama list, ollama ps
5.3 Skalierung
Lösung mit einem Ollama-Back-End skalieren
Vertikal
- Weisen Sie Ihrem LLM, das zum Engpass geworden ist, einen größeren GPU-Slice zu, bis Sie eine vollständige GPU verwenden.
- Wenn Sie ein größeres LLM für eine bessere Antwortgenauigkeit verwenden möchten, z. B. ein LLM mit 405 Milliarden Parametern anstelle eines mit 7 Milliarden, benötigen Sie möglicherweise mehr als eine GPU, um es reibungslos auszuführen.
- Bei Modellen, die auf mehr als eine GPU passen, kann es zu Latenz aufgrund der GPU-übergreifenden Kommunikation kommen.
Horizontal
- Stellen Sie so viele Ollama-Pods bereit, wie Sie für den gewünschten Durchsatz für ein bestimmtes LLM benötigen.
- Erhöhen Sie dazu die replica-Anzahl in der entsprechenden YAML-Datei für die Ollama-Bereitstellung.
- Der Kubernetes-Dienst vom Typ LoadBalancer verteilt Anfragen zur Codeunterstützung zwischen Endpunkten (d.h. Pods) und gibt die entsprechenden Antworten über die bereitgestellte externe IP-Adresse zurück.
- Das Continue-Plug-in verweist nur auf eine IP-Adresse pro Funktion.

Um das vLLM-Backend in Ihrer GDC-Air-Gap-Umgebung zu skalieren, können Sie eine ähnliche Strategie wie für Ollama verwenden, die sich sowohl auf die Zuweisung von Hardwareressourcen als auch auf die Pod-Replikation konzentriert.
Lösung mit einem vLLM-Backend skalieren
Vertikal
- GPU-Zuweisung upgraden:Wenn der Inferenzdurchsatz (Tokens/Sekunde) zu einem Engpass wird, weisen Sie einen größeren GPU-Slice zu, bis Sie eine vollständige NVIDIA A100-GPU verwenden.
- Konfigurationen mit mehreren GPUs: Bei umfangreichen Modellen (z. B. mit 70 Mrd. bis 405 Mrd. Parametern), die nicht in den Arbeitsspeicher einer einzelnen A100-GPU mit 80 GB passen, müssen Sie mit Tensor-Parallelität auf mehrere GPUs skalieren.
- Latenz: Modelle, die sich über mehrere GPUs erstrecken, können einen leichten Overhead in Bezug auf die GPU-übergreifende Kommunikation (z. B. NCCL-Synchronisierung) aufweisen.
Horizontal
- Durchsatz über Replikate erhöhen: Wenn Sie ein höheres Volumen gleichzeitiger Nutzeranfragen für dasselbe Modell verarbeiten möchten, erhöhen Sie die Anzahl der Replikate in der YAML-Datei für die vLLM-Bereitstellung.
- Dedizierte Modellinstanzen: Da vLLM als Serving-Engine für ein einzelnes Modell konzipiert ist und den KV-Cache-Speicher bei der Initialisierung festlegt, müssen Sie für jedes LLM, das Sie hosten möchten, eine separate Gruppe von Pods bereitstellen.
- Load-Balancing: Der GDC Kubernetes-Dienst (Typ „LoadBalancer“) verteilt eingehende Inferenzanfragen automatisch auf alle fehlerfreien vLLM-Pod-Endpunkte, die diesem Dienst zugeordnet sind.
5.4 Fehlerbehebung
Häufige Fehler und Gegenmaßnahmen.
| Fehler | Problembehebung |
|---|---|
| FIPS-SELBSTTEST FEHLGESCHLAGEN | Tritt auf, wenn Bibliotheken wie BoringSSL keine Integritätssignaturen haben. Fehler beheben, indem Sie BORINGSSL_FIPS=0 festlegen und offizielle vLLM-Images verwenden |
| Gewichtsladen fehlgeschlagen | Prüfen Sie, ob die IOPS bei kleinen PVCs gedrosselt werden. Für die Initialisierung des Leistungsmodells ist ein Volume mit 500 GiB erforderlich. |
| Verbindung verweigert | Prüfen Sie, ob der PNP den targetPort (8000/8080) explizit zulässt. GDC-Firewalls gewähren nicht automatisch Zugriff auf Back-End-Ports für Load-Balancer-VIPs. |