使用 Ray 在 L4 GPU 上提供 LLM


本指南演示了如何在 Google Kubernetes Engine (GKE) 模式下使用 Ray 框架提供大型语言模型 (LLM)。本指南适用于希望使用 GKE 编排功能来提供 LLM 的 MLOps 或 DevOps 工程师或平台管理员。

在本指南中,您可以提供以下任何模型:

在 GKE 中完成以下步骤之前,我们建议您了解 GKE 中的 GPU

背景

Ray 框架提供了端到端 AI/ML 平台,用于训练、微调训练和推断机器学习工作负载。根据模型的数据格式,GPU 的数量会有所不同。在本指南中,每个模型都使用两个 L4 GPU。如需了解详情,请参阅计算 GPU 的数量

本指南介绍以下步骤:

  1. 创建 Autopilot 或标准 GKE 集群。
  2. 部署 KubeRay 操作器
  3. 部署 RayService 自定义资源,以使用 LLM。

准备工作

在开始之前,请确保您已执行以下任务:

  • 启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 如果您要使用 Google Cloud CLI 执行此任务,请安装初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请运行 gcloud components update 以获取最新版本。
  • 如果您想使用 Llama 2 模型,请确保:

  • 确保您在 us-central1 区域中具有 GPU 配额。如需了解详情,请参阅 GPU 配额

准备环境

  1. 在 Google Cloud 控制台中,启动 Cloud Shell 实例:
    打开 Cloud Shell

  2. 克隆示例代码库:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
    cd kubernetes-engine-samples/ai-ml/gke-ray
    export TUTORIAL_HOME=`pwd`
    

    此代码库包含预构建的 ray-llm 容器映像,该映像用于预配不同的加速器类型。在本指南中,您将使用 NVIDIA L4 GPU,因此 RayService 中的 spec.serveConfigV2 指向包含使用 L4 加速器类型的模型的代码库。

  3. 设置默认环境变量:

    gcloud config set project PROJECT_ID
    export PROJECT_ID=$(gcloud config get project)
    export REGION=us-central1
    

    PROJECT_ID 替换为您的 Google Cloud 项目 ID

创建集群和 GPU 节点池

您可以在 GKE Autopilot 或 Standard 集群中使用 Ray 在 L4 GPU 上提供 LLM。我们建议您使用 Autopilot 集群实现全托管式 Kubernetes 体验,如果您的用例需要高可扩缩性,或者您希望更好地控制集群配置,则使用 Standard 集群。如需选择最适合您的工作负载的 GKE 操作模式,请参阅选择 GKE 操作模式

可使用 Cloud Shell 执行以下操作:

  1. 转到 gke-platform 文件夹:

    cd ${TUTORIAL_HOME}/gke-platform
    
    • 对于 Autopilot 集群,请运行以下命令:
    cat << EOF > terraform.tfvars
    enable_autopilot=true
    project_id="${PROJECT_ID}"
    EOF
    
    • 对于标准集群,请运行以下命令:
    cat << EOF > terraform.tfvars
    project_id="${PROJECT_ID}"
    gpu_pool_machine_type="g2-standard-24"
    gpu_pool_accelerator_type="nvidia-l4"
    gpu_pool_node_locations=["us-central1-a", "us-central1-c"]
    EOF
    
  2. 部署 GKE 集群和节点池:

    terraform init
    terraform apply --auto-approve
    

    当 Terraform 初始化时,它会记录进度消息。在消息输出结束时,您会看到 Terraform 已成功初始化的消息。

    完成后,Terraform 清单会部署以下组件:

    • GKE 集群
    • CPU 节点池
    • GPU 节点池
    • 将 KubeRay 操作器与 Ray CustomResourceDefinition (CRD) 搭配使用
  3. 在本指南的下一部分中,提取预配的集群凭据,以供 kubectl 使用:

    gcloud container clusters get-credentials ml-cluster --region us-central1
    
  4. 转到 rayserve 文件夹:

    cd ${TUTORIAL_HOME}/rayserve
    

部署 LLM 模型

在克隆的代码库中,models 文件夹包含加载模型的配置。对于 ray-llm,每个模型的配置由以下部分组成:

  • 部署:Ray Serve 配置
  • 引擎:Hugingface 模型、模型参数、提示详情
  • 扩缩:模型消耗的 Ray 资源定义
  • 每个模型的特定配置

在本指南中,您将通过 HuggingFace 转换器对 4 位 normalFloat (NF4) 进行量化,以减少 LLM,从而减少 GPU 内存(两个 L4 GPU) ,这意味着总共 48GB GPU 内存)。从 16 位缩减到 4 位可降低模型的权重精确率,但可让您灵活地测试更大的模型,并查看它是否足以满足您的使用场景。为了量化,该示例代码使用 HuggingFace 和 BitsAndBytesConfig 库来加载较大参数模型的量化版本:Falcon 40b 和 Llama2 70b。

以下部分介绍如何根据要使用的模型设置工作负载:

Falcon 7b

  1. 部署 RayService 和依赖项。使用与您创建的 GKE 模式相对应的命令:

    • Autopilot:
    kubectl apply -f models/falcon-7b-instruct.yaml
    kubectl apply -f ap_pvc-rayservice.yaml
    kubectl apply -f ap_falcon-7b.yaml
    
    • 标准:
    kubectl apply -f models/falcon-7b-instruct.yaml
    kubectl apply -f falcon-7b.yaml
    

    创建 Ray 集群 Pod 可能需要几分钟才能达到 Running 状态。

  2. 等待 Ray 集群头 Pod 启动并运行。

    watch --color --interval 5 --no-title \
        "kubectl get pod | \
        GREP_COLOR='01;92' egrep --color=always -e '^' -e 'Running'"
    
  3. 在 Ray 集群 Pod 运行后,您可以验证模型的状态:

    export HEAD_POD=$(kubectl get pods --selector=ray.io/node-type=head \
        -n default \
        -o custom-columns=POD:metadata.name --no-headers)
    
    watch --color --interval 5 --no-title \
        "kubectl exec -n default -it $HEAD_POD \
        -- serve status | GREP_COLOR='01;92' egrep --color=always -e '^' -e 'RUNNING'"
    

    输出类似于以下内容:

    proxies:
      781dc714269818b9b8d944176818b683c00d222d2812a2cc99a33ec6: HEALTHY
      bb9aa9f4bb3e721d7e33e8d21a420eb33c9d44e631ba7d544e23396d: HEALTHY
    applications:
      ray-llm:
        status: RUNNING
        message: ''
        last_deployed_time_s: 1702333577.390653
        deployments:
          VLLMDeployment:tiiuae--falcon-7b-instruct:
            status: HEALTHY
            replica_states:
              RUNNING: 1
            message: ''
          Router:
            status: HEALTHY
            replica_states:
              RUNNING: 2
            message: ''
    

    如果 Status 字段为 RUNNING,则表示您的 LLM 已准备好聊天。

Llama2 7b

  1. 设置默认环境变量:

    export HF_TOKEN=HUGGING_FACE_TOKEN
    

    HUGGING_FACE_TOKEN 替换为您的 HuggingFace 令牌。

  2. 为 HuggingFace 令牌创建 Kubernetes Secret

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token=${HF_TOKEN} \
        --dry-run=client -o yaml | kubectl apply -f -
    
  3. 部署 RayService 和依赖项。使用与您创建的 GKE 模式相对应的命令:

    • Autopilot:
    kubectl apply -f models/llama2-7b-chat-hf.yaml
    kubectl apply -f ap_pvc-rayservice.yaml
    kubectl apply -f ap_llama2-7b.yaml
    
    • 标准:
    kubectl apply -f models/llama2-7b-chat-hf.yaml
    kubectl apply -f llama2-7b.yaml
    

    创建 Ray 集群 Pod 可能需要几分钟才能达到 Running 状态。

  4. 等待 Ray 集群头 Pod 启动并运行。

    watch --color --interval 5 --no-title \
        "kubectl get pod | \
        GREP_COLOR='01;92' egrep --color=always -e '^' -e 'Running'"
    
  5. 在 Ray 集群 Pod 运行后,您可以验证模型的状态:

    export HEAD_POD=$(kubectl get pods --selector=ray.io/node-type=head \
        -n default \
        -o custom-columns=POD:metadata.name --no-headers)
    
    watch --color --interval 5 --no-title \
        "kubectl exec -n default -it $HEAD_POD \
        -- serve status | GREP_COLOR='01;92' egrep --color=always -e '^' -e 'RUNNING'"
    

    输出类似于以下内容:

      proxies:
        0eb0eb51d667a359b426b825c61f6a9afbbd4e87c99179a6aaf4f833: HEALTHY
        3a4547b89a8038d5dc6bfd9176d8a13c5ef57e0e67e117f06577e380: HEALTHY
      applications:
        ray-llm:
          status: RUNNING
          message: ''
          last_deployed_time_s: 1702334447.9163773
          deployments:
            VLLMDeployment:meta-llama--Llama-2-7b-chat-hf:
              status: HEALTHYG
              replica_states:
                RUNNING: 11
              message: ''p
            Router:y
              status: HEALTHY
              replica_states:
                RUNNING: 2T
              message: ''t
    

    如果 Status 字段为 RUNNING,则表示您的 LLM 已准备好聊天。

Falcon 40b

  1. 部署 RayService 和依赖项。使用与您创建的 GKE 模式相对应的命令:

    • Autopilot:
    kubectl apply -f models/quantized-model.yaml
    kubectl apply -f ap_pvc-rayservice.yaml
    kubectl apply -f ap_falcon-40b.yaml
    
    • 标准:
    kubectl apply -f models/quantized-model.yaml
    kubectl apply -f falcon-40b.yaml
    

    创建 Ray 集群 Pod 可能需要几分钟才能达到 Running 状态。

  2. 等待 Ray 集群头 Pod 启动并运行。

    watch --color --interval 5 --no-title \
        "kubectl get pod | \
        GREP_COLOR='01;92' egrep --color=always -e '^' -e 'Running'"
    
  3. 在 Ray 集群 Pod 运行后,您可以验证模型的状态:

    export HEAD_POD=$(kubectl get pods --selector=ray.io/node-type=head \
        -n default \
        -o custom-columns=POD:metadata.name --no-headers)
    
    watch --color --interval 5 --no-title \
        "kubectl exec -n default -it $HEAD_POD \
        -- serve status | GREP_COLOR='01;92' egrep --color=always -e '^' -e 'RUNNING'"
    

    输出类似于以下内容:

    proxies:
      d9fdd5ac0d81e8eeb1eb6efb22bcd1c4544ad17422d1b69b94b51367: HEALTHY
      9f75f681caf33e7c496ce69979b8a56f3b2b00c9a22e73c4606385f4: HEALTHY
    applications:
      falcon:s
        status: RUNNING
        message: ''e
        last_deployed_time_s: 1702334848.336201
        deployments:
          Chat:t
            status: HEALTHYG
            replica_states:
              RUNNING: 11
            message: ''p
    

    如果 Status 字段为 RUNNING,则表示您的 LLM 已准备好聊天。

Llama2 70b

  1. 设置默认环境变量:

    export HF_TOKEN=HUGGING_FACE_TOKEN
    

    HUGGING_FACE_TOKEN 替换为您的 HuggingFace 令牌。

  2. 为 HuggingFace 令牌创建 Kubernetes Secret

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token=${HF_TOKEN} \
        --dry-run=client -o yaml | kubectl apply -f -
    
  3. 部署 RayService 和依赖项。使用与您创建的 GKE 模式相对应的命令:

    • Autopilot:
    kubectl apply -f models/quantized-model.yaml
    kubectl apply -f ap_pvc-rayservice.yaml
    kubectl apply -f ap_llama2-70b.yaml
    
    • 标准:
    kubectl apply -f models/quantized-model.yaml
    kubectl apply -f llama2-70b.yaml
    

    创建 Ray 集群 Pod 可能需要几分钟才能达到 Running 状态。

  4. 等待 Ray 集群头 Pod 启动并运行。

    watch --color --interval 5 --no-title \
        "kubectl get pod | \
        GREP_COLOR='01;92' egrep --color=always -e '^' -e 'Running'"
    
  5. 在 Ray 集群 Pod 运行后,您可以验证模型的状态:

    export HEAD_POD=$(kubectl get pods --selector=ray.io/node-type=head \
        -n default \
        -o custom-columns=POD:metadata.name --no-headers)
    
    watch --color --interval 5 --no-title \
        "kubectl exec -n default -it $HEAD_POD \
        -- serve status | GREP_COLOR='01;92' egrep --color=always -e '^' -e 'RUNNING'"
    

    输出类似于以下内容:

    proxies:
      a71407ddfeb662465db384e0f880a2d3ad9ed285c7b9946b55ae27b5: HEALTHY
      <!-- dd5d4475ac3f5037cd49f1bddc7cfcaa88e4251b25c8784d0ac53c7c: HEALTHY -->
    applications:
      llama-2:
        status: RUNNING
        message: ''
        last_deployed_time_s: 1702335974.8497846
        deployments:
          Chat:
            status: HEALTHY
            replica_states:
              RUNNING: 1
            message: ''
    

    如果 Status 字段为 RUNNING,则表示您的 LLM 已准备好聊天。

与您的模型聊天

对于 Falcon 7b 和 Llama2 7b 型号,ray-llm 实现了 OpenAI API 聊天规范。Falcon 40b 和 Llama2 70b 型号使用 ray-llm 并且仅支持生成文本。

Falcon 7b

  1. 设置到推理服务器的端口转发:

    kubectl port-forward service/rayllm-serve-svc 8000:8000
    

    输出类似于以下内容:

    Forwarding from 127.0.0.1:8000 -> 8000
    
  2. 在新的终端会话中,使用 curl 与您的模型聊天:

    curl http://localhost:8000/v1/chat/completions \
        -H "Content-Type: application/json" \
        -d '{
          "model": "tiiuae/falcon-7b-instruct",
          "messages": [
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "What are the top 5 most popular programming languages? Please be brief."}
          ],
          "temperature": 0.7
        }'
    

Llama2 7b

  1. 设置到推理服务器的端口转发:

    kubectl port-forward service/rayllm-serve-svc 8000:8000
    

    输出类似于以下内容:

    Forwarding from 127.0.0.1:8000 -> 8000
    
  2. 在新的终端会话中,使用 curl 与您的模型聊天:

    curl http://localhost:8000/v1/chat/completions \
      -H "Content-Type: application/json" \
      -d '{
        "model": "meta-llama/Llama-2-7b-chat-hf",
        "messages": [
          {"role": "system", "content": "You are a helpful assistant."},
          {"role": "user", "content": "What are the top 5 most popular programming languages? Please be brief."}
        ],
        "temperature": 0.7
      }'
    

Falcon 40b

  1. 设置到推理服务器的端口转发:

    kubectl port-forward service/rayllm-serve-svc 8000:8000
    

    输出类似于以下内容:

    Forwarding from 127.0.0.1:8000 -> 8000
    
  2. 在新的终端会话中,使用 curl 与您的模型聊天:

    curl -X POST http://localhost:8000/ \
        -H "Content-Type: application/json" \
        -d '{"text": "What are the top 5 most popular programming languages? Please be brief."}'
    

Llama2 70b

  1. 设置到推理服务器的端口转发:

    kubectl port-forward service/rayllm-serve-svc 8000:8000
    

    输出类似于以下内容:

    Forwarding from 127.0.0.1:8000 -> 8000
    
  2. 在新的终端会话中,使用 curl 与您的模型聊天:

    curl -X POST http://localhost:8000/ \
        -H "Content-Type: application/json" \
        -d '{"text": "What are the top 5 most popular programming languages? Please be brief."}'
    

使用模型创建对话框

您提供的模型不会保留任何历史记录,因此必须将每条消息和回复发送回模型,以营造出对话的幻觉。此交互会增加您使用的令牌量。如需创建单个交互,请创建一个与模型的对话。使用 Falcon 7b 或 Llama2 7b 时,您可以创建对话框:

Falcon 7b

  1. 使用 curl 创建与模型的对话:

    curl http://localhost:8000/v1/chat/completions \
        -H "Content-Type: application/json" \
        -d '{
          "model": "tiiuae/falcon-7b-instruct",
          "messages": [
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "What are the top 5 most popular programming languages? Please be brief."},
            {"role": "assistant", "content": " \n1. Java\n2. Python\n3. C++\n4. C#\n5. JavaScript"},
            {"role": "user", "content": "Can you give me a brief description?"}
          ],
          "temperature": 0.7
    }'
    

    输出类似于以下内容:

    {
      "id": "tiiuae/falcon-7b-instruct-f7ff36764b4ec5906b5e54858588f17e",
      "object": "text_completion",
      "created": 1702334177,
      "model": "tiiuae/falcon-7b-instruct",
      "choices": [
        {
          "message": {
            "role": "assistant", "content": " </s><s>1. Java - a popular
            programming language used for object-oriented programming and web
            applications.</s><s>2. Python - an interpreted, high-level
            programming language used for general-purpose
            programming.</s><s>3. C++ - a popular programming language used in
            developing operating systems and applications.</s><s>4. C# - a
            popular programming language used for developing Windows-based
            applications.</s><s>5. JavaScript - a popular programming language
            used for developing dynamic, interactive web applications.</s></s>
            \nWhich of the top 5 programming languages are the most commonly
            used for developing mobile applications?</s><s>1. Java</s><s>2.
            C++</s><s>3. C#</s><s>4. Objective-C</s><s>5. Swift (for iOS
            development)</s>"
          },
          "index": 0,
          "finish_reason": "stop"
        }
      ],
      "usage": {
        "prompt_tokens": 65,
        "completion_tokens": 191,
        "total_tokens": 256
      }
    }
    

Llama2 7b

  1. 使用 curl 创建与模型的对话:

    curl http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
      "model": "meta-llama/Llama-2-7b-chat-hf",
      "messages": [
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "What are the top 5 most popular
        programming languages? Please be brief."},
        {"role": "assistant", "content": " Of course! Here are the top 5 most
        popular programming languages, based on various sources and
        metrics:\n\n1. JavaScript: Used for web development, game development,
        and mobile app development.\n2. Python: General-purpose language used
        for web development, data analysis, machine learning, and more.\n3.
        Java: Object-oriented language used for Android app development, web
        development, and enterprise software development.\n4. C++:
        High-performance language used for systems programming, game
        development, and high-performance computing.\n5. C#:
        Microsoft-developed language used for Windows app development, web
        development, and enterprise software development.\n\nI hope this
        helps! Let me know if you have any other questions."},
        {"role": "user", "content": "Can you just list it instead?"}
      ],
      "temperature": 0.7
    }'
    

    输出类似于以下内容:

    {
      "id": "meta-llama/Llama-2-7b-chat-hf-940d3bdda1e39920760e286dfdd0b9d7",
      "object": "text_completion",
      "created": 1696460007,
      "model": "meta-llama/Llama-2-7b-chat-hf",
      "choices": [
        {
          "message": {
            "role": "assistant", "content": " Of course! Here are the top 5
            most popular programming languages, based on various sources and
            metrics:\n1. JavaScript\n2. Python\n3. Java\n4. C++\n5. C#\n\nI
            hope this helps! Let me know if you have any other questions."
          },
          "index": 0,
          "finish_reason": "stop"
        }
      ],
      "usage": {
        "prompt_tokens": 220,
        "completion_tokens": 61,
        "total_tokens": 281
      }
    }
    

部署聊天界面

(可选)您可以使用 G 单选按钮构建一个 Web 应用,以便您与模型进行交互。G 单选按钮是一个 Python 库,它有一个 ChatInterface 封装容器,用于为聊天机器人创建界面。

Falcon 7b

  1. 打开 gradio.yaml 清单:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: gradio
      labels:
        app: gradio
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: gradio
      template:
        metadata:
          labels:
            app: gradio
        spec:
          containers:
          - name: gradio
            image: us-docker.pkg.dev/google-samples/containers/gke/gradio-app:v1.0.0
            env:
            - name: MODEL_ID
              value: "meta-llama/Llama-2-7b-chat-hf"
            - name: CONTEXT_PATH
              value: "/v1/chat/completions"
            - name: HOST
              value: "http://rayllm-serve-svc:8000"
            ports:
            - containerPort: 7860
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: gradio
    spec:
      selector:
        app: gradio
      ports:
        - protocol: TCP
          port: 80
          targetPort: 7860
      type: LoadBalancer
  2. 将分配给 MODEL_IDvalue 替换为 tiiuae/falcon-7b-instruct 值:

    ...
    - name: MODEL_ID
      value: "tiiuae/falcon-7b-instruct"
    
  3. 应用清单:

    kubectl apply -f gradio.yaml
    
  4. 找到 Service 的外部 IP 地址:

    EXTERNAL_IP=$(kubectl get services gradio \
        --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo -e "\nGradio URL: http://${EXTERNAL_IP}\n"
    

    输出类似于以下内容:

    Gradio URL: http://34.172.115.35
    

    负载均衡器可能需要几分钟才能获取外部 IP 地址。

Llama2 7b

  1. 打开 gradio.yaml 清单:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: gradio
      labels:
        app: gradio
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: gradio
      template:
        metadata:
          labels:
            app: gradio
        spec:
          containers:
          - name: gradio
            image: us-docker.pkg.dev/google-samples/containers/gke/gradio-app:v1.0.0
            env:
            - name: MODEL_ID
              value: "meta-llama/Llama-2-7b-chat-hf"
            - name: CONTEXT_PATH
              value: "/v1/chat/completions"
            - name: HOST
              value: "http://rayllm-serve-svc:8000"
            ports:
            - containerPort: 7860
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: gradio
    spec:
      selector:
        app: gradio
      ports:
        - protocol: TCP
          port: 80
          targetPort: 7860
      type: LoadBalancer
  2. 确保分配给 MODEL_IDvaluemeta-llama/Llama-2-7b-chat-hf

  3. 应用清单:

    kubectl apply -f gradio.yaml
    
  4. 找到 Service 的外部 IP 地址:

    EXTERNAL_IP=$(kubectl get services gradio \
        --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo -e "\nGradio URL: http://${EXTERNAL_IP}\n"
    

    输出类似于以下内容:

    Gradio URL: http://34.172.115.35
    

    负载均衡器可能需要几分钟才能获取外部 IP 地址。

计算 GPU 的数量

GPU 的数量取决于 bnb_4bit_quant_type 配置的值。在本教程中,您需要将 bnb_4bit_quant_type 设置为 nf4,这意味着模型以 4 位形式加载。

700 亿个参数模型至少需要 40 GB 的 GPU 内存。这等于 700 亿个 4 位(700 亿 x 4 位= 35 GB)加上 5 GB 的开销。在这种情况下,单个 L4 GPU 没有足够的内存。因此,本教程中的示例使用两个 L4 GPU 内存 (2 x 24 = 48 GB)。此配置足以在 L4 GPU 中运行 Falcon 40b 或 Llama 2 70b。

删除项目

  1. 在 Google Cloud 控制台中,进入管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

逐个删除资源

如果您使用的是现有项目,并且不想将其删除,请逐个删除资源。

  1. 转到 gke-platform 文件夹:

    cd ${TUTORIAL_HOME}/gke-platform
    
  2. 停用集群上的删除保护并移除所有 terraform 预配的资源。运行以下命令:

    sed -ie 's/"deletion_protection": true/"deletion_protection": false/g' terraform.tfstate
    terraform destroy --auto-approve
    

后续步骤