概览
本文档提供了分步说明,介绍了如何在 Google Distributed Cloud (GDC) 的气隙环境中部署开放权重的大语言模型 (LLM),例如 Gemma、Llama 和 DeepSeek。它涵盖了如何使用 vLLM 实现高吞吐量服务,以及如何使用 Ollama 实现易用性,同时利用 GDC 平台的功能,包括 Kubernetes、Harbor 和 GPU 资源。
架构
该解决方案涉及在用户集群中将容器化的 LLM 服务后端 (vLLM、Ollama) 部署为 Deployment。模型权重存储在 Persistent Volume 上,这些 Persistent Volume 是从 Harbor 注册表中的映像填充的。LoadBalancer 类型的 Kubernetes 服务会公开后端 API。项目网络政策可确保对这些服务的访问安全无虞。

准备工作
确保满足以下前提条件:
- GDC 网闸隔离配置 1.15.1 或更高版本。
- 创建的用户集群具有充足的资源(CPU、内存、GPU)。
- 至少需要 1 个 NVIDIA A100 GPU。
- Harbor 实例可用且可访问。
- 已配置
kubectl和gdcloudCLI 以访问用户集群。 - Docker 客户端已安装并配置为推送到 Harbor。
- 已授予必要的 IAM 权限(例如,Namespace Admin、Cluster Developer)。
- 如果使用受限模型,则需要配置 Hugging Face 账号和身份验证。
第 1 部分:常见设置
1.1 创建映像拉取 Secret
如需为 GDC 网闸隔离配置中的容器工作负载配置映像拉取 Secret,您需要创建一个 Kubernetes docker-registry Secret,其中包含用于访问您的私有 Harbor 项目的凭据。然后,在部署规范中引用此 Secret。
您应使用 Harbor 机器人账号以编程方式访问私有 Harbor 项目中的映像。
请按以下步骤配置映像拉取 Secret:
创建 Harbor 机器人账号:
- 前往 Harbor 实例界面。
- 前往您的 Harbor 项目。
- 选择机器人账号标签页。
- 点击 New Robot Account(新建机器人账号)。
- 为其指定名称(例如,
oss-llm-puller),并授予其必要的权限(至少是 pull 访问权限),直到过期时间为止。 - 安全地存储机器人账号名称(例如
robot$oss-llm-puller)和提供的 Secret 令牌。
向 Harbor 验证 Docker 的身份:
在安装了 Docker 且可访问 Harbor 注册表的机器上,使用机器人账号凭据登录:
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:
使用 kubectl 在项目命名空间中创建类型为 docker-registry 的 Secret,使用在上一步中更新的 Docker 配置文件:
# 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}
第 2 部分:使用 vLLM 进行部署
2.1 获取 vLLM Docker 映像
在具有互联网访问权限的机器上,拉取 vLLM Docker 映像,然后将其转移到您的 Harbor 项目:
# 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
将 HARBOR_URL 和 PROJECT 替换为您的 Harbor 实例网址和项目名称。
2.2 在 PVC 中准备模型权重
创建 YAML 文件(例如 model-pvc.yaml):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: model-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Gi
storageClassName: standard-rwo
volumeMode: Filesystem
应用 PVC:kubectl apply -f model-pvc.yaml
从 Hugging Face 下载权重:
hf auth login
hf download google/gemma-3-4b-it
使用辅助 pod(例如 helper-pod.yaml)将权重转移到 PVC。
确保您在 Harbor 中有一个 busybox 映像。
# 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 并复制文件:
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 后端
创建部署文件 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
创建服务文件 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
应用配置:
kubectl apply -f vllm-gemma-3-4b-it-deployment.yaml
kubectl apply -f vllm-gemma-3-4b-it-service.yaml
2.4 配置网络政策
应用 ProjectNetworkPolicy 资源以允许入站流量到达 vLLM 服务端口 (8000)。创建 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
应用政策:kubectl apply -f vllm-netpol.yaml
第 3 部分:使用 Ollama 进行部署
3.1 准备 Dockerfile
创建 Dockerfile 以构建预加载了您的模型(一个或多个)的 Ollama 映像:
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 构建并推送映像
构建并推送映像:
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 后端
创建 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
应用清单:kubectl apply -f ollama-gemma3.yaml
3.4 配置网络政策
创建 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
应用政策:kubectl apply -f ollama-netpol.yaml
第 4 部分:验证
通过检查 pod 状态、服务 IP 地址并使用 curl 向 vLLM 和 Ollama 的 LoadBalancer IP 地址发送测试推理请求,来验证部署。
检查所有容器和服务是否都处于 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
测试 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
}'
测试 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
}'
第 5 部分:操作和问题排查
5.1 vLLM 操作
检查日志:kubectl logs -f -n NAMESPACE
查询内部状态(从调试 pod):
wget -qO- http://gemma-3-4b-it/v1/models
wget -qO- http://gemma-3-4b-it/health
5.2 Ollama 操作
访问 CLI:kubectl exec -it -n NAMESPACE -- sh
Pod 内:ollama list、ollama ps
5.3 扩缩
使用 Ollama 后端扩缩解决方案
垂直
- 为已成为瓶颈的 LLM 分配更大的 GPU 切片,直到使用完整的 GPU。
- 如果您想使用更大的 LLM 来提高回答准确性,例如使用 4,050 亿参数的 LLM 而不是 70 亿参数的 LLM,则可能需要多个 GPU 才能顺畅运行。
- 如果模型适合多个 GPU,则会因 GPU 间通信而产生一些延迟时间。
横向
- 根据需要在给定 LLM 上部署尽可能多的 Ollama pod,以实现目标吞吐量。
- 为此,请增加相应 Ollama 部署 YAML 文件中的 replica 数量。
- Kubernetes 服务(类型为 LoadBalancer)将在端点(即 pod)之间分配代码辅助请求,并通过公开的外部 IP 返回各自的响应。
- 请注意,Continue 插件的每个功能仅指向一个 IP 地址。

如需在 GDC 气隙环境中扩缩 vLLM 后端,您可以遵循与 Ollama 类似的策略,同时侧重于硬件资源分配和 pod 复制。
使用 vLLM 后端扩缩解决方案
垂直
- 升级 GPU 分配:如果推理吞吐量(令牌/秒)成为瓶颈,请分配更大的 GPU 切片,直到使用完整的 NVIDIA A100 GPU。
- 多 GPU 配置:对于无法放入单个 80GB A100 内存中的大型模型(例如,参数数量为 700 亿到 4050 亿),您必须使用张量并行处理扩展到多个 GPU。
- 延迟时间注意事项:请注意,跨多个 GPU 的模型可能会因 GPU 间通信(例如 NCCL 同步)而产生少量开销。
横向
- 通过副本提高吞吐量:为了处理针对同一模型的更多并发用户请求,请在 vLLM 部署 YAML 中增加副本数量。
- 专用模型实例:由于 vLLM 被设计为单模型服务引擎,并在初始化时固定其 KV 缓存内存,因此您必须为要托管的每个不同的 LLM 部署一组单独的 pod。
- 负载均衡:GDC Kubernetes 服务(类型为 LoadBalancer)会自动将传入的推理请求分配给与该服务关联的所有运行正常的 vLLM pod 端点。
5.4 问题排查
常见错误和缓解措施。
| 错误 | 缓解 |
|---|---|
| FIPS 自检失败 | 当 BoringSSL 等库缺少完整性签名时,会发生此错误。通过设置 BORINGSSL_FIPS=0 并利用官方 vLLM 映像来修复 |
| 权重加载停滞 | 检查小型 PVC 是否存在 IOPS 限制。性能模型初始化需要 500GiB 的卷。 |
| 连接遭拒 | 确认 PNP 明确允许 targetPort (8000/8080)。GDC 防火墙不会自动授予对负载均衡器 VIP 的后端端口的访问权限。 |