引入 Llama Stack 中的 vLLM 推理提供程序

8 分钟阅读
Yuan Tang (Red Hat) 和 Ashwin Bharambe (Meta)

我们很高兴地宣布,通过 Red Hat AI 工程团队与 Meta Llama Stack 团队的合作,vLLM 推理提供程序现已在 Llama Stack 中可用。本文介绍了这一集成,并提供了一份教程,帮助您在本地或 Kubernetes 集群中部署并使用它。

什么是 Llama Stack?

llama-stack-diagram
llama-stack-diagram
Llama Stack 定义并标准化了将生成式 AI 应用推向市场所需的核心构建块。这些构建块以可互操作的 API 形式呈现,并由广泛的服务提供程序(Service Providers)提供具体实现。

Llama Stack 致力于简化构建生产级应用程序的过程,支持多种模型——从最新的 Llama 3.3 模型到用于安全性的 Llama Guard 等专业模型。其目标是提供预打包的实现(即“发行版”),并可在多种部署环境中运行。Stack 可以协助您完成整个应用开发生命周期:从本地、移动或桌面端开始迭代,并无缝过渡到本地部署或公有云部署。在过渡的每一步,您都可以使用相同的 API 和开发人员体验。

在此架构中,API 的每个具体实现都称为“提供程序”(Provider)。用户可以通过配置更换提供程序。vLLM 是一个支持推理 API 的高性能 API 的典型示例。

vLLM 推理提供程序

Llama Stack 提供两种 vLLM 推理提供程序

  1. 远程 vLLM 推理提供程序,通过 vLLM 的 OpenAI 兼容服务器实现;
  2. 内联(Inline)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 Token。

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

一旦模型服务器启动,我们可以获取模型列表并测试提示词(prompt)

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> 替换为您实际的 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://:5000 inference chat-completion --message "hello, what model are you?"

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

致谢

我们衷心感谢 Red Hat AI 工程团队在 vLLM 推理提供程序的实现上所做的工作,以及在漏洞修复、改进和关键设计讨论方面的贡献。我们还要感谢来自 Meta 的 Llama Stack 团队和 vLLM 团队,感谢他们及时的 PR 审查和漏洞修复。