Ray Serve 生产部署依赖管理实战:runtime_env 与按部署依赖隔离
Ray Serve 生产部署依赖管理实战runtime_env 与按部署依赖隔离【免费下载链接】rayRay is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.项目地址: https://gitcode.com/gh_mirrors/ra/ray导读在生产环境中把 Ray Serve 应用真正跑起来绕不开一个基础问题应用的代码和 Python 依赖如何在集群的每一台机器上保持一致、可用。本文基于 Ray Serve 官方生产指南系统讲解两条核心路径——通过runtime_env为整个应用注入远程代码与 pip 依赖以及通过ray_actor_options为单个 Deployment 隔离互不兼容的依赖版本同时给出 driver 进程与 Deployment 依赖不一致时的延迟导入标准写法并深入仓库源码与配置校验逻辑帮助你在集群、Kubernetes 与本地开发三种场景下做出正确的依赖管理决策。为什么生产环境必须显式处理依赖Ray Serve 的部署入口是一个import_path例如text_ml:app它指向应用图Deployment Graph的顶层对象。Serve 的 Controller 和 Replica Actor 在运行时必须能够真正 import 到这个路径在本地开发时import_path通常落在当前工作目录current working directoryServe 进程能直接找到在集群上运行时工作目录并不存在代码文件也不会自动同步到每个节点因此必须显式地把代码送到所有节点上。官方推荐两种做法见 集群配置文档 所在的生产指南把代码构建进集群的容器镜像适合代码稳定、希望避免运行时下载的场景KubeRay 的 RayCluster/RayService 镜像构建即属此类使用带远程 URIremote URI的runtime_env让每个节点在启动应用时从远程存储拉取代码灵活、可热更新也是官方文档强调的最佳实践。从源码看runtime_env最终会落到 Replica Actor 的启动选项上作为 Ray Actor 的运行时环境来解析、下载与缓存因此它天然具备集群一致 按需安装 缓存复用的特性。应用级 runtime_env给整个应用注入代码与依赖一个可直接复制的完整配置示例官方给出的 Text ML Models 示例展示了标准写法用working_dir指向托管在远程的代码压缩包用pip声明 Python 依赖import_path: text_ml:app runtime_env: working_dir: https://github.com/ray-project/serve_config_examples/archive/HEAD.zip pip: - torch - transformers这个配置的作用是部署文本摘要与翻译应用时即使你的本地机器上没有text_ml.py代码集群也会从远程 URL 下载working_dir指向的压缩包并解压到每个节点的沙箱目录按pip列表为每个节点安装torch、transformers然后才启动 Replica Actor此时text_ml:app已可正常导入。远程 URIremote URI的打包与使用规范runtime_env中的working_dir与py_modules字段既可以填本地路径也可以填远程 URI。Ray Core 的 依赖管理文档 对远程 URI 有明确约束生产环境中极易踩坑务必注意远程 URI 必须直接指向一个压缩包.zip、.tar.gz、.tgz、.tar.xz且压缩包顶层只能有一个目录解压后该目录的内容会被直接作为working_dir或py_module使用打包时需在目标目录的父目录中执行例如把example_dir打包cd /some_path zip -r archive.zip example_dir # 或用 tar -czf archive.tar.gz example_dir打包后可用以下命令检查顶层是否只有一个目录zipinfo -1 archive.zip tar -tzf archive.tar.gz # 期望输出示例 # example_dir/ # example_dir/my_file_1.txt # example_dir/subdir/my_file_2.txt归档包中的隐藏文件与元数据目录如.DS_Store、__MACOSX、__pycache__会混入顶层导致解压结构不符合单一顶层目录要求上传前务必检查上传到对象存储后即可通过 URI 引用例如runtime_env: working_dir: s3://example_bucket/example.zip也支持tar.gz、tgz、tar.xz等格式。Serve 配置层面对 URI 的强制校验在 Serve 的生产配置Serve config / RayService 中的applications[].runtime_env中URI 约束比普通 Ray job 更严格。查看 python/ray/serve/schema.py 中的RayActorOptionsSchemaruntime_env字段的校验器runtime_env_contains_remote_uris会解析working_dir与py_modules中的每一个 URI校验失败时抛出明确的错误信息runtime_envs in the Serve config support only remote URIs in working_dir and py_modules, or local:// URIs for directories that already exist on every node。也就是说Serve 配置文件的runtime_env只能使用远程 URI如 HTTP(S)、S3 等或local://URI不能直接引用本地 zip 文件或本地目录——因为配置文件要能被整个集群的节点一致地解析本地路径在每台机器上不一定存在。这一限制在 Serve 官方配置文档的runtime_env字段说明中也有强调并在 python/ray/serve/tests/test_runtime_env.py 等测试中覆盖了相应失败场景。关于 PYTHONPATH 与本地开发的取舍官方文档特别提示你当然可以把整个部署图打包成独立的 Python 包然后用PYTHONPATH指过去实现本地机器的位置无关。但最佳实践仍是使用runtime_env——PYTHONPATH依赖的是运行 Serve 的这台机器的环境变量无法保证集群其他节点一致而runtime_env由 Ray 在每个节点上统一解析、安装与缓存能确保所有机器的运行环境完全一致。使用serve build生成配置后的必做步骤生产环境常通过serve build自动生成 Serve 配置但要注意自动生成的runtime_env字段恒为空字典必须手动补充。官方配置文档 config.md 的示例即展示了这一点——serve build生成的配置中runtime_env: {}如果torch、transformers没有预装在集群环境中你需要手动把这两个 pip 包补进runtime_env否则应用会在启动阶段因依赖缺失而失败。按部署Per-Deployment依赖隔离一个 Deployment 一套环境适用场景与原理Ray Serve 支持让同一个应用内的不同 Deployment 运行互不相同的 Python 依赖哪怕它们互相冲突也没关系。官方给出的典型例子同时服务一个依赖 TensorFlow 1 的旧模型和另一个依赖 TensorFlow 2 的新模型。实现原理与 Ray 的 runtime-environments 机制一致runtime_env本质上是一个Ray Actor 选项Serve 的每个 Replica 都是一个 Ray Actor因此可以在 Deployment 的ray_actor_options中注入各自的runtime_env让不同 Replica 各跑各的环境。前置条件仅支持Mac OS 与 Linux不支持 Windows需先安装ray[default]以确保 Runtime Environments 功能可用pip install ray[default]完整可运行示例同应用内两个 requests 版本共存官方配套代码 doc/source/serve/doc_code/varying_deps.py 给出了完整实现——同一个应用里一个 Deployment 用requests2.25.1另一个用requests2.26.0由一个 Ingress 根据查询参数路由import requests from starlette.requests import Request from ray import serve from ray.serve.handle import DeploymentHandle serve.deployment class Ingress: def __init__( self, ver_25_handle: DeploymentHandle, ver_26_handle: DeploymentHandle ): self.ver_25_handle ver_25_handle self.ver_26_handle ver_26_handle async def __call__(self, request: Request): if request.query_params[version] 25: return await self.ver_25_handle.remote() else: return await self.ver_26_handle.remote() serve.deployment def requests_version(): return requests.__version__ ver_25 requests_version.options( name25, ray_actor_options{runtime_env: {pip: [requests2.25.1]}}, ).bind() ver_26 requests_version.options( name26, ray_actor_options{runtime_env: {pip: [requests2.26.0]}}, ).bind() app Ingress.bind(ver_25, ver_26) serve.run(app) assert requests.get(http://127.0.0.1:8000/?version25).text 2.25.1 assert requests.get(http://127.0.0.1:8000/?version26).text 2.26.0要点拆解requests_version.options(...)返回一个带新配置的 Deployment 副本name区分两个版本ray_actor_options{runtime_env: {...}}为各自注入不同的 pip 依赖serve.run(app)部署后两个 Replica 各自安装自己的requests版本互不干扰通过http://127.0.0.1:8000/?version25与?version26即可验证路由正确且返回各自版本号示例使用DeploymentHandle做 Deployment 间组合调用——Ingress 不直接访问requests库而是通过 handle 转发这正是避免 driver 端依赖冲突的关键。源码视角ray_actor_options 如何贯穿配置层python/ray/serve/deployment.py 中Deployment.ray_actor_options属性直接暴露 Replica 的 Actor 选项而options()方法第 222 行起接受ray_actor_options参数并深拷贝生成新的 Deployment 配置bind()方法再把它封装成可部署的 Application——这就是requests_version.options(...).bind()链式调用的底层机制python/ray/serve/schema.py 中的RayActorOptionsSchema为runtime_env提供了 Pydantic 校验仅允许远程 URI 或local://URI并同时定义了num_cpus、num_gpus、memory、resources、accelerator_type、label_selector等选项——也就是说ray_actor_options不止承载runtime_env还能精细化控制每个 Deployment 的算力资源与调度约束配置层面同样支持 per-deployment 覆盖在 Serve 配置文件的applications[].deployments[]条目中配置ray_actor_options即可在不改动代码的前提下为某个 Deployment 单独指定runtime_env。注意事项避免 from source 安装耗尽集群资源官方给出了一条重要生产经验尽量避免在runtime_env中动态安装需要从源码编译的包如某些不带 wheel 的 C/C 扩展。这类安装耗时很长且编译过程会吃满节点 CPU/内存可能拖垮整个 Ray 集群。建议将这类包预先编译好放入私有 PyPI 仓库或直接构建进 Docker 镜像让runtime_env的pip列表只保留能快速从 wheel 安装的依赖。driver 与 Deployment 依赖不一致延迟导入Delayed Import模式问题根源实际部署中经常出现这种错位Deployment 所需的依赖driver 程序运行serve.run、发起 Serve API 调用的进程并没有安装。比如模型服务用到了torch而提交任务的 driver 机器只有轻量环境。如果在模块顶层就import torchdriver 进程在加载应用代码时会直接 ImportError 崩溃。这个问题即使不使用 runtime_env 也同样存在因此需要一种与运行时环境无关的稳健写法。标准写法把导入推迟到__call__内官方配套代码 doc/source/serve/doc_code/delayed_import.py 展示了正确姿势——将重依赖的导入放在 Deployment 的方法内部from ray import serve serve.deployment class MyDeployment: def __call__(self, model_path): from my_module import my_model self.model my_model.load(model_path)要点解释from my_module import my_model被放在__call__内部只有在请求真正到达、该 Deployment 的 Replica 已就绪时才执行导入由于每个 Replica 运行在自己的runtime_env中安装了my_module及其依赖此时导入必然成功driver 进程只需要能导入MyDeployment类本身的定义即from ray import serve等轻量依赖不必安装my_module。这一模式尤其适合以下场景模型权重按需加载、大体积推理库只装在生产 Replica 上、driver 机器保持精简。它与按部署依赖隔离配合使用可以做到driver 零负担、每个 Replica 各取所需。总结与延伸阅读生产环境的依赖管理可以概括为三层策略层次手段适用场景应用级applications[].runtime_env远程 URI pip整个应用共享一套代码与依赖部署到多节点集群部署级ray_actor_options.runtime_env同一应用内不同 Deployment 需要互不兼容的依赖版本代码级延迟导入__call__内 importdriver 与 Replica 依赖不一致保持 driver 轻量化配套的仓库证据与延伸阅读doc/source/serve/doc_code/varying_deps.py按部署依赖隔离的完整可运行示例doc/source/serve/doc_code/delayed_import.py延迟导入标准写法python/ray/serve/schema.pyRayActorOptionsSchema对runtime_env远程 URI 的强制校验实现python/ray/serve/deployment.pyDeployment.options()与ray_actor_options的底层实现doc/source/serve/production-guide/config.mdServe 配置文件规范含runtime_env字段约束与serve build说明doc/source/ray-core/handling-dependencies.rstRay Core 依赖管理总览含 Remote URIs 打包细则与 runtime_env 缓存/回收机制python/ray/serve/tests/test_runtime_env.pyruntime_env 相关测试可验证 working_dir 与失败条件。在集群上部署时优先把代码构建进容器镜像KubeRay 集群配置或使用带远程 URI 的runtime_env在单个应用内部需要版本隔离时用ray_actor_options为每个 Deployment 指定独立环境最后别忘了用延迟导入守住 driver 进程的轻量边界——三者组合即可覆盖从本地开发到生产集群的完整依赖管理链路。【免费下载链接】rayRay is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.项目地址: https://gitcode.com/gh_mirrors/ra/ray创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考