Posted on July 16, 2026 by Michael Troutman, LINBIT

CNCF projects highlighted in this post

Kubernetes logo llm-d logo Piraeus Datastore logo

在內部執行大型語言模型 (LLM) 工作負載,是團隊除了採用託管 API 服務之外的幾種模式之一。託管 API 服務方便且適用於許多工作負載。自託管則是某些團隊選擇的補充選項,其理由包括在高請求量時的成本可預測性、對延遲有更多控制,以及控制資料存放位置以符合合約或法規要求。混合式做法(針對高流量或敏感工作負載在本機執行開源模型,同時針對適合的任務使用託管 API 呼叫)是一種兼具兩者優點的實用中間方案。本文記錄了在 Kubernetes 實驗環境中建置這類自託管推論堆疊所需的步驟。在這個範例中,我們選擇使用 vLLM 進行推論,並使用 LINSTOR® 作為持久性儲存。

Background on vLLM

vLLM 是一個高效能的開源大型語言模型推論引擎。它專為在叢集環境中高效地提供許多並行請求而設計。

vLLM 在此使用情境中的一個重要特性是,它提供與 OpenAI 相容的 REST API。任何已經可以與 OpenAI API 溝通的工具(LangChain、LlamaIndex,或你自己呼叫 OpenAI SDK 的程式碼),只要變更 URL 就能指向自託管的 vLLM 執行個體。這種相容性正是前述混合式架構得以成為可行替代方案的原因。

Overview of the setup

本文的指示使用一個 Kubernetes 叢集,並由 LINSTOR 透過 LINSTOR Container Storage Interface (CSI) 驅動程式提供持久性儲存。Kubernetes 是整個堆疊的協調層,而儲存整合則遵循雲端原生生態系統中通用的標準 CSI。LINSTOR 是一個建構在 DRBD® 之上的開源軟體定義儲存解決方案,可在節點間提供複製的區塊儲存。複製式儲存適合用來儲存需要承受 Pod 重啟與節點故障的大型模型權重檔案。

本部落格使用的模型是 meta-llama/Llama-3.2-1B-Instruct,這是 Meta 提供的一個小型但功能強大的模型,已針對遵循使用者指示進行微調。由於只有 1B 參數,它輕量到可以在 CPU 上執行(這對沒有專用 GPU 節點的實驗環境很重要),同時仍足以用於測試此設定。

Prerequisites

在 Kubernetes 中部署任何東西之前,你需要先取得模型本身。Meta Llama 模型在 Hugging Face 上受到存取限制,這表示你必須先申請存取權限才能下載。

  1. huggingface.co 建立帳戶。
  2. 前往 Llama-3.2-1B-Instruct 模型頁面 並提交存取申請。
  3. 申請核准後,前往你的 Hugging Face 帳戶設定,建立具有讀取權限的存取權杖。
  4. 妥善保管該權杖,因為下一節會需要使用它。

你還需要在 Kubernetes 中部署 LINSTOR,並建立 StorageClass 以供 Kubernetes 工作負載申請 PersistentVolumeClaims。開源的 Piraeus Operator 會將 LINSTOR 與 LINSTOR CSI 驅動程式部署到 Kubernetes 中,並提供相關設定文件。在下一節中,你將從名為 linstor-csi-lvm-thin-r2 的 LINSTOR StorageClass 建立 PersistentVolumeClaim。

Deployment

此部署包含三個 Kubernetes 資源:用於模型儲存的 PersistentVolumeClaim、存放 Hugging Face 權杖的 Secret,以及用來執行推論伺服器的 Deployment 與 Service。

Creating the PVC and secret

此 PVC 使用 linstor-csi-lvm-thin-r2 儲存類別,該類別會佈建一個在叢集中擁有兩個複本的精簡佈建 LVM 磁碟區。這同時提供了備援與磁碟空間的有效利用,這點在模型權重容易消耗數十 GB 的情況下相當重要。

容器的 mountPath 為 /root/.cache/huggingface。這是 vLLM 容器快取已下載模型權重的位置。透過 LINSTOR 提供的持久性磁碟區掛載此路徑,模型只需從 Hugging Face 下載一次,之後的 Pod 重啟便可完全跳過下載步驟。

輸入以下指令以建立 PVC 與 Hugging Face 權杖 Secret:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: vllm-models
spec:
  storageClassName: linstor-csi-lvm-thin-r2
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 50Gi
---
apiVersion: v1
kind: Secret
metadata:
  name: hf-token-secret
type: Opaque
stringData:
  token: "REPLACE_WITH_YOUR_TOKEN"
EOF

IMPORTANT: 請將 REPLACE_WITH_YOUR_TOKEN 替換成你實際的 Hugging Face 存取權杖,並在套用此設定前變更 StorageClass 名稱(若你的名稱不同)。

Deploying vLLM

輸入以下指令以部署 vLLM 推論伺服器及其服務,此指令改編自 vLLM Kubernetes 部署文件

VLLM_IMAGE=public.ecr.aws/q9t5s3a7/vllm-cpu-release-repo:latest
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: vllm
  template:
    metadata:
      labels:
        app.kubernetes.io/name: vllm
    spec:
      containers:
      - name: vllm
        image: $VLLM_IMAGE
        command: ["/bin/sh", "-c"]
        args: [
          "vllm serve meta-llama/Llama-3.2-1B-Instruct --gpu-memory-utilization 0.80"
        ]
        env:
        - name: HF_TOKEN
          valueFrom:
            secretKeyRef:
              name: hf-token-secret
              key: token
        ports:
          - containerPort: 8000
        volumeMounts:
          - name: llama-storage
            mountPath: /root/.cache/huggingface
      volumes:
      - name: llama-storage
        persistentVolumeClaim:
          claimName: vllm-models
---
apiVersion: v1
kind: Service
metadata:
  name: vllm-server
spec:
  selector:
    app.kubernetes.io/name: vllm
  ports:
  - protocol: TCP
    port: 8000
    targetPort: 8000
  type: ClusterIP
EOF

📝 NOTE: 即使在僅使用 CPU 的部署中,vLLM 仍會使用 --gpu-memory-utilization 旗標來控制它為處理請求保留記憶體的積極程度。若未設定此旗標,vLLM 預設會保留 92% 的可用記憶體,這在我的測試環境中導致啟動失敗。將其設為 0.80 可在我的環境中為引擎成功初始化提供足夠的緩衝空間。

Watching the logs

第一次啟動需要幾分鐘。vLLM 需要從 Hugging Face 下載模型權重(此模型約 2.5GB),並初始化引擎。輸入以下指令以追蹤日誌。

kubectl logs -f deployment/vllm-server

當你看到類似「INFO: Application startup complete」的訊息時,即可進行測試。

Testing the deployment

從叢集內部測試的最簡單方式是部署一個臨時的 curl Pod。輸入以下指令以建立一個。

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: curl-client
  namespace: default
spec:
  containers:
  - name: curl
    image: curlimages/curl:latest
    command: ["sleep", "infinity"]
  restartPolicy: Never
EOF

在 Pod 中啟動互動式 shell 環境:

kubectl exec -it curl-client -- sh

並使用叢集內 DNS 名稱向 vLLM 服務發送請求:

curl http://vllm-server.default.svc.cluster.local:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "meta-llama/Llama-3.2-1B-Instruct",
    "messages": [
      {"role": "user", "content": "What is the capital of France?"}
    ]
  }'

If everything is working, you will get back a response in the familiar OpenAI format:

{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "The capital of France is Paris."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 15,
    "completion_tokens": 9,
    "total_tokens": 24
  }
}

測試完成後,刪除 curl Pod:

kubectl delete pod curl-client

Pausing the deployment

若你需要在排除問題時暫停部署,同時保留 PVC 與設定,可將其縮放到零:

kubectl scale deployment vllm-server --replicas=0

💡 TIP: 將部署縮放到零可保留 PVC 與快取的模型權重。當你再次擴展部署時,無需重新下載模型。

使用以下指令恢復:

kubectl scale deployment vllm-server --replicas=1

Conclusion

此實驗設定展示了基本模式:在 Kubernetes 中執行自託管 LLM,並由 LINSTOR 管理的複製式持久性儲存提供支援,且可透過標準的 OpenAI 相容 API 存取。將模型權重快取在 LINSTOR 磁碟區上,可讓 Pod 重啟更快,而 DRBD 提供的複製功能則確保磁碟區不會成為單點故障。

接下來,你可以嘗試在叢集中加入 GPU 節點以獲得顯著更好的效能,或在自己的私有資料上進一步訓練模型,使其更適合你的特定使用情境。如果你正朝向混合式 AI 發展,自然的下一步是加入推論路由器,根據成本、延遲或能力需求在本地部署與託管 API 之間分派請求。llm-d 專案 是一個專為此類設定設計的開源 Kubernetes 請求路由器。這些後續步驟是供進一步探索的方向,而非本文實驗涵蓋的設定。

如果你對在 Kubernetes 中執行 vLLM 或任何容器化應用程式搭配 LINSTOR,或是 LINBIT® 軟體在你環境中的其他使用情境有疑問,歡迎加入 LINBIT Community Forum