我們很高興地宣佈,透過 Red Hat AI 工程團隊和 Meta 的 Llama Stack 團隊之間的合作,vLLM 推理提供程式現已在 Llama Stack 中可用。本文將介紹此整合,並提供一個教程,幫助您開始在本地使用它或將其部署在 Kubernetes 叢集中。

什麼是 Llama Stack?

llama-stack-diagram

Llama Stack 定義並標準化了將生成式 AI 應用程式推向市場所需的核心構建模組集。這些構建模組以可互操作的 API 形式呈現,並由廣泛的服務提供商提供其實現。

Llama Stack 專注於讓使用者能夠輕鬆地使用各種模型構建生產應用程式,從最新的 Llama 3.3 模型到用於安全性的專用模型 Llama Guard 以及其他模型。目標是提供預打包的實現(也稱為“發行版”),這些實現可以在各種部署環境中執行。該 Stack 可以幫助您完成整個應用程式開發生命週期 - 從在本地、移動裝置或桌面裝置上迭代開始,並無縫過渡到本地部署或公共雲部署。在此過渡的每個階段,都可以使用相同的 API 集和相同的開發者體驗。

在此架構中,API 的每個特定實現都稱為“提供程式”。使用者可以透過配置來更換提供程式。vLLM 是支援推理 API 的高效能 API 的一個突出例子。

vLLM 推理提供程式

Llama Stack 提供了兩個 vLLM 推理提供程式

  1. 遠端 vLLM 推理提供程式,透過 vLLM 的 OpenAI 相容伺服器
  2. 內聯 vLLM 推理提供程式,與 Llama Stack 伺服器並行執行。

在本文中,我們將透過遠端 vLLM 推理提供程式演示該功能。

教程

前提條件

  • Linux 作業系統
  • 如果您想透過 CLI 下載模型,則需要 Hugging Face CLI
  • 符合 OCI 標準的容器技術,如 PodmanDocker(可以在執行 llama stack CLI 命令時透過 CONTAINER_BINARY 環境變數指定)。
  • 用於 Kubernetes 部署的 Kind
  • 用於管理 Python 環境的 Conda

透過容器開始

啟動 vLLM 伺服器

我們首先使用 Hugging Face CLI 下載 “Llama-3.2-1B-Instruct” 模型。請注意,您需要請求訪問許可權,然後在登入時指定您的 Hugging Face 令牌。

mkdir /tmp/test-vllm-llama-stack
huggingface-cli login --token <YOUR-HF-TOKEN>
huggingface-cli download meta-llama/Llama-3.2-1B-Instruct --local-dir /tmp/test-vllm-llama-stack/.cache/huggingface/hub/models/Llama-3.2-1B-Instruct

接下來,讓我們從原始碼構建 vLLM CPU 容器映象。請注意,雖然我們將其用於演示目的,但還有許多其他映象可用於不同的硬體和架構

git clone git@github.com:vllm-project/vllm.git /tmp/test-vllm-llama-stack
cd /tmp/test-vllm-llama-stack/vllm
podman build -f Dockerfile.cpu -t vllm-cpu-env --shm-size=4g .

然後我們可以啟動 vLLM 容器

podman run -it --network=host \
   --group-add=video \
   --ipc=host \
   --cap-add=SYS_PTRACE \
   --security-opt seccomp=unconfined \
   --device /dev/kfd \
   --device /dev/dri \
   -v /tmp/test-vllm-llama-stack/.cache/huggingface/hub/models/Llama-3.2-1B-Instruct:/app/model \
   --entrypoint='["python3", "-m", "vllm.entrypoints.openai.api_server", "--model", "/app/model", "--served-model-name", "meta-llama/Llama-3.2-1B-Instruct", "--port", "8000"]' \
    vllm-cpu-env

模型伺服器啟動後,我們可以獲取模型列表並測試提示

curl https://:8000/v1/models

curl https://:8000/v1/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "meta-llama/Llama-3.2-1B-Instruct",
        "prompt": "San Francisco is a",
        "max_tokens": 7,
        "temperature": 0
    }'

啟動 Llama Stack 伺服器

一旦我們驗證 vLLM 伺服器已成功啟動並能夠處理請求,我們就可以構建並啟動 Llama Stack 伺服器。

首先,我們克隆 Llama Stack 原始碼並建立一個包含所有依賴項的 Conda 環境

git clone git@github.com:meta-llama/llama-stack.git /tmp/test-vllm-llama-stack/llama-stack
cd /tmp/test-vllm-llama-stack/llama-stack
conda create -n stack python=3.10
conda activate stack
pip install .

接下來,我們使用 llama stack build 構建容器映象

cat > /tmp/test-vllm-llama-stack/vllm-llama-stack-build.yaml << "EOF"
name: vllm
distribution_spec:
  description: Like local, but use vLLM for running LLM inference
  providers:
    inference: remote::vllm
    safety: inline::llama-guard
    agents: inline::meta-reference
    vector_io: inline::faiss
    datasetio: inline::localfs
    scoring: inline::basic
    eval: inline::meta-reference
    post_training: inline::torchtune
    telemetry: inline::meta-reference
image_type: container
EOF

export CONTAINER_BINARY=podman
LLAMA_STACK_DIR=. PYTHONPATH=. python -m llama_stack.cli.llama stack build --config /tmp/test-vllm-llama-stack/vllm-llama-stack-build.yaml --image-name distribution-myenv

容器映象成功構建後,我們可以編輯生成的 vllm-run.yaml 檔案,將其更改為 /tmp/test-vllm-llama-stack/vllm-llama-stack-run.yaml,並在 models 欄位中進行以下更改

models:
- metadata: {}
  model_id: ${env.INFERENCE_MODEL}
  provider_id: vllm
  provider_model_id: null

然後我們可以使用我們透過 llama stack run 構建的映象啟動 Llama Stack 伺服器

export INFERENCE_ADDR=host.containers.internal
export INFERENCE_PORT=8000
export INFERENCE_MODEL=meta-llama/Llama-3.2-1B-Instruct
export LLAMA_STACK_PORT=5000

LLAMA_STACK_DIR=. PYTHONPATH=. python -m llama_stack.cli.llama stack run \
--env INFERENCE_MODEL=$INFERENCE_MODEL \
--env VLLM_URL=http://$INFERENCE_ADDR:$INFERENCE_PORT/v1 \
--env VLLM_MAX_TOKENS=8192 \
--env VLLM_API_TOKEN=fake \
--env LLAMA_STACK_PORT=$LLAMA_STACK_PORT \
/tmp/test-vllm-llama-stack/vllm-llama-stack-run.yaml

或者,我們可以改為執行以下 podman run 命令

podman run --security-opt label=disable -it --network host -v /tmp/test-vllm-llama-stack/vllm-llama-stack-run.yaml:/app/config.yaml -v /tmp/test-vllm-llama-stack/llama-stack:/app/llama-stack-source \
--env INFERENCE_MODEL=$INFERENCE_MODEL \
--env VLLM_URL=http://$INFERENCE_ADDR:$INFERENCE_PORT/v1 \
--env VLLM_MAX_TOKENS=8192 \
--env VLLM_API_TOKEN=fake \
--env LLAMA_STACK_PORT=$LLAMA_STACK_PORT \
--entrypoint='["python", "-m", "llama_stack.distribution.server.server", "--yaml-config", "/app/config.yaml"]' \
localhost/distribution-myenv:dev

一旦我們成功啟動 Llama Stack 伺服器,我們就可以開始測試推理請求

透過 Bash

llama-stack-client --endpoint https://:5000 inference chat-completion --message "hello, what model are you?"

輸出

ChatCompletionResponse(
    completion_message=CompletionMessage(
        content="Hello! I'm an AI, a conversational AI model. I'm a type of computer program designed to understand and respond to human language. My creators have 
trained me on a vast amount of text data, allowing me to generate human-like responses to a wide range of questions and topics. I'm here to help answer any question you 
may have, so feel free to ask me anything!",
        role='assistant',
        stop_reason='end_of_turn',
        tool_calls=[]
    ),
    logprobs=None
)

透過 Python

import os
from llama_stack_client import LlamaStackClient

client = LlamaStackClient(base_url=f"https://:{os.environ['LLAMA_STACK_PORT']}")

# List available models
models = client.models.list()
print(models)

response = client.inference.chat_completion(
    model_id=os.environ["INFERENCE_MODEL"],
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Write a haiku about coding"}
    ]
)
print(response.completion_message.content)

輸出

[Model(identifier='meta-llama/Llama-3.2-1B-Instruct', metadata={}, api_model_type='llm', provider_id='vllm', provider_resource_id='meta-llama/Llama-3.2-1B-Instruct', type='model', model_type='llm')]
Here is a haiku about coding:

Columns of code flow
Logic codes the endless night
Tech's silent dawn rise

在 Kubernetes 上部署

除了在本地啟動 Llama Stack 和 vLLM 伺服器,我們還可以將它們部署在 Kubernetes 叢集中。出於演示目的,我們將使用本地 Kind 叢集

kind create cluster --image kindest/node:v1.32.0 --name llama-stack-test

將 vLLM 伺服器作為 Kubernetes Pod 和 Service 啟動(請記住將 <YOUR-HF-TOKEN> 替換為您的實際令牌)

cat <<EOF |kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: vllm-models
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 50Gi
---
apiVersion: v1
kind: Secret
metadata:
  name: hf-token-secret
type: Opaque
data:
  token: "<YOUR-HF-TOKEN>"
---
apiVersion: v1
kind: Pod
metadata:
  name: vllm-server
  labels:
    app: vllm
spec:
  containers:
  - name: llama-stack
    image: localhost/vllm-cpu-env:latest
    command:
        - bash
        - -c
        - |
          MODEL="meta-llama/Llama-3.2-1B-Instruct"
          MODEL_PATH=/app/model/$(basename $MODEL)
          huggingface-cli login --token $HUGGING_FACE_HUB_TOKEN
          huggingface-cli download $MODEL --local-dir $MODEL_PATH --cache-dir $MODEL_PATH
          python3 -m vllm.entrypoints.openai.api_server --model $MODEL_PATH --served-model-name $MODEL --port 8000
    ports:
      - containerPort: 8000
    volumeMounts:
      - name: llama-storage
        mountPath: /app/model
    env:
      - name: HUGGING_FACE_HUB_TOKEN
        valueFrom:
          secretKeyRef:
            name: hf-token-secret
            key: token
  volumes:
  - name: llama-storage
    persistentVolumeClaim:
      claimName: vllm-models
---
apiVersion: v1
kind: Service
metadata:
  name: vllm-server
spec:
  selector:
    app: vllm
  ports:
  - port: 8000
    targetPort: 8000
  type: NodePort
EOF

我們可以透過日誌驗證 vLLM 伺服器是否已成功啟動(下載模型可能需要幾分鐘)

$ kubectl logs vllm-server
...
INFO:     Started server process [1]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

然後我們可以修改之前建立的 vllm-llama-stack-run.yaml 檔案,將其更改為 /tmp/test-vllm-llama-stack/vllm-llama-stack-run-k8s.yaml,並使用以下推理提供程式

providers:
  inference:
  - provider_id: vllm
    provider_type: remote::vllm
    config:
      url: http://vllm-server.default.svc.cluster.local:8000/v1
      max_tokens: 4096
      api_token: fake

一旦我們定義了 Llama Stack 的執行配置,我們就可以使用該配置和伺服器原始碼構建映象

cat >/tmp/test-vllm-llama-stack/Containerfile.llama-stack-run-k8s <<EOF
FROM distribution-myenv:dev

RUN apt-get update && apt-get install -y git
RUN git clone https://github.com/meta-llama/llama-stack.git /app/llama-stack-source

ADD ./vllm-llama-stack-run-k8s.yaml /app/config.yaml
EOF
podman build -f /tmp/test-vllm-llama-stack/Containerfile.llama-stack-run-k8s -t llama-stack-run-k8s /tmp/test-vllm-llama-stack

然後我們可以透過部署 Kubernetes Pod 和 Service 來啟動 Llama Stack 伺服器

cat <<EOF |kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: llama-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: llama-stack-pod
  labels:
    app: llama-stack
spec:
  containers:
  - name: llama-stack
    image: localhost/llama-stack-run-k8s:latest
    imagePullPolicy: IfNotPresent
    command: ["python", "-m", "llama_stack.distribution.server.server", "--yaml-config", "/app/config.yaml"]
    ports:
      - containerPort: 5000
    volumeMounts:
      - name: llama-storage
        mountPath: /root/.llama
  volumes:
  - name: llama-storage
    persistentVolumeClaim:
      claimName: llama-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: llama-stack-service
spec:
  selector:
    app: llama-stack
  ports:
  - protocol: TCP
    port: 5000
    targetPort: 5000
  type: ClusterIP
EOF

我們可以檢查 Llama Stack 伺服器是否已啟動

$ kubectl logs vllm-server
...
INFO:     Started server process [1]
INFO:     Waiting for application startup.
INFO:     ASGI 'lifespan' protocol appears unsupported.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://['::', '0.0.0.0']:5000 (Press CTRL+C to quit)

現在讓我們將 Kubernetes 服務轉發到本地埠,並透過 Llama Stack 客戶端針對它測試一些推理請求

kubectl port-forward service/llama-stack-service 5000:5000
llama-stack-client --endpoint https://:5000 inference chat-completion --message "hello, what model are you?"

您可以在官方文件中瞭解更多關於 Llama Stack 的不同提供程式和功能的資訊。

致謝

我們要感謝 Red Hat AI 工程團隊實現了 vLLM 推理提供程式,併為許多錯誤修復、改進和關鍵設計討論做出了貢獻。我們還要感謝 Meta 的 Llama Stack 團隊和 vLLM 團隊及時進行的 PR 審查和錯誤修復。