我们很高兴地宣布,通过红帽 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 环境变量来指定)。
  • Kind 用于 Kubernetes 部署。
  • Conda 用于管理 Python 环境。

通过容器开始

启动 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://127.0.0.1:8000/v1/models

curl https://127.0.0.1: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://127.0.0.1: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://127.0.0.1:{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 Client 测试一些针对它的推理请求

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

您可以在 官方文档 上了解有关 Llama Stack 的不同提供程序和功能的更多信息。

致谢

我们要感谢红帽 AI 工程团队实现了 vLLM 推理提供程序,并为许多错误修复、改进和关键设计讨论做出了贡献。我们还要感谢 Meta 的 Llama Stack 团队和 vLLM 团队及时进行的 PR 审查和错误修复。