GDC 网闸隔离配置上的开放权重 LLM 用户指南

本部署指南详细介绍了如何在 Google Distributed Cloud (GDC) 气隙环境中,使用开源软件 (OSS) 运行先进的大语言模型 (LLM)。具体而言,本指南介绍了运行最新版 Gemma、DeepSeek 和 Llama 模型所需的所有步骤,同时还介绍了提供其他公开提供的大语言模型的常见程序。我们将利用容器化的服务后端(例如 vLLM 和 Ollama),它们涵盖广泛的功能、不同的配置级别,并且易于部署。

内容摘要

本指南详细介绍了如何在 Google Distributed Cloud (GDC) 隔离环境中部署和运行开源大语言模型 (LLM)。

核心目标和基础设施

  • 用途:详细说明如何使用 vLLM 和 Ollama 等优化后端运行前沿 LLM,包括 Gemma 3(多模态)、DeepSeek-R1 和 Llama (3.1/3.2)。
  • 最低版本:GDC 网闸隔离配置软件版本 1.15.x*。
  • 硬件要求:至少需要 1 个 NVIDIA A100 GPU。建议使用高性能存储空间(500GiB+ PVC),以将模型权重加载时间从 30 分钟缩短到 5 分钟以内。

部署组件

组件 说明
LLM 后端 vLLM(使用 PagedAttention 实现高吞吐量服务)和 Ollama(简化本地管理并支持多模态)
模型 公开可用的 LLM,包括 Gemma、DeepSeek 和 Llama 系列
加速器 支持完整的 NVIDIA A100 GPU 或 多实例 GPU (MIG) 切片,具体取决于模型的 VRAM 占用空间
GDC 服务 使用 GDC 容器服务、负载均衡器 (ELB/ILB)、Harbor Container Registry

关键操作程序

  • 模型管理:LLM 后端会根据使用情况管理模型加载和卸载。vLLM 会将模型固定到内存中,以实现一致的低延迟,而 Ollama 允许动态加载模型,但会在闲置 30 分钟后自动卸载模型。
  • 自定义:用户可以从互联网拉取首选 LLM,构建新的 Docker 映像并上传到本地 Harbor 注册表。

前提条件

版本

  • GDC 网闸隔离配置 1.15.1 或更高版本。

组件

  • 创建的用户集群具有充足的资源(CPU、内存、GPU)。
  • 至少需要 1 个 NVIDIA A100 GPU。
  • Harbor 实例可用且可访问。
  • 已配置 kubectl 和 gdcloud CLI 以访问用户集群。
  • Docker 客户端已安装并配置为推送到 Harbor。
  • 已授予必要的 IAM 权限(例如命名空间管理员、集群开发者)。
  • 如果使用受限模型,则需要配置 Hugging Face 账号和身份验证。

所需容量

资源 容量
CPU 8 个 vCPU
RAM 内存 32 GiB
临时存储 16 Gi
GPU 1 个 NVIDIA A100 GPU
永久性卷 *500GiB+

服务后端需要这些硬件资源才能通过 GPU 加速运行 LLM。例如,一个 a2-ultragpu-1g-gdc 机器类型就可提供足够的资源。

概要图表

高级别开放权重 LLM 服务图。

此图描绘了一位用户,他正在使用 vLLM 和 Ollama 这两个 LLM 后端公开的 API,以从 Gemma、Llama 和 DeepSeek 等开源模型获取推理响应。根据模型大小(以数十亿参数衡量),每个 LLM 可能需要一个完整的 GPU 或 GPU 的一部分才能运行。

LLM 部署的服务和安全边界示意图

此解决方案依赖于 GDC 网闸隔离配置软件堆栈中已包含的以下服务:

  • GDC 容器服务:用于运行所有进程并使用 HPA 扩缩这些进程。
  • GDC 负载均衡器:使用 Kubernetes 服务来为该解决方案中的所有后端提供服务,并使客户网络可以访问 LLM,这称为 ELB,代表外部负载均衡器。相反,客户可以选择仅从用户集群内部访问 LLM,这在只有内部代理在其解决方案内部请求推理时可能很有用,这称为 ILB,代表内部负载均衡器。
  • GDC CI/CD:Harbor 容器注册表,用于存储 LLM 后端和模型权重。
  • GDC 可观测性堆栈:用于收集和搜索后端日志并提供信息中心。

在安全性方面,对 LLM 部署的访问权限取决于哪些用户可以访问公开 LLM 服务后端(在 Kubernetes 项目 / 命名空间中以 pod 形式运行)的负载均衡器服务。

服务和安全边界架构图。

部署

前提条件

在连接到 GDC 网闸隔离配置环境的工作站上安装以下工具:

  1. docker
  2. kubectl
  3. gdcloud CLI

此文档包含部署 LLM 所需的所有源代码和配置文件。

创建用于从 Harbor 拉取映像的 Secret

如需为 GDC 网闸隔离配置中的容器工作负载配置映像拉取 Secret,您需要创建一个 Kubernetes docker-registry Secret,其中包含用于访问私有 Harbor 项目的凭据。然后,在部署规范中引用此 Secret。

您应使用 Harbor 机器人账号以编程方式访问私有 Harbor 项目中的映像。

请按以下步骤配置映像拉取 Secret:

创建 Harbor 机器人账号:

  • 前往 Harbor 实例界面。
  • 前往您的 Harbor 项目。
  • 选择“机器人账号”标签页。
  • 点击 + 新建机器人账号。
  • 为其指定名称(例如 oss-llm-puller),并授予其必要的权限(至少是“拉取”访问权限),直到过期时间为止。
  • 安全地存储机器人账号名称(例如 robot$oss-llm-puller)和提供的 Secret 令牌。

向 Harbor 验证 Docker 的身份:

在安装了 Docker 且可访问 Harbor 注册表的机器上,使用机器人账号凭据登录:

export INSTANCE_URL="your-harbor-instance-url"
# for example, harbor1-project1.org1.zone1.google.gdc.com

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

export ROBOT_SECRET="your-robot-account-secret"

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

# Sample output:

WARNING! Using --password via the CLI is insecure. Use --password-stdin.

WARNING! Your credentials are stored unencrypted in '$HOME/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded

如示例输出所示,此命令会使用身份验证详细信息更新本地 Docker 配置文件 ($HOME/.docker/config.json)。config.json 文件如下所示:

{
    "auths": {
        "harbor1-project1.org1.zone1.google.gdc.com": {
            "auth": "...omitted..."
        }
    }
}

如果您之前使用 Docker 客户端登录过其他注册中心,您会在“auths”中看到更多条目。

创建 Kubernetes 映像拉取 Secret:

使用 kubectl 在项目命名空间中创建类型为 docker-registry 的 Secret,使用在上一步中更新的 Docker 配置文件:

# Log in to GDC air-gapped using the next commands

gdcloud auth login --login-config-cert {path to your web TLS certificate}

gdcloud clusters get-credentials {Your User Cluster}
kubectl config set-context --current --namespace=NAMESPACE

export SECRET_NAME="oss-llm-pull-secret" # You can choose another name
export NAMESPACE="your-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}

您稍后将在 LLM 后端容器规范中通过添加 imagePullSecrets 字段来引用此 Secret。

按照这些步骤操作后,Kubernetes 集群现在将使用提供的凭据从 GDC 气隙环境中的私有 Harbor 注册表中拉取容器映像。产品文档中也包含这些步骤。

设置 LLM 部署

您可以选择以下 2 种 LLM 后端:

  • vLLM
  • Ollama

您可以选择其中一个后端,也可以同时部署这两个后端。每个后端都可以运行一个或多个 LLM,例如:

  • Gemma 3
  • DeepSeek
  • Llama
  • 其他 LLM

第 1 部分提供了有关如何部署 vLLM 的说明,第 2 部分介绍了如何安装 Ollama。

1. 将 vLLM 部署为 LLM 后端

获取 vLLM Docker 映像

一般来说,vLLM 用于提供从 HuggingFace 下载的模型,正如我们将在本 LLM 部署中展示的那样。部分模型有访问限制,这意味着用户必须明确请求或同意访问 Hugging Face Hub 上的文件和内容,然后才能下载或使用这些文件和内容。

按照文档中的说明,使用 Docker 登录您的 Harbor 实例。在具有互联网访问权限的机器上,拉取 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:Harbor 实例网址。
  • PROJECT:Harbor 项目名称。

本部署指南使用的 vLLM 版本为:v0.13.0

将 LLM 模型权重存储在 PVC 中

打开新终端,并创建一个用于保存 vLLM 部署文件的文件夹:

mkdir vllm-deployment
cd vllm-deployment

然后,创建一个 YAML 文件(例如 model-pvc.yaml),以使用标准 Kubernetes 原语声明 PersistentVolumeClaim (PVC):

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

连接到 GDC 网闸隔离配置集群并应用 PVC:

# Log in to GDC air-gapped using the next commands

gdcloud auth login --login-config-cert {path to your web TLS cert}

gdcloud clusters get-credentials {Your User Cluster}
kubectl config set-context --current --namespace=NAMESPACE

# Apply the PVC manifest
kubectl apply -f model-pvc.yaml

从 Hugging Face 下载模型权重:

# Log in to Hugging Face
hf auth login

# Download weights for Gemma 3 (for example, 4B instruction-tuned)
hf download google/gemma-3-4b-it

示例输出:

Fetching 9 files: 100%| 9/9 [00:00<00:00, 19.34it/s]
Downloading (…)7f58a/.gitattributes: 100%| 1.62k/1.62k [00:00<00:00, 7.82MB/s]
Downloading (…)del-00002-of-00002.safetensors: 100%| 3.75G/3.75G [00:46<00:00, 81.3MB/s]
Downloading (…)del-00001-of-00002.safetensors: 100%| 4.90G/4.90G [00:54<00:00, 90.1MB/s]
Downloading (…)-3-4b-it/README.md: 100%| 23.3k/23.3k [00:00<00:00, 48.0MB/s]
Downloading (…)8a/model.safetensors.index.json: 100%| 26.3k/26.3k [00:00<00:00, 39.5MB/s]
Downloading (…)f58a/config.json: 100%| 908/908 [00:00<00:00, 4.41MB/s]
Downloading (…)generation_config.json: 100%| 210/210 [00:00<00:00, 936kB/s]
Downloading (…)tokenizer.json: 100%| 33.1M/33.1M [00:01<00:00, 17.6MB/s]
/Users/rashjab/.cache/huggingface/hub/models--google--gemma-3-4b-it/snapshots/952ec8ffca3adadfd00c6d71b40280ebdbb7f58a

如示例输出中所示,磁盘上的模型占用的存储空间为 8.64 GB,这符合我们之前定义的 500 GiB PVC。请务必确保您选择加载到永久卷 (PV) 中的任何模型都是这种情况,例如,对于 15GB 的模型,使用 500 GiB PVC 可实现 1,500 IOPS,从而将权重加载时间从大约 30 分钟大幅缩短到 5 分钟以内。

通过将模型文件复制到 PV 的卷中来填充 PV。这通常包括:

  • 创建用于挂载 PV 的临时“辅助”Pod。
  • 使用 kubectl cp 将模型文件从工作站复制到辅助 pod 的已挂载卷中。
  • 辅助 pod 示例:

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

替换以下内容:

  • HARBOR_URL:Harbor 实例网址。
  • PROJECT:Harbor 项目名称。
  • oss-llm-pull-secret:如果您选择了其他映像拉取 Secret 名称。
  • model-pvc:替换为您为 PVC 指定的名称。

推送辅助 Docker 映像 (busybox):

docker pull busybox:latest
docker tag busybox HARBOR_URL/PROJECT/busybox:latest
docker push HARBOR_URL/PROJECT/busybox:latest

替换以下内容:

  • HARBOR_URL:Harbor 实例网址。
  • PROJECT:Harbor 项目名称。

应用 helper-pod.yaml 文件并将模型权重复制到 PV:

kubectl apply -f helper-pod.yaml

# Wait for the pod to be Running
kubectl cp ~/.cache/huggingface/hub/ NAMESPACE/model-uploader:/data/

# After copying, delete the helper pod
kubectl delete pod model-uploader

部署 vLLM 后端

创建部署文件,以便 vLLM 运行模型服务器。以下示例部署了 gemma-3-4b-it 模型。

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 # change with the name you gave initially to your PVC
      # vLLM needs to access the host's shared memory for tensor parallel inference.
      - 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", # Use the repo ID
          "--max-model-len",
          "32768",
          "--enforce-eager"
        ]
        env:
        - name: HF_HUB_OFFLINE
          value: "1"
        - name: HF_HOME
          value: "/model" # Tells vLLM to look for models in /model/hub
        # --- PERFORMANCE & STABILITY OPTIMIZATIONS ---
        - name: NCCL_P2P_DISABLE
          value: "1" # Prevents initialization hangs on P2P checks
        - name: NCCL_IB_DISABLE
          value: "1" # Prevents initialization hangs on InfiniBand checks
        # --- FIPS BYPASS ---
        - 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"       # Increased to handle model weight verification
            memory: "64Gi"  # Increased to ensure headroom for weight loading
          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

替换以下内容:

  • HARBOR_URL:Harbor 实例网址。
  • PROJECT:Harbor 项目名称。
  • oss-llm-pull-secret:您在创建映像拉取 Secret 时提供的名称。
  • claimName:您为初始 PVC 创建操作指定的名称。

接下来,创建一个 Kubernetes 服务文件来公开 vLLM 后端 API:

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

验证并测试 vLLM 部署

  1. 检查 pod 状态:

    kubectl get pods
    

    等待 pod 处于 Running 状态。

  2. 检查服务:

    kubectl get service
    

    示例输出:

    # NAME          TYPE         CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
    # gemma-3-4b-it LoadBalancer 10.201.136.62  136.125.37.198   80:30109/TCP   3d1h
    

    记下上一个输出中 vLLM 服务的 EXTERNAL-IP(例如 136.125.37.198)。您还可以使用以下方式打印:

    kubectl get service gemma-3-4b-it \
    -o jsonpath='{.status.loadBalancer.ingress[*].ip}'
    
  3. 查看日志:

    kubectl logs YOUR_VLLM_POD
    

    查找指示服务器已启动且模型已加载的消息(例如 INFO: Application startup complete.)。由于没有 Hugging Face 下载,如果文件位于具有高持久卷容量的 PVC 上,则此过程应该很快。

  4. 使用 curl 测试推理:

    验证 vLLM pod 正在运行并获取服务的外部 IP 地址后,您可以使用 curl 发送推理请求。由于 vLLM 提供与 OpenAI 兼容的 API,因此您可以使用 /v1/chat/completions 端点:

    curl http://EXTERNAL_IP/v1/chat/completions \
      -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
      }'
    

    将 EXTERNAL_IP 替换为 vLLM 服务的外部 IP 地址。

2. 将 Ollama 部署为 LLM 后端

Ollama 是一种热门框架,用于在本地和容器化环境中运行开源 LLM。它通过处理权重下载、量化和 GPU 加速来简化模型执行。它包含一个内置 REST API,用于生成补全内容、对话回答和嵌入。

与其他服务引擎不同,Ollama 可动态管理模型执行:

  • 动态模型加载:当收到对特定模型的请求时,Ollama 会按需将权重加载到内存(GPU VRAM 或系统 RAM)中。
  • 非活动状态卸载:如果模型在 30 分钟内(可通过 OLLAMA_KEEP_ALIVE 进行配置)未收到任何推理请求,Ollama 会将其从内存中卸载,以释放硬件资源供其他工作负载使用。
  • 每个实例只能有一个有效模型:Ollama 会在每个服务器实例中一次处理一个有效模型的请求。如果并行调用多个模型,Ollama 会将请求排队并按顺序交换模型,这可能会导致延迟。如需同时提供多个模型,而不进行交换,您应为每个模型部署单独的 Ollama 实例。

下载模型权重并创建 Docker 映像

与可以直接从永久性卷加载权重的 vLLM 不同,Ollama 通常要求模型以其内部目录格式 (~/.ollama/models) 存储。

在无法从公共互联网下载模型的隔离环境中,您必须在构建阶段将模型权重预先打包到容器映像本身中。这种方法可确保容器完全自包含,并且在部署后即可立即提供服务,而无需外部网络访问权限。

为 Ollama 部署创建一个文件夹:

mkdir ollama-gemma3
cd ollama-gemma3

创建一个包含以下内容的 Dockerfile:

# Use a standard Linux base image
FROM ubuntu

# Install necessary dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    ca-certificates \
    zstd \
    && rm -rf /var/lib/apt/lists/*

# Install Ollama
# This uses Ollama's official installation script, which adds Ollama to /usr/local/bin
RUN curl -fsSL https://ollama.com/install.sh -o install.sh && \
    chmod +x install.sh && \
    ./install.sh && \
    rm install.sh

# Set environment variables for Ollama (optional, but a good practice)
# ENV OLLAMA_HOST="0.0.0.0"

# If you want to customize the model storage path within the container, set OLLAMA_MODELS, for example:
# ENV OLLAMA_MODELS="/usr/local/ollama/models"
# The default OLLAMA_MODELS is /root/.ollama
# Then, ensure you create and populate that directory.

# --- Download Gemma 3 model weights ---
# This step starts Ollama server in the background, pulls the model,
# and then kills the server to allow the Docker build to continue.
# This approach works around the Docker RUN command limitations for services.

RUN ollama serve & \
    # Give the Ollama server a moment to start up
    # Use --retry and --retry-connrefused to handle startup delays
    curl --retry 10 --retry-connrefused -s http://localhost:11434 || true && \
    # Pull the Gemma 3 model weights
    ollama pull gemma3:latest && \
    # Stop the background Ollama server process cleanly
    pkill ollama || true

# Expose Ollama's default port
EXPOSE 11434

# Command to run Ollama server when the container starts
CMD ["ollama", "serve"]

如果您想尝试其他开源 LLM,例如 Llama 3.2 或 DeepSeek-R1 系列的模型,请修改上一个 Dockerfile 中的此 Ollama 命令:

ollama pull gemma3:latest

例如,如果您想拉取具有 30 亿参数的 Llama 3.2,请使用以下命令:

ollama pull llama3.2:3b

或者,如果您想使用 80 亿参数的 DeepSeek-R1,请使用以下命令:

ollama pull deepseek-r1:8b

您还可以下载多个 LLM,并将其模型权重容器化。这样,当您请求通过不同的预加载模型进行推理时,Ollama 便能够在模型之间切换。例如,您可以将 Gemma 和 Llama 模型都添加到 Docker 映像中。请注意,在无网络连接的环境中,Ollama 后端无法访问 Ollama 库,因此无法随时下载任何模型。

请务必先查看并同意每项模型许可和使用条款,然后再在应用中运行这些模型。Ollama 的库中有许多 LLM。在构建 LLM 后端 Docker 映像时,您只需连接到互联网即可下载模型权重。

构建 Docker 映像并将其推送到 Harbor

按照文档中的说明,使用 Docker 登录您的 Harbor 实例。构建 Gemma 3 Docker 映像并将其上传到您的 Harbor 代码库:

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

替换以下内容:

  • HARBOR_URL:Harbor 实例网址。
  • PROJECT:Harbor 项目名称。

本部署指南所用的 Ollama 版本:0.14.1

准备 Kubernetes 部署和服务

创建一个名为 ollama-gemma3.yaml 的文件,以定义 Ollama 后端部署及其负载均衡器配置,从而将其公开为服务:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ollama-gemma3
  namespace: osd-dev
  labels:
    app: ollama-gemma3
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ollama-gemma3
  template:
    metadata:
      labels:
        app: ollama-gemma3
    spec:
      containers:
      - name: ollama-gemma3
        image: rashjab-mhs-rashjab-test2.gdc1.us-west6-a.staging.gpcdemolabs.com/rashjab-repo/ollama-gemma3:latest
        env:
        # This is the crucial fix: Tell Ollama to listen on all network interfaces
        - 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: osd-dev
spec:
  type: LoadBalancer
  selector:
    app: ollama-gemma3
  ports:
  - name: ollama-gemma3-port
    port: 11434
    protocol: TCP
    targetPort: 11434

替换以下内容:

  • HARBOR_URL:Harbor 实例网址。
  • PROJECT:Harbor 项目名称。
  • oss-llm-pull-secret:如果您选择了其他映像拉取 Secret 名称。

验证 GPU 分配

GDC 网闸隔离配置支持 NVIDIA 多实例 GPU (MIG),您可以在公开文档中查看其不同的 MIG 配置文件。如果您要部署小型 LLM,建议检查其大小(以内存为单位),并相应地对 GPU 进行分区。节点池的分区方案在其 Cluster 自定义资源中定义。如需详细了解如何应用 GPU 分区方案,请参阅添加节点池。如果您是应用操作员 (AO),请咨询您的平台管理员 (PA),了解您的项目 / 集群中已安装和可用的加速器。您可以详细了解如何配置容器以使用 GPU 资源以及如何检查 GPU 资源分配。

Gemma 3 4B 模型的默认精度为 16 位。根据 Google 发布的此表格,此 LLM 的 GPU 内存要求为 6.4 GB,使用 BF16(16 位)精度。

在之前的 ollama-gemma3.yaml 中,我们将 10 GB GPU 切片 (NVIDIA A100) 附加到容器,因为此 VRAM 内存量足以加载 Gemma 3 4B 模型并高效运转它,如下所示:

ollama-gemma3.yaml

... omitted ...

        resources:
          limits:
            nvidia.com/mig-1g.10gb-NVIDIA_A100_80GB_PCIE: 1
          requests:
            nvidia.com/mig-1g.10gb-NVIDIA_A100_80GB_PCIE: 1

... omitted ...

部署 Ollama 后端并将其作为服务公开

连接到集群并将上下文设置为项目命名空间:

# Log in to GDC air-gapped using the next commands

gdcloud auth login --login-config-cert {path to your web TLS certificate}

gdcloud clusters get-credentials {Your User Cluster}
kubectl config set-context --current --namespace=NAMESPACE

应用 Ollama 清单:

kubectl apply -f ollama-gemma3.yaml

确保 Ollama pod 正在运行,并且关联的服务已就位:

kubectl get pods

示例输出:

# NAME                             READY   STATUS    RESTARTS   AGE
# ollama-gemma3-6fc6fff74b-9qtpf   1/1     Running   0          1h
kubectl get service

示例输出:

# NAME             TYPE          CLUSTER-IP    EXTERNAL-IP    PORT(S)          AGE
# ollama-gemma3   LoadBalancer  172.0.0.1     10.0.0.1       11434:31822/TCP  1d

记下上一个输出中 Ollama 服务的 EXTERNAL-IP(例如 10.0.0.1)。您还可以使用以下方式打印:

kubectl get service ollama-gemma3 \
-o jsonpath='{.status.loadBalancer.ingress[*].ip}'

在检查 Ollama Gemma 3 是否正常运行之前,您需要应用网络政策,例如:

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

应用网络政策:

kubectl apply -f ollama-netpol.yaml

验证并测试 Ollama 部署:

  1. 检查 Ollama 是否正在运行:

    curl http://EXTERNAL_IP:11434
    

    预期响应:“Ollama is running”

  2. 发送补全请求:

    curl -X POST http://EXTERNAL_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
      }'
    

请求推理

现在,您的 LLM 后端(vLLM 和 Ollama)已部署,并且可以通过各自的 LoadBalancer 服务访问,您可以开始使用它们进行推理了。

根据您的使用场景,您有以下几种选择:

  • 交互式 Web 界面:如需进行测试、原型设计或为用户提供类似 ChatGPT 的体验,您可以部署 Open WebUI 或类似的前端。
  • 命令行界面 (CLI):如需快速验证、编写脚本或实现自动化,您可以使用 curl 或自定义脚本。
  • 应用集成:将您的内部应用、AI 代理或 IDE 插件(例如,适用于 VS Code/JetBrains 的 Continue)直接连接到 vLLM (/v1) 或 Ollama (/v1) 提供的与 OpenAI 兼容的端点。

1. 使用界面与 LLM 互动

如需提供类似 ChatGPT 的界面来与模型互动,请在集群中部署 Open WebUI。

创建 Open WebUI 部署

创建 open-webui-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: open-webui
  namespace: osd-dev
  labels:
    app: open-webui
spec:
  replicas: 1
  selector:
    matchLabels:
      app: open-webui
  template:
    metadata:
      labels:
        app: open-webui
    spec:
      containers:
      - name: open-webui
        image: HARBOR_URL/PROJECT/open-webui:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
        env:
        - name: OPENAI_API_BASE_URL
          value: "http://gemma-3-4b-it/v1" # Point to your vLLM service
        - name: OPENAI_API_KEY
          value: "EMPTY"
        - name: WEBUI_AUTH
          value: "False"
      imagePullSecrets:
      - name: oss-llm-pull-secret
---
apiVersion: v1
kind: Service
metadata:
  name: open-webui
  namespace: osd-dev
spec:
  type: LoadBalancer
  selector:
    app: open-webui
  ports:
  - port: 80
    targetPort: 8080

应用网络政策

您必须允许您的机器通过端口 80 访问 Open WebUI 服务。

更新您的 PNP 项目网络政策,以包含 open-webui 服务或应用广泛的政策:

apiVersion: networking.gdc.goog/v1
kind: ProjectNetworkPolicy
metadata:
  name: allow-webui-ingress
  namespace: NAMESPACE
spec:
  subject:
    subjectType: UserWorkload
  policyType: Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 0.0.0.0/0 # Allows traffic from any IP then restrict this for production
    ports:
    - protocol: TCP
      port: 8080

验证

  1. 获取 Web 界面 IP:

    kubectl get service open-webui -n NAMESPACE
    
  2. 访问界面:打开浏览器,然后前往 http://WEB_UI_EXTERNAL_IP

  3. 配置:首次登录时,创建管理员账号。在设置中,确保 OpenAI 连接指向 http://gemma-3-4b-it/v1

  4. 选择模型:在对话界面中,从模型下拉菜单中选择 google/gemma-3-4b-it。

    打开 WebUI 模型选择下拉菜单。

2. 使用 CLI 发出 HTTP 请求

  • curl:
curl -X POST http://OLLAMA_LOAD_BALANCER_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
}'

验证与确认

检查所有容器和服务是否都在运行:

# Log in to GDC air-gapped using the next commands

gdcloud auth login --login-config-cert {path to your web TLS cert}
gdcloud clusters get-credentials {Your User Cluster}
kubectl config set-context --current --namespace=NAMESPACE

# Pods

kubectl get pods # This command should return results like below

# NAME                                   READY   STATUS    RESTARTS   AGE
# vllm-6fc6fff74b-9qtpf                 1/1     Running   0          1h
# ollama-798fbf6ff6-9m7ln               1/1     Running   0          1h


# Services

kubectl get services # This command should return results like below

# NAME                      TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)           AGE
# vllm-service              LoadBalancer   172.0.0.1        10.0.0.1       11434:31822/TCP   1h
# ollama-service        LoadBalancer   172.0.0.2        10.0.0.2       11434:30728/TCP   1h

移除步骤

  1. 移除所有 Kubernetes 资源:

    kubectl delete -f vllm-gemma-3-4b-it-deployment.yaml
    kubectl delete -f vllm-gemma-3-4b-it-service.yaml
    
  2. 从 Harbor 代码库中手动删除未使用的容器。

操作

vLLM 后端

vLLM 基础知识

vLLM 旨在成为高性能的单模型服务引擎。它不支持在单个服务器进程中提供多个模型,也不支持在单个启动命令中定义多个模型。如需提供多个模型(例如聊天模型和单独的嵌入模型),您必须启动单独的 vLLM 实例,每个实例位于自己的 pod 或容器中,可以选择分配给不同的 GPU 或 GPU 切片。与其他可能会动态交换模型的后端不同,vLLM 实例会在初始化时固定其模型和关联的 KV 缓存内存,以确保始终实现高吞吐量和低延迟。

您可以使用以下命令在任何 vLLM pod 上打开终端:

kubectl exec -it gemma-3-4b-random-string -n NAMESPACE -- /bin/bash

这会显示哪些模型已加载、它们的实参和 PID:

ps aux | grep vllm

预期类似输出:

root           1  0.1  0.8 9436960 1550504 ?     Ssl  Feb04   9:18 python3 -m vllm.entrypoints.openai.api_server --model google/gemma-3-4b-it --max-model-len 32768 --enforce-eager
root         504  0.0  0.0   3476  1596 pts/0    S+   02:48   0:00 grep --color=auto vllm

如果您想要更简洁、更“Ollama 式”的视图,而不想看到 grep 命令本身和系统 PID 的杂乱信息,可以使用以下单行命令:

ps aux | grep [v]llm.entrypoints | awk -F'--model ' '{print $2}' | awk '{print $1}'

示例回答:

google/gemma-3-4b-it

您可以使用标准 kubectl 命令并通过查询内部 API 端点来监控 vLLM 部署的状态。

检查 Pod 日志

打开终端以查看初始化日志和服务日志:

kubectl logs -f gemma-3-4b-it-random_string -n NAMESPACE

查找服务器已准备就绪的确认信息:

(APIServer pid=1) INFO:     Started server process [1]
(APIServer pid=1) INFO:     Waiting for application startup.
(APIServer pid=1) INFO:     Application startup complete.

查询内部状态

由于 vLLM 提供与 OpenAI 兼容的 API,因此您可以直接从集群内的终端(例如使用 debug-sh pod)验证已加载的模型和系统健康状况。

您需要先启动调试 pod,以下是相关说明:

启动调试 Pod

运行以下命令,在项目命名空间中启动临时 shell。此配置使用之前推送到 Harbor 注册表的 busybox 映像,并通过 --overrides 标志注入必要的凭据:

kubectl run --rm -it debug-sh \
  --image=HARBOR_URL/PROJECT/busybox:latest \
  --restart=Never -n osd-dev --overrides='{
    "spec": {
      "imagePullSecrets": [{"name": "oss-llm-pull-secret"}]
    }
  }' -- sh

主要参数:

  • --rm:在退出时自动删除 pod,从而保留集群资源。
  • --overrides:在气隙环境中是必需的,用于允许 pod 向私有 Harbor 注册表进行身份验证。

查询模型服务器状态

进入 pod 后,使用 wget 实用程序查询 vLLM API。由于 vLLM 服务将内部端口 8000 映射到端口 80,因此您可以使用该服务的 DNS 名称:

  • 列出已加载的模型:验证 vLLM 引擎是否已成功初始化并加载 Gemma 3 权重。

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

    成功响应:

    {"object":"list","data":[{"id":"google/gemma-3-4b-it"}]}
    
  • 检查应用健康状况:确认 API 服务器已准备好处理请求。

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

    成功响应:

    OK
    

Ollama 基础知识

Ollama 不支持同时将多个模型加载到内存中。此外,如果您在一段时间内(默认情况下通常为 30 分钟)未使用 LLM,Ollama 会从内存中卸载当前模型。

由于上述行为,该解决方案会根据您的需求和使用情形实现两个或更多 Ollama 后端,以便许多开发者或用户可以并行使用它们,而不会出现只有一个 Ollama 实例频繁加载和卸载模型,从而产生不必要的延迟。

您可以使用以下命令在任何 Ollama pod 上打开终端:

kubectl exec -it ollama-chat-random_string -- sh
kubectl exec -it ollama-autocomplete-random_string -- sh

在 pod 内,您可以使用 Ollama CLI 探索可用的模型,并查看已加载的模型:

  • 在 ollama-chat pod 中,您会找到一个模型:

    # ollama list
    NAME                     ID              SIZE      MODIFIED
    codegemma:7b-instruct    0c96700aaada    5.0 GB    2 hours ago
    
    # ollama ps
    NAME                     ID              SIZE     PROCESSOR    UNTIL
    codegemma:7b-instruct    0c96700aaada    10 GB    100% GPU     4 minutes from now
    
  • 在 ollama-autocomplete pod 中,您会找到两个模型:

    # ollama list
    NAME                     ID              SIZE      MODIFIED
    codegemma:2b             926331004170    1.6 GB    2 hours ago
    codegemma:7b-code        aee9a63c13b9    5.0 GB    2 hours ago
    
    # ollama ps
    NAME                 ID              SIZE      PROCESSOR    UNTIL
    codegemma:7b-code    aee9a63c13b9    7.1 GB    100% GPU     4 minutes from now
    
  • 当 Ollama 卸载其当前模型时,输出为空:

    # ollama ps
    NAME    ID    SIZE    PROCESSOR    UNTIL
    

请注意,当您发送下一个代码辅助请求时,Ollama 会动态加载模型,无需任何手动干预。

GPU 容量的重要性

如果您预配了 10GB 的 GPU 切片,但模型内存占用空间(略)大于该值,您会发现部分 LLM 加载到了 CPU 上,如下所示:

# ollama ps
NAME                     ID              SIZE     PROCESSOR         UNTIL
codegemma:7b-instruct    0c96700aaada    10 GB    6%/94% CPU/GPU    29 minutes from now

这并非最佳做法,会导致响应延迟时间更长,因此请务必提供本指南中建议的 GPU 大小。

LLM

通过 vLLM 引入其他 LLM 或您偏好的 LLM

如需在 GDC 网闸隔离配置环境中通过 vLLM 运行不同大小的 Gemma 或任何其他开源大语言模型 (LLM),例如 DeepSeek-R1 或 Llama 3.1,您必须将新模型权重注入到您的环境中,并更新部署清单以指向新资源。

根据为此解决方案建立的操作程序,按照以下步骤加载其他模型:

  1. 下载新的模型权重:

    在可访问互联网的工作站上,使用 Hugging Face CLI 下载所需模型的权重。

    • 对于 Gemma 3 (3-12b-it):
    hf download google/gemma-3-12b-it
    
    • 对于 DeepSeek-R1(7B Distill):
    hf download deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
    
    • 对于 Llama 3.1 (8B):
    hf download meta-llama/Meta-Llama-3.1-8B-Instruct
    

    注意:请确保您已在 Hugging Face 上接受模型的许可条款,然后再下载。

  2. 填充永久性卷 (PVC):

    更新现有 model-pvc 或创建一个容量足以支持新模型的新 。使用辅助 Pod 方法从工作站转移权重。 较大的设备规格需要更多的磁盘空间。确保您的 Persistent Volume Claim (PVC) 有足够的容量来存储新的权重。

    • Gemma 3 4B:约 15.2 GB。
    • Gemma 3 27B:约 54 GB 以上。
    • 建议:无论模型大小如何,都应保持 500GiB PVC 建议,以确保高 IOPS (1,500),从而加快加载速度。
    # Copy the new model's hub directory to the helper pod
    kubectl cp ~/.cache/huggingface/hub/ osd-dev/model-uploader:/data/
    
  3. 更新 vLLM 部署清单:

    修改 vLLM 部署 YAML 以引用新的模型 ID。与可以动态交换模型的 Ollama 不同,vLLM 实例专用于单个模型,必须更新或重新部署才能更改模型。

    根据您使用的模型(此处以 deepseek 为例)更新部署的 args 部分:

    args: [
      "-m", "vllm.entrypoints.openai.api_server",
      "--model", "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B", # Update to new Repo ID
      "--max-model-len", "32768",
      "--enforce-eager" # Recommended for GDC stability
    ]
    
  4. 调整 GPU 和内存资源:

    较大的模型需要更多 VRAM 来存储权重和 KV 缓存。如果您从 4B 模型改用 8B 或 14B 模型,请确保资源限制足够,以免发生“内存不足”崩溃。

    • VRAM 占用空间:在 16 位精度下,一个 80 亿参数的模型仅权重就需要大约 16 GB 的 VRAM,再加上 vLLM PagedAttention 缓存所需的额外内存。
    • 建议:对于参数超过 70 亿的模型,请使用完整的 A100 GPU (nvidia.com/gpu-pod-NVIDIA_A100_80GB_PCIE: 1),以确保最佳性能并为 KV 缓存留出充足的内存空间。
  5. 验证新模型:

    部署完成协调后,请使用内部 API 验证新模型是否处于有效状态:

    # Inside a debug-sh pod
    wget -qO- http://gemma-3-4b-it/v1/models
    

    成功响应:

    {"object":"list","data":[{"id":"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"}]}
    

使用 Ollama 引入其他 LLM

如果您想尝试其他开源 LLM,例如 Llama 3.2 或 DeepSeek-R1 系列的模型,请修改上一个 Dockerfile 中的以下 Ollama 命令:

ollama pull gemma3:latest

例如,如果您想拉取具有 30 亿参数的 Llama 3.2,请使用以下命令:

ollama pull llama3.2:3b

或者,如果您想使用 80 亿参数的 DeepSeek-R1,请使用以下命令:

ollama pull deepseek-r1:8b

您还可以下载多个 LLM,并将其模型权重容器化。这样,当您请求通过不同的预加载模型进行推理时,Ollama 便能够在模型之间切换。例如,您可以将 Gemma 和 Llama 模型都添加到 Docker 映像中。请注意,在无网络连接的环境中,Ollama 后端无法访问 Ollama 库,因此无法随时下载任何模型。

请务必先查看并同意每项模型许可和使用条款,然后再在应用中运行这些模型。Ollama 的库中有许多 LLM。在构建 LLM 后端 Docker 映像时,您只需连接到互联网即可下载模型权重。

Open WebUI

检查 Open WebUI pod 是否处于运行状态:

kubectl get pods -n NAMESPACE

预期输出:

NAME                             READY   STATUS    RESTARTS   AGE
open-webui-69df4664d-przjl       1/1     Running   0          6d3h

检查服务 IP:

kubectl get service -n NAMESPACE

预期输出:

NAME          TYPE            CLUSTER-IP       EXTERNAL-IP      PORT(S)           AGE
open-webui     LoadBalancer   10.201.137.164   136.125.37.224   80:30492/TCP      6d3h

以下部分介绍了如何使用主要界面功能。

打开 WebUI 界面示意图。

使用 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 地址。

开放权重 LLM 伸缩架构图。

如需在 GDC 气隙环境中扩缩 vLLM 后端,您可以遵循与 Ollama 类似的策略,同时侧重于硬件资源分配和 pod 复制。

使用 vLLM 后端扩缩解决方案

垂直

  • 升级 GPU 分配:如果推理吞吐量(令牌/秒)成为瓶颈,请分配更大的 GPU 切片,直到使用完整的 NVIDIA A100 GPU。
  • 多 GPU 配置:对于无法放入单个 80GB A100 内存中的大型模型(例如,参数数量为 700 亿到 4050 亿),您必须使用张量并行处理扩展到多个 GPU。
  • 延迟时间注意事项:请注意,跨多个 GPU 的模型可能会因 GPU 间通信(例如 NCCL 同步)而产生轻微的开销。

横向

  • 通过副本提高吞吐量:如需处理针对同一模型的更多并发用户请求,请增加 vLLM 部署 YAML 中的 replicas 数量。
  • 专用模型实例:由于 vLLM 被设计为单模型服务引擎,并在初始化时固定其 KV 缓存内存,因此您必须为要托管的每个不同的 LLM 部署一组单独的 pod。
  • 负载均衡:GDC Kubernetes 服务(类型为 LoadBalancer)会自动将传入的推理请求分配给与该服务关联的所有运行正常的 vLLM pod 端点。

问题排查

错误 缓解
FIPS 自检失败 当 BoringSSL 等库缺少完整性签名时,会发生此错误。通过设置 BORINGSSL_FIPS=0 并利用官方 vLLM 映像来修复
权重加载停滞 检查小型 PVC 是否存在 IOPS 限制。性能模型初始化需要 500GiB 的卷。
连接遭拒 确认 PNP 明确允许 targetPort (8000/8080)。GDC 防火墙不会自动授予对负载均衡器 VIP 的后端端口的访问权限。