部署多个应用#

Serve 支持部署多个独立的 Serve 应用。本用户指南将引导您了解如何生成多应用配置文件,使用 Serve CLI 进行部署,并通过 CLI 和 Ray Serve Dashboard 监控您的应用。

背景#

背景#

随着多应用 Serve 的引入,我们将向您介绍应用的新概念,以及何时应该选择在每个集群中部署单个应用还是多个应用。

一个应用由一个或多个部署组成。一个应用中的部署通过 模型组合 连接成一个定向无环图。一个应用可以通过指定的路由前缀通过 HTTP 调用,并且入口部署处理所有入站流量。由于一个应用中的部署之间存在依赖关系,一个应用就是一个升级单元。

何时使用多个应用#

您可以通过使用模型组合或多应用来解决许多用例。然而,两者都有其各自的优点,并且可以一起使用。

假设您有多个模型和/或业务逻辑都需要为单个请求执行。如果它们位于同一个存储库中,那么您很可能将它们作为一个单元进行升级,因此我们建议将所有这些部署放在一个应用中。

另一方面,如果这些模型或业务逻辑有逻辑分组,例如,相互通信但位于不同存储库中的模型组,我们建议将模型分离到不同的应用中。另一个常见的多应用用例是分离可能不相互通信的模型组,但您希望将它们托管在一起以提高硬件利用率。因为一个应用是一个升级单元,拥有多个应用可以使您为每个独立模型(或模型组)部署不同的端点。然后,您可以轻松地向集群添加或删除应用,并独立于其他应用升级应用。

入门#

定义一个 Serve 应用

import requests
import starlette

from transformers import pipeline
from io import BytesIO
from PIL import Image

from ray import serve
from ray.serve.handle import DeploymentHandle


@serve.deployment
def downloader(image_url: str):
    image_bytes = requests.get(image_url).content
    image = Image.open(BytesIO(image_bytes)).convert("RGB")
    return image


@serve.deployment
class ImageClassifier:
    def __init__(self, downloader: DeploymentHandle):
        self.downloader = downloader
        self.model = pipeline(
            "image-classification", model="google/vit-base-patch16-224"
        )

    async def classify(self, image_url: str) -> str:
        image = await self.downloader.remote(image_url)
        results = self.model(image)
        return results[0]["label"]

    async def __call__(self, req: starlette.requests.Request):
        req = await req.json()
        return await self.classify(req["image_url"])


app = ImageClassifier.bind(downloader.bind())

将此代码复制到名为 image_classifier.py 的文件中。

定义第二个 Serve 应用

import starlette

from transformers import pipeline

from ray import serve


@serve.deployment
class Translator:
    def __init__(self):
        self.model = pipeline("translation_en_to_de", model="t5-small")

    def translate(self, text: str) -> str:
        return self.model(text)[0]["translation_text"]

    async def __call__(self, req: starlette.requests.Request):
        req = await req.json()
        return self.translate(req["text"])


app = Translator.bind()

将此代码复制到名为 text_translator.py 的文件中。

生成一个包含这两个应用的多应用配置文件,并将其保存到 config.yaml

serve build image_classifier:app text_translator:app -o config.yaml

这将生成以下配置

proxy_location: EveryNode

http_options:
  host: 0.0.0.0
  port: 8000

grpc_options:
  port: 9000
  grpc_servicer_functions: []

logging_config:
  encoding: JSON
  log_level: INFO
  logs_dir: null
  enable_access_log: true

applications:
  - name: app1
    route_prefix: /classify
    import_path: image_classifier:app
    runtime_env: {}
    deployments:
      - name: downloader
      - name: ImageClassifier

  - name: app2
    route_prefix: /translate
    import_path: text_translator:app
    runtime_env: {}
    deployments:
      - name: Translator

注意

每个应用的名称都会自动生成为 app1app2 等。要为应用指定自定义名称,请在继续下一步之前修改配置文件。

部署应用#

要部署应用,请确保先启动 Ray 集群。

$ ray start --head

$ serve deploy config.yaml
> Sent deploy request successfully!

通过各自的端点 /classify/translate 查询应用。

bear_url = "https://cdn.britannica.com/41/156441-050-A4424AEC/Grizzly-bear-Jasper-National-Park-Canada-Alberta.jpg"  # noqa
resp = requests.post("https://:8000/classify", json={"image_url": bear_url})

print(resp.text)
# 'brown bear, bruin, Ursus arctos'
text = "Hello, the weather is quite fine today!"
resp = requests.post("https://:8000/translate", json={"text": text})

print(resp.text)
# 'Hallo, das Wetter ist heute ziemlich gut!'

使用 serve run 的开发工作流#

您还可以使用 CLI 命令 serve run,在本地或远程集群上轻松运行和测试您的应用。

$ serve run config.yaml
> 2023-04-04 11:00:05,901 INFO scripts.py:327 -- Deploying from config file: "config.yaml".
> 2023-04-04 11:00:07,505 INFO worker.py:1613 -- Started a local Ray instance. View the dashboard at http://127.0.0.1:8265
> 2023-04-04 11:00:09,012 SUCC scripts.py:393 -- Submitted deploy config successfully.

serve run 命令会阻塞终端,允许 Serve 的日志流式传输到控制台。这有助于您轻松测试和调试应用。如果您想更改代码,可以按 Ctrl-C 中断命令并关闭 Serve 及其所有应用,然后重新运行 serve run

注意

serve run 仅支持运行多应用配置文件。如果您想通过直接传递导入路径来运行应用,serve run 一次只能运行一个应用导入路径。

检查状态#

通过运行 serve status 来检查应用的状态。

$ serve status
proxies:
  2e02a03ad64b3f3810b0dd6c3265c8a00ac36c13b2b0937cbf1ef153: HEALTHY
applications:
  app1:
    status: RUNNING
    message: ''
    last_deployed_time_s: 1693267064.0735464
    deployments:
      downloader:
        status: HEALTHY
        replica_states:
          RUNNING: 1
        message: ''
      ImageClassifier:
        status: HEALTHY
        replica_states:
          RUNNING: 1
        message: ''
  app2:
    status: RUNNING
    message: ''
    last_deployed_time_s: 1693267064.0735464
    deployments:
      Translator:
        status: HEALTHY
        replica_states:
          RUNNING: 1
        message: ''

在应用之间发送请求#

您还可以通过使用 Serve API serve.get_app_handle 来获取集群上任何活动 Serve 应用的句柄,从而在应用之间进行调用,而无需通过 HTTP。该句柄可用于在应用上直接执行请求。以上述分类器和翻译器应用为例。您可以修改 ImageClassifier__call__ 方法,以检查 HTTP 请求中的另一个参数,并将请求发送到翻译器应用。

    async def __call__(self, req: starlette.requests.Request):
        req = await req.json()
        result = await self.classify(req["image_url"])

        if req.get("should_translate") is True:
            handle: DeploymentHandle = serve.get_app_handle("app2")
            return await handle.translate.remote(result)

        return result

然后,使用 should_translate 标志设置为 True 的标志向分类器应用发送请求

bear_url = "https://cdn.britannica.com/41/156441-050-A4424AEC/Grizzly-bear-Jasper-National-Park-Canada-Alberta.jpg"  # noqa
resp = requests.post(
    "https://:8000/classify",
    json={"image_url": bear_url, "should_translate": True},
)

print(resp.text)
# 'Braunbär, Bruin, Ursus arctos'

深入检查#

要更深入地了解集群上运行的应用,请访问 Ray Serve Dashboard:https://:8265/#/serve

您可以看到所有已部署到 Ray 集群的应用

applications

每个应用下的部署列表

deployments

以及每个部署的副本列表

replicas

有关 Ray Serve Dashboard 的更多详细信息,请参阅 Serve Dashboard 文档

添加、删除和更新应用#

您可以添加、删除或更新 applications 字段下的条目,以添加、删除或更新集群中的应用。这不会影响集群上的其他应用。要更新应用,请修改 applications 字段下相应条目中的配置选项。

注意

当您重新提交配置时,应用的原地更新行为与单个应用的行为相同。有关应用如何响应不同配置更改的更多信息,请参阅 更新 Serve 应用

从单应用配置迁移#

将单应用配置 ServeApplicationSchema 迁移到多应用配置格式 ServeDeploySchema 非常简单。 applications 字段下的每个条目都与旧的单应用配置格式匹配。要将单应用配置转换为多应用配置格式,请执行以下操作:

  • 将整个旧配置复制到 applications 字段下的一个条目中。

  • 从条目中删除 hostport,并将它们移动到 http_options 字段下。

  • 命名应用。

  • 如果尚未设置,请将应用级别的 route_prefix 设置为应用中入口部署的路由前缀。在多应用配置中,您应该在应用级别设置路由前缀,而不是为每个应用中的入口部署设置。

  • 根据需要添加更多应用。

有关多应用配置格式的更多详细信息,请参阅 ServeDeploySchema 的文档。

注意

您必须从应用条目中删除 hostport。在多应用配置中,在单个应用中指定集群级别的选项是不适用的,也不被支持。