ZenML 集成 BentoML Model Deployer 实战:从 Bento 构建到本地 HTTP 与容器化模型部署
【免费下载链接】zenmlZenML 🙏: One AI Platform from Pipelines to Agents. https://zenml.io.项目地址: https://gitcode.com/GitHub_Trending/ze/zenml
本文以 ZenML 官方文档《BentoML》为骨架,系统讲解如何借助 ZenML 的 BentoML 集成,将训练完成的模型打包为 Bento 制品,并通过bentoml_model_deployer_step以本地 HTTP 服务器或容器化服务两种方式部署上线,同时结合仓库源码剖析底层实现,帮助你在实际项目中快速落地一套标准化、可持续演进的模型服务方案。
BentoML 与 ZenML 的集成定位
BentoML 是一个开源的机器学习模型服务框架,可用于在本地环境、云环境或 Kubernetes 环境中部署模型。在 ZenML 中,BentoML Model Deployer 是 Model Deployer 栈组件众多 flavor(风味实现)之一,随 BentoML 集成一起提供。它能够在本地运行的 HTTP 服务器上部署并管理 BentoML 模型或 Bento 制品。
在 ZenML 的模型部署体系中,BentoML 集成支持两条部署路径:
- 本地 HTTP 服务器:将 Bento 作为守护进程在本地持续运行,适合本地开发与快速测试;
- 容器化服务:将 Bento 构建为 Docker 镜像并推送到 ZenML Stack 的容器注册中心,再以容器方式在本地运行,镜像可以进一步被带到任何远程环境执行。
⚠️ 需要注意:在 BentoML 生态中,
Yatai与bentoctl才是负责把 Bento 部署到 Kubernetes 集群和云平台的工具。其中bentoctl目前已废弃,可能无法兼容最新的 BentoML 版本。而 ZenML 的 BentoML 集成负责在本地开发与生产场景下完成前两条路径的部署。
从源码结构看,该集成位于src/zenml/integrations/bentoml/,包含以下核心模块:
flavors/bentoml_model_deployer_flavor.py:定义BentoMLModelDeployerConfig与 flavor 注册信息;model_deployers/bentoml_model_deployer.py:BentoMLModelDeployer栈组件实现,负责服务的创建、启动、停止与删除;services/bentoml_local_deployment.py、services/bentoml_container_deployment.py:两种部署模式的服务类;services/deployment_type.py:定义BentoMLDeploymentType枚举(local/container);steps/bento_builder.py、steps/bentoml_deployer.py:内置的 Bento 构建步骤与部署步骤;constants.py:DEFAULT_BENTO_FILENAME、默认端口、健康检查路径等常量。
何时使用 BentoML Model Deployer
官方文档建议在以下场景选择 BentoML Model Deployer:
- 希望在组织内部标准化模型的部署方式,让所有模型以一致的方式进入生产环境;
- 希望以简单的方式完成模型部署,同时保留在合适的时机将模型平滑升级为生产级解决方案的能力;
- 如果期望用 Kubernetes 等方案部署模型,则可以参考 ZenML 提供的其他 Model Deployer Flavors,例如 Seldon Core、MLflow 等。
此外,BentoML 也支持更复杂的生产级部署形态:将 ZenML 管道中构建好的 Bento,通过bentoctl部署到 AWS Lambda、AWS SageMaker、Google Cloud Functions、Google Cloud AI Platform、Azure Functions 等云环境。需要说明的是,官方文档同时标注:bentoctl集成实现仍在推进中,且该 CLI 目前已废弃,可能无法兼容最新 BentoML 版本,请以实际使用的 BentoML 版本为准。
📌 补充提示:ZenML 官方文档(Model Deployers 总览)已给出弃用说明——Model Deployer 栈组件正逐步被更灵活的 Deployer 组件与 Pipeline Deployments 范式取代。但 Model Deployer 集成仍保持向后兼容可用,BentoML 这一实现适合作为理解模型服务化与学习 ZenML 部署抽象的入口。
安装与注册:快速启用 BentoML 集成
安装集成包
要在本地机器上部署模型,首先需要安装 BentoML 集成所需的 Python 包:
zenml integration install bentoml -y该命令会安装bentoml及相关依赖,并在 ZenML 中注册 BentoML 集成提供的 flavor、步骤与服务实现。
注册 Model Deployer 栈组件
安装完成后,通过以下命令注册 BentoML model deployer:
zenml model-deployer register bentoml_deployer --flavor=bentoml注册完成后,ZenML 集成会以守护进程方式在后台启动一个本地 HTTP 部署服务器,持续为最新的模型和 Bento 提供服务。
组件配置项:service_path
从 flavors/bentoml_model_deployer_flavor.py 源码可以看到,BentoMLModelDeployerConfig暴露了一个可选配置字段:
service_path(默认""):BentoML 服务目录的路径。若不提供,则使用默认服务路径。
在 model_deployers/bentoml_model_deployer.py 中,local_path属性决定了部署守护进程配置的存放位置:如果用户配置了service_path则优先使用;否则按组件 ID 生成本地默认路径(GlobalConfiguration().local_stores_path下的子目录),并自动创建目录。也就是说,不传service_path也能开箱即用,它主要用于自定义部署服务元数据的落盘位置。
在 CLI 注册时如需指定,可写作:
zenml model-deployer register bentoml_deployer --flavor=bentoml --service_path=/your/custom/path注册后,将该组件加入你的 Stack 即可在管道中使用:
zenml stack register local_bentoml -m default -a default -o default -d bentoml_deployer --set使用流程总览
官方文档推荐的标准使用流程分为三步:
- 创建 BentoML Service:定义模型服务的核心逻辑(输入、输出、预处理与推理 API);
- 构建 Bento:既可以使用内置的
bento_builder_step将模型与 Service 打包成 Bento 制品,也可以自行调用bentos.build自定义构建; - 部署 Bento:通过
bentoml_model_deployer_step将 Bento 部署到本地 HTTP 服务器或容器化服务。
下面逐一展开。
第一步:创建 BentoML Service
Service 是 BentoML 中定义模型如何被服务的核心逻辑。以下示例展示了一个用于服务 torch 模型的基础 Bento Service(以 MNIST 手写数字识别为例):
import bentoml from bentoml.validators import DType, Shape from bentoml.io import PILImage import numpy as np import torch from typing import Annotated # Note: SERVICE_NAME and MODEL_NAME would be defined elsewhere # Note: to_numpy() would be a custom function to convert tensors to numpy arrays @bentoml.service( name=SERVICE_NAME, ) class MNISTService: def __init__(self): # load model self.model = bentoml.pytorch.load_model(MODEL_NAME) self.model.eval() @bentoml.api() async def predict_ndarray( self, inp: Annotated[np.ndarray, DType("float32"), Shape((28, 28))] ) -> np.ndarray: inp = np.expand_dims(inp, (0, 1)) output_tensor = await self.model(torch.tensor(inp)) return to_numpy(output_tensor) @bentoml.api() async def predict_image(self, f: PILImage) -> np.ndarray: assert isinstance(f, PILImage) arr = np.array(f) / 255.0 assert arr.shape == (28, 28) arr = np.expand_dims(arr, (0, 1)).astype("float32") output_tensor = await self.model(torch.tensor(arr)) return to_numpy(output_tensor)要点说明:
@bentoml.service(name=...)声明一个 Bento Service,name需要与后续构建 Bento 时使用的 Service 名称一致;__init__中通过bentoml.pytorch.load_model(MODEL_NAME)加载保存在 BentoML 模型仓库中的模型并置为 eval 模式;- 每个
@bentoml.api()装饰的方法都暴露为一个 HTTP 推理 API,例如predict_ndarray、predict_image; - 参数注解用于声明输入输出的类型与校验规则,如
DType("float32")校验数据类型、Shape((28, 28))校验张量形状;PILImage用于接收图像输入。关于 API 输入输出类型与校验器的完整用法,可参考 BentoML 官方 I/O 类型文档。
第二步:构建 Bento 制品
方式一:自行构建 Bento
bento_builder_step只是为了让生活更简单而存在——你完全可以自己构建 Bento,然后在部署步骤中使用它。文档给出的自建思路如下,理解它有助于你对 Bento 构建过程做更高度的定制:
# 1. use the step context to get the output artifact uri context = get_step_context() # 2. you can save the model and bento uri as part of the bento labels labels = labels or {} labels["model_uri"] = model.uri labels["bento_uri"] = os.path.join( context.get_output_artifact_uri(), DEFAULT_BENTO_FILENAME ) # 3. Load the model from the model artifact model = load_artifact_from_response(model) # 4. Save the model to a BentoML model based on the model type try: module = importlib.import_module(f".{model_type}", "bentoml") module.save_model(model_name, model, labels=labels) except importlib.metadata.PackageNotFoundError: bentoml.picklable_model.save_model( model_name, model, ) # 5. Build the BentoML bundle. You can use any of the parameters supported by the bentos.build function. bento = bentos.build( service=service, models=[model_name], version=version, labels=labels, description=description, include=include, exclude=exclude, python=python, docker=docker, build_ctx=working_dir or source_utils.get_source_root(), )这里的model_name应是你保存到 BentoML 时使用的模型名称,通常通过下面两种方式之一保存:
bentoml.MODEL_TYPE.save_model(model_name, model, labels=labels) # or bentoml.picklable_model.save_model( model_name, model, )基于上述思路,一个自定义的 builder step 大致长这样:
from zenml import step @step def my_bento_builder(model) -> bento.Bento: ... # Load the model from the model artifact model = load_artifact_from_response(model) # save to bentoml bentoml.pytorch.save_model(model_name, model) # Build the BentoML bundle. You can use any of the parameters supported by the bentos.build function. bento = bentos.build( ... ) return bento构建完成后,你就可以按需使用这个 Bento 制品了。
方式二:使用内置的bento_builder_step
一旦定义好 Bento Service,就可以在 ZenML 管道中调用内置的bento_builder_step来构建 Bento 制品。请确保 Bento Service 文件位于仓库根目录层级,并在service参数中填入正确的类名。
from zenml import pipeline, step from zenml.integrations.bentoml.steps import bento_builder_step @pipeline def bento_builder_pipeline(): model = ... bento = bento_builder_step( model=model, model_name="pytorch_mnist", # Name of the model model_type="pytorch", # Type of the model (pytorch, tensorflow, sklearn, xgboost..) service="service.py:CLASS_NAME", # Path to the service file within zenml repo labels={ # Labels to be added to the bento bundle "framework": "pytorch", "dataset": "mnist", "zenml_version": "0.21.1", }, exclude=["data"], # Exclude files from the bento bundle python={ "packages": ["zenml", "torch", "torchvision"], }, # Python package requirements of the model )参数详解(依据 steps/bento_builder.py 源码签名):
| 参数 | 类型 | 说明 |
|---|---|---|
model | UnmaterializedArtifact | 上一个步骤产出的已训练/已加载模型制品(必填) |
model_name | str | 保存到 BentoML 时使用的模型名称(必填) |
model_type | str | 模型类型,如pytorch、tensorflow、sklearn、xgboost等(必填),用于选择bentoml.<model_type>.save_model对应的保存器 |
service | str | BentoML Service 文件在仓库内的路径及类名,格式path/to/service.py:ClassName(必填) |
version | Optional[str] | 模型/Bento 的版本号,不传则由 BentoML 自动生成 |
labels | Optional[Dict[str, str]] | 附加到 Bento 制品的标签,可用于元数据追踪 |
description | Optional[str] | Bento 的描述信息 |
include | Optional[List[str]] | 需要打进 Bento 包的文件列表 |
exclude | Optional[List[str]] | 需要从 Bento 包排除的文件列表 |
python | Optional[Dict[str, Any]] | Bento 的 Python 依赖配置(如{"packages": [...]}) |
docker | Optional[Dict[str, Any]] | Bento 容器镜像的配置 |
working_dir | Optional[str] | Bento 构建的工作目录,默认取source_utils.get_source_root() |
源码级实现细节(src/zenml/integrations/bentoml/steps/bento_builder.py):
- 步骤首先通过
get_step_context()获取输出制品 URI,并把model_uri与bento_uri写入 labels 中——部署步骤正是从 Bento 的info.labels里读取这两个 URI 来做追溯的; - 通过
load_artifact_from_response(model)从 ZenML 制品仓库中加载模型实例; - 保存模型时先尝试
importlib.import_module(f".{model_type}", "bentoml")动态导入对应的 BentoML 模块(如bentoml.pytorch),若因对应框架包未安装抛出PackageNotFoundError,则回退到bentoml.picklable_model.save_model使用可序列化方式保存; - 最后调用
bentos.build(...)生成 Bento 制品并作为步骤输出返回,其build_ctx默认使用source_utils.get_source_root()。
Bento Builder 步骤可以在你用 ZenML 创建的任何编排管道中使用。该步骤会构建 Bento 制品并保存到当前使用的 Artifact Store,之后既可以用 BentoML Model Deployer 步骤在本地或容器化环境中服务模型,也可以在远程环境中配合bentoctl或 Yatai 使用——这让模型打包方式对不同的部署场景都保持就绪。
第三步:使用bentoml_model_deployer_step部署
构建好 Bento 制品后,就可以使用内置的bentoml_model_deployer_step把它部署到本地 HTTP 服务器或本地机器上的容器化服务。
📌 提示:
bentoml_model_deployer_step只能运行在本地环境。但使用容器化部署时,该步骤创建的 Docker 镜像会自动推送到 ZenML Stack 的容器注册中心,你可以把镜像带到远程环境部署模型。
本地部署(Local Deployment)
以下示例把 Bento 部署到本地 HTTP 服务器:
from zenml import pipeline, step from zenml.integrations.bentoml.steps import bentoml_model_deployer_step @pipeline def bento_deployer_pipeline(): bento = ... deployed_model = bentoml_model_deployer_step( bento=bento model_name="pytorch_mnist", # Name of the model port=3001, # Port to be used by the http server )容器化部署(Containerized Deployment)
以下示例把 Bento 部署到本地机器上运行的容器化服务。请确保本地已安装dockerCLI,以便构建镜像并运行容器。
你可以为镜像指定名称和标签,镜像会被构建并推送到 ZenML Stack 的容器注册中心。默认情况下使用 Bento 的 tag 作为镜像名;如果你提供自定义镜像名,务必在镜像名前加上正确的注册中心名称作为前缀,否则镜像推送会失败。
from zenml import pipeline, step from zenml.integrations.bentoml.steps import bentoml_model_deployer_step @pipeline def bento_deployer_pipeline(): bento = ... deployed_model = bentoml_model_deployer_step( bento=bento model_name="pytorch_mnist", # Name of the model port=3001, # Port to be used by the http server deployment_type="container", image="my-custom-image", image_tag="my-custom-image-tag", platform="linux/amd64", )该步骤会:
- 为 Bento 构建 Docker 镜像并推送到容器注册中心;
- 在本地运行该 Docker 镜像,使其可被调用进行推理。
部署后可以用以下命令查看本机镜像与运行中的容器:
docker images docker ps由于镜像同时被推送到 ZenML Stack 的容器注册中心,你可以把它运行在任何环境中,例如:
docker run -it --rm -p 3000:3000 image:image-tag serve部署步骤参数详解
根据 steps/bentoml_deployer.py 的源码签名,bentoml_model_deployer_step支持以下参数:
| 参数 | 类型 / 默认值 | 说明 |
|---|---|---|
bento | bento.Bento | 要部署的 Bento 制品(必填) |
model_name | str | 部署的模型名称(必填) |
port | int | 预测服务使用的端口(必填) |
deployment_type | BentoMLDeploymentType.LOCAL | 部署类型:local或container(对应 services/deployment_type.py 中的枚举) |
deploy_decision | bool = True | 是否执行部署;若为False且有已存在的等价服务,则复用上一次部署的服务(可用于质量门控,只有模型达标才部署新版本) |
workers | Optional[int] = 1 | 预测服务使用的 worker 进程数 |
backlog | Optional[int] = 2048 | 拒绝请求前允许排队等待的请求数 |
production | bool = False | 是否以生产模式运行服务 |
working_dir | Optional[str] = None | 预测服务的工作目录 |
host | Optional[str] = None | 服务监听的主机地址 |
image | Optional[str] = None | 容器部署时的镜像名 |
image_tag | Optional[str] = None | 容器部署时的镜像标签 |
platform | Optional[str] = None | 构建镜像的目标平台(如linux/amd64) |
ssl_certfile/ssl_keyfile/ssl_keyfile_password/ssl_version/ssl_cert_reqs/ssl_ca_certs/ssl_ciphers | Optional[...] = None | 本地部署启用 HTTPS 时的 SSL 参数(对应 services/bentoml_local_deployment.py 的SSLBentoMLParametersConfig) |
timeout | int = 30 | 等待服务启动/停止的超时秒数 |
部署步骤的底层行为
结合 model_deployers/bentoml_model_deployer.py 源码可以看清部署步骤的执行逻辑:
- 通过
BentoMLModelDeployer.get_active_model_deployer()获取当前激活的 model deployer 栈组件,并从get_step_context()取得管道名与步骤名; - 从 Bento 的
info.labels中读取model_uri、bento_uri,通过bentoml.load解析出 Service 暴露的所有 API 路径(service_apis),组装成部署配置; - 根据
deployment_type选择BentoMLLocalDeploymentConfig(含 SSL 与 production 参数)或BentoMLContainerDeploymentConfig(含 image、image_tag、platform 参数); - 调用
find_model_server查找同管道名、同步骤名、同模型名的既有服务;若deploy_decision为False且存在既有服务,则直接复用并返回(保证服务不中断); - 否则以
replace=True调用deploy_model创建新服务并替换旧服务——两个部署服务被视为"等价"的条件是拥有相同的pipeline_name、pipeline_step_name与model_name,即它们服务于由同一管道步骤部署的同一模型的多个版本;若找到多个等价服务,随机选一个更新,其余删除。
在本地模式下,服务以守护进程运行(BentoMLLocalDeploymentService),启动时会依据 BentoML 版本分支调用serve_http_production(BentoML < 1.2)或_bentoml_impl.server.serve_http(BentoML >= 1.2),并内置健康检查路径readyz(见 constants.py),默认端口为3000。在容器模式下,服务会调用bentoml.container.build构建镜像,没有容器注册中心时会以 Bento tag 直接命名镜像并给出警告。
组合管道:训练、构建、部署一体化
定义好所有步骤后,就可以创建 ZenML 管道并运行了。注意两个前置条件:
bento_builder_step期望接收训练好的模型作为输入,因此需要有一个前置步骤训练模型并输出,或从之前的运行中加载模型;bentoml_model_deployer_step期望接收 Bento 制品作为输入,因此需要有一个前置步骤构建 Bento,或从之前的运行/外部来源加载 Bento。
端到端一体化管道
下面的例子创建了一条训练模型、构建 Bento、为它创建并运行 Docker 镜像并推送到容器注册中心的管道;之后你可以另建一条管道拉取该镜像并部署到远程环境:
# Import the pipeline to use the pipeline decorator from zenml.pipelines import pipeline # Pipeline definition @pipeline def bentoml_pipeline( importer, trainer, evaluator, deployment_trigger, bento_builder, deployer, ): """Link all the steps and artifacts together""" train_dataloader, test_dataloader = importer() model = trainer(train_dataloader) accuracy = evaluator(test_dataloader=test_dataloader, model=model) decision = deployment_trigger(accuracy=accuracy) bento = bento_builder(model=model) deployer(deploy_decision=decision, bento=bento, deployment_type="container")这里deployment_trigger是一个质量门控步骤:只有当模型准确率等指标达标时才产出deploy_decision=True,避免把劣化模型部署上线。
远程训练 + 本地/云端部署分离
在更复杂的场景中,你可能希望在远程环境训练模型并构建 Bento,然后由另一条管道拉取 Bento 制品部署到本地 HTTP 服务器或云平台。下面两个管道分别完成这两件事:
# Import the pipeline to use the pipeline decorator from zenml.pipelines import pipeline # Pipeline definition @pipeline def remote_train_pipeline( importer, trainer, evaluator, bento_builder, ): """Link all the steps and artifacts together""" train_dataloader, test_dataloader = importer() model = trainer(train_dataloader) accuracy = evaluator(test_dataloader=test_dataloader, model=model) bento = bento_builder(model=model) @pipeline def local_deploy_pipeline( bento_loader, deployer, ): """Link all the steps and artifacts together""" bento = bento_loader() deployer(deploy_decision=decision, bento=bento)与本地已部署模型交互:发起预测
模型部署完成后,ZenML 会自动为你创建 BentoML 客户端。你只需调用服务对象的predict方法,传入输入数据与 API 函数名即可向已部署模型发送请求:
@step def predictor( inference_data: Dict[str, List], service: BentoMLDeploymentService, ) -> None: """Run an inference request against the BentoML prediction service. Args: service: The BentoML service. data: The data to predict. """ service.start(timeout=10) # should be a NOP if already started for img, data in inference_data.items(): prediction = service.predict("predict_ndarray", np.array(data)) result = to_labels(prediction[0]) rich_print(f"Prediction for {img} is {result}")从 services/bentoml_local_deployment.py 源码看,predict(api_endpoint, data, sync=True)会:
- 首先检查服务是否在运行,未运行则抛出异常;
- 根据
sync参数选择SyncHTTPClient或AsyncHTTPClient,指向服务的prediction_url; - 调用
client.call(api_endpoint, data)完成推理请求。
容器化服务的predict则使用bentoml.client.Client.from_url建立连接(见 services/bentoml_container_deployment.py)。此外,服务还暴露prediction_url与prediction_apis_urls属性,分别给出 HTTP 服务地址与各个推理 API 的完整 URL 列表。
本地部署与测试是起步和验证模型的好方式。但真实业务场景往往需要把模型部署到远程环境:你可以在部署步骤中将deployment_type设置为container以生成容器镜像,然后在远程环境中使用该镜像;也可以借助bentoctl或 Yatai 把 Bento 部署到云环境。
从本地走向云端:bentoctl
⚠️ 警告:
bentoctlCLI 目前已废弃,可能无法兼容最新的 BentoML 版本,使用前请确认你所依赖的 BentoML 版本。
bentoctl帮助把任意机器学习模型作为生产级 API 端点部署到云端,它是一个命令行工具,提供了管理 BentoML 制品包的简单接口。bentoctlCLI 提供了一系列operators(与云服务交互的插件),包括:
- AWS Lambda
- AWS SageMaker
- AWS EC2
- Google Cloud Run
- Google Compute Engine
- Azure Container Instances
- Heroku
它的典型用法是:从 ZenML 管道中取出已构建的 Bento,再用bentoctl将其部署到上述云环境之一。关于bentoctl工具的更多信息,可查看其官方 GitHub 仓库;关于 BentoML Model Deployer 全部可配置属性的完整说明,可查阅 ZenML SDK Docs 中的zenml.integrations.bentoml模块文档。
总结
ZenML 的 BentoML 集成把「模型训练 → 制品打包 → 本地/容器部署 → 推理调用」串成一条标准化的流水线:通过zenml integration install bentoml与zenml model-deployer register完成环境准备;用自定义 Service 或内置bento_builder_step把模型与推理逻辑封装为 Bento;再用bentoml_model_deployer_step一键完成本地 HTTP 服务或容器化部署,配合deploy_decision、workers、production、SSL 等参数满足从开发到生产的多种场景需求。源码层面,部署步骤的"等价服务查找与替换"机制保证了持续部署工作流不会为每个模型版本都新起一个服务,从而实现模型版本的无缝滚动更新。想要继续深入,可以阅读仓库中以下关键文件:
- 部署步骤实现:steps/bentoml_deployer.py
- Bento 构建步骤实现:steps/bento_builder.py
- 栈组件实现:model_deployers/bentoml_model_deployer.py
- 本地服务实现:services/bentoml_local_deployment.py
- 容器服务实现:services/bentoml_container_deployment.py
- Flavor 定义:flavors/bentoml_model_deployer_flavor.py
- Model Deployer 总览与其余 flavors:docs/book/component-guide/model-deployers/README.md
【免费下载链接】zenmlZenML 🙏: One AI Platform from Pipelines to Agents. https://zenml.io.项目地址: https://gitcode.com/GitHub_Trending/ze/zenml
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考