
人工智能分布式训练强化学习任务调度模型推理服务【免费下载链接】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 集群上运行多个 Ray Serve 应用时不同应用往往依赖完全不同的运行环境——例如一个应用需要 GPU 与专用推理库另一个应用只需要 CPU 与图像处理库。如果强行把所有依赖塞进同一个环境不仅镜像臃肿还容易产生依赖冲突。本文基于 Ray 官方文档 Run Multiple Applications in Different Containers 与当前仓库源码系统讲解如何通过基于 Podman 的image_uri运行时环境特性在同一集群中用不同的容器镜像隔离运行多个 Serve 应用。读完本文你将掌握 Podman 的安装配置、应用镜像的构建与推送、Serve 配置文件的编写以及容器化部署中的环境变量继承规则与常见故障排查方法。注意该特性目前为实验性experimental功能API 可能在未来版本中发生变化。若你有额外的功能需求或遇到问题可在 Ray 的 GitHub 仓库提交 issue。特性概览同一集群、不同镜像、相互隔离传统方式下同一集群内所有 Ray worker 进程共享同一套 Python 环境和依赖。而image_uri运行时环境允许你为每个应用application指定一个独立的容器镜像所有该应用下的 deployment replica副本都会在对应镜像的容器中启动和运行。这意味着每个应用的依赖被封装在各自的镜像里互不干扰不同应用可以使用不同的 Python 包版本、系统库甚至是不同的 Ray 镜像如 CPU 版与 GPU 版应用间通过 Serve 的 HTTP 路由route_prefix对外提供服务调用方无需感知底层的容器隔离。该能力由 Ray 内部的ImageURIPlugin实现其定义位于 image_uri.pyclass ImageURIPlugin(RuntimeEnvPlugin): Starts worker in a container of a custom image. name image_uri staticmethod def get_compatible_keys(): return {image_uri, config, env_vars}从源码可以看出image_uri当前只允许与config和env_vars两个运行时环境字段组合使用这一点会在后文与其他运行时环境字段的兼容性一节详述。第一步安装 Podmanimage_uri特性使用 Podman 来启动和管理容器因此你需要在集群中所有 head 节点和 worker 节点上安装 Podman。请参考 Podman 官方安装文档进行安装。对于 Ubuntu 用户Podman 软件包自 Ubuntu 20.10 起进入官方软件源可直接通过 apt 安装sudo apt-get update sudo apt-get install podman -y安装完成后可以用podman --version验证安装是否成功。关键约束Ray 与 Python 版本必须与宿主机精确匹配在构建应用镜像之前必须先明确一条硬性约束:::warning 容器内的Ray 版本和 Python 版本必须与宿主机环境完全一致。其中 Python 版本需要精确到补丁号patch number例如宿主机是 Python 3.11.x容器内的 Python 版本也必须完全一致。 :::这是因为image_uri的容器化方案并非让容器独立运行完整服务而是让容器内的 Ray Python worker 进程与宿主机的 raylet 协同工作下文底层原理一节会展开说明。版本不一致会导致 RPC 协议、Cython 扩展或共享库不匹配进而引发难以排查的运行时错误。因此构建镜像时务必以宿主机实际运行环境为基准选择 Ray 官方镜像标签。实战两个模型应用分别跑在不同容器中下面以官方文档的经典示例展开在同一个集群上分别部署Whisper 语音转录模型GPU 镜像和Resnet50 图像分类模型CPU 镜像。1. 编写两个应用的 Dockerfile将以下内容分别保存为whisper.Dockerfile和resnet.Dockerfile。whisper.Dockerfile—— 使用 Ray GPU 镜像rayproject/ray:2.56.1-py311-gpu以便 Whisper 模型可以在 GPU 上运行# Use a Ray GPU image, rayproject/ray:2.56.1-py311-gpu, so the Whisper model can run on GPUs. FROM rayproject/ray:2.56.1-py311-gpu # Install the package faster_whisper, which is a dependency for the Whisper model. RUN pip install faster_whisper0.10.0 RUN sudo apt-get update sudo apt-get install curl -y # Download the source code for the Whisper application into whisper_example.py. RUN curl -O https://raw.githubusercontent.com/ray-project/ray/master/doc/source/serve/doc_code/whisper_example.py # Add /home/ray path to PYTHONPATH avoid import module error ENV PYTHONPATH ${PYTHONPATH}:/home/rayresnet.Dockerfile—— 使用 Ray CPU 镜像rayproject/ray:2.56.1-py311-cpu# Use a Ray CPU image, rayproject/ray:2.56.1-py311-cpu. FROM rayproject/ray:2.56.1-py311-cpu # Install the packages torch and torchvision, which are dependencies for the ResNet model. RUN pip install torch2.0.1 torchvision0.15.2 RUN sudo apt-get update sudo apt-get install curl -y # Download the source code for the ResNet application into resnet50_example.py. RUN curl -O https://raw.githubusercontent.com/ray-project/ray/master/doc/source/serve/doc_code/resnet50_example.py # Add /home/ray path to PYTHONPATH avoid import module error ENV PYTHONPATH ${PYTHONPATH}:/home/ray两点说明Dockerfile 中通过curl将应用源码直接下载进镜像最终保存在/home/ray目录下PYTHONPATH追加/home/ray是为了避免导入模块时报错。实际项目中你也可以将源码通过 COPY 指令复制进镜像。这两个示例应用的真实源码就存放在当前仓库中Whisper 应用见 whisper_example.py其中WhisperModel部署声明了ray_actor_options{num_cpus: 1.0, num_gpus: 1}并在初始化时加载faster_whisper模型devicecuda, compute_typefloat16Resnet50 应用见 resnet50_example.py使用torchvision.models.resnet50进行 CPU 推理。仓库中的这两个文件与 Dockerfile 里下载的是同一份源码可直接对照阅读。2. 构建镜像并推送到镜像仓库接下来用 Podman 构建这两个镜像并推送到你选择的容器镜像仓库。下面以alice/whisper_image:latest和alice/resnet_image:latest作为占位镜像名请将alice替换为你自己的仓库名。Whisper 镜像# Build the image from the Dockerfile using Podman export IMG1alice/whisper_image:latest podman build -t $IMG1 -f whisper.Dockerfile . # Push to a registry. This step is unnecessary if you are deploying Serve locally. podman push $IMG1Resnet 镜像# Build the image from the Dockerfile using Podman export IMG2alice/resnet_image:latest podman build -t $IMG2 -f resnet.Dockerfile . # Push to a registry. This step is unnecessary if you are deploying Serve locally. podman push $IMG2注意如果你只是在本地单机部署 Serve未开启集群可以跳过podman push步骤集群部署时则必须将镜像推送到所有节点都能访问的 registry。3. 编写 Serve 配置并指定镜像最后在应用的运行时环境runtime environment规范中通过image_uri字段为每个应用指定其容器镜像:::note 在旧版本中该能力是通过运行时环境的container字段访问的。该 API 现已废弃deprecated推荐改用image_uri。 :::下面的 Serve 配置将whisper应用运行在镜像IMG1中将resnet应用运行在镜像IMG2中。可以使用podman images命令查看本机已有的镜像名。配置生效后两个应用下的所有 deployment replica 都会在各自镜像的容器中启动运行applications: - name: whisper import_path: whisper_example:entrypoint route_prefix: /whisper runtime_env: image_uri: {IMG1} - name: resnet import_path: resnet50_example:app route_prefix: /resnet runtime_env: image_uri: {IMG2}配置要点import_path指向镜像内/home/ray下的应用入口Whisper 应用的绑定对象是whisper_example.py中的entrypoint即WhisperModel.bind()Resnet 应用的绑定对象是resnet50_example.py中的app即Model.bind()与 Dockerfile 中的下载内容一一对应route_prefix定义了对外 HTTP 路由示例中分别为/whisper和/resnet请求会根据路径被分发到对应容器中的副本将{IMG1}、{IMG2}替换为实际的镜像名如alice/whisper_image:latest后即可通过 Serve 的标准部署流程如serve deploy加载该配置。4. 发送请求验证配置部署完成后两个应用分别监听各自的路由。下面的 Python 代码演示了如何调用 Whisper 应用完成音频转录、调用 Resnet 应用完成图片分类 import requests audio_file https://storage.googleapis.com/public-lyrebird-test/test_audio_22s.wav resp requests.post(http://localhost:8000/whisper, json{filepath: audio_file}) # doctest: SKIP resp.json() # doctest: SKIP { language: en, language_probability: 1, duration: 21.775, transcript_text: Well, think about the time of our ancestors. A ping, a ding, a rustling in the bushes is like, whoo, that means an immediate response. Oh my gosh, whats that thing? Oh my gosh, I have to do it right now. And dude, its not a tiger, right? Like, but our, our body treats stress as if its life-threatening because to quote Robert Sapolsky or butcher his quote, hes a Robert Sapolsky is like one of the most incredible stress physiologists of, whisper_alignments: [ [ 0.0, 0.36, Well,, 0.3125 ], ... ] } link_to_image https://serve-resnet-benchmark-data.s3.us-west-1.amazonaws.com/000000000019.jpeg resp requests.post(http://localhost:8000/resnet, json{uri: link_to_image}) # doctest: SKIP resp.text # doctest: SKIP oxWhisper 接口返回语音的语言、时长、转录文本及单词级对齐信息whisper_alignmentsResnet 接口返回预测的 ImageNet 类别示例图片的分类结果为ox公牛。这也与仓库中 resnet50_example.py 的__main__自测逻辑一致——它会对同一张示例图发起请求并断言resp.text ox。底层原理image_uri 是如何把 worker 放进容器的理解底层实现有助于排查问题。ImageURIPlugin的工作流程分为两个阶段全部实现在 image_uri.py阶段一create()—— 拉取镜像并探测 worker 入口路径插件会用 Podman 临时运行一次镜像执行一段内嵌 Python 脚本把镜像内ray._private.workers.default_worker模块的绝对路径写入共享卷文件从而自动探测出镜像内 Ray 的default_worker.py位置早期版本需要用户手动指定container.worker_path现在已自动检测cmd [ podman, run, --rm, -v, f{tmpdir}:/shared:Z, image_uri, python, -c, get_worker_path_script, ]阶段二modify_context()—— 拼接容器启动命令并覆盖 worker 入口插件构建出如下形式的 Podman 命令作为容器内 Python worker 的启动前缀podman run -v /tmp/ray:/tmp/ray \ --cgroup-managercgroupfs --networkhost --pidhost --ipchost \ --usernskeep-id \ --env RAY_RAYLET_PID23478 --env RAY_JOB_ID$RAY_JOB_ID \ --entrypoint python rayproject/ray:nightly-py39源码中这段命令的构造逻辑见 image_uri.py包含几个值得注意的设计决策-v /tmp/ray:/tmp/ray将宿主机 Ray 临时目录session 目录挂载进容器保证容器内 worker 能与宿主机 raylet 共享状态--networkhost --pidhost --ipchost容器与宿主机共享网络、PID 与 IPC 命名空间这是容器内 worker 能作为集群一员正常注册通信的前提--usernskeep-idrootless 容器默认会把宿主用户映射为容器内root这会导致挂载卷的属主错乱keep-id模式将宿主用户以其自身身份映射进容器从而正确访问挂载的 Ray 临时目录详见源码注释--entrypoint pythonimage_uri容器以 Python 为入口启动配合context.override_worker_entrypoint让 Ray 使用镜像内探测到的default_worker.py通过--env显式注入环境变量细节见下文环境变量继承规则。值得注意这段启动命令中会追加--env RAY_JOB_ID$RAY_JOB_ID因为RAY_JOB_ID在setup()调用时尚未生成只有等到 worker 命令真正执行时才可用因此以 shell 变量展开的形式传入。与其他运行时环境字段的兼容性目前image_uri字段只支持与config和env_vars搭配使用。如果尝试同时使用其他运行时环境字段如pip、conda、working_dir、py_modules等会直接抛出ValueError。这一限制在 runtime_env.py 的校验逻辑中实现if self.get(image_uri): image_uri_plugin_cls get_image_uri_plugin_cls() invalid_keys ( set(runtime_env.keys()) - image_uri_plugin_cls.get_compatible_keys() ) if len(invalid_keys): raise ValueError( The image_uri field currently cannot be used together with other fields of runtime_env. fSpecified fields: {invalid_keys} )从源码可以推断这一限制源于架构设计镜像本身已经封装了全部依赖容器内无法再叠加执行宿主机侧的pip/conda等环境准备逻辑同时image_uri的get_compatible_keys()明确声明只兼容{image_uri, config, env_vars}三个键。如果你确实需要将image_uri与其他特性组合使用建议到 Ray 的 GitHub 仓库提交 feature request。环境变量继承规则与优先级容器内进程的环境变量来自三个来源按从高到低的优先级排列runtime_env[env_vars]中显式指定的环境变量所有以RAY_前缀开头的宿主机环境变量包括两个特殊变量RAY_RAYLET_PID和RAY_JOB_ID会在容器运行时被继承镜像中通过 DockerfileENV指令设置的环境变量。这一规则与 image_uri.py 的实现完全对应插件先遍历宿主机os.environ收集所有RAY_前缀变量再用context.env_vars即env_vars字段覆盖同名变量最后统一以--env参数注入容器。因此若env_vars与RAY_前缀变量或镜像内变量同名env_vars的取值优先。仓库中的测试用例 test_ray_env_vars.py 和 test_with_env_vars.py 专门验证了这些场景此外 test_runtime_env_container.py 汇总了容器化运行时环境的整体测试可作为深入学习的参考。在 Docker 容器中运行 Ray 集群时的特殊要求如果你的 raylet 本身运行在Docker容器内那么该容器需要具备启动新容器的权限。为此启动运行 raylet 的容器时必须加上--privileged标志docker run --privileged ... # 启动运行 raylet 的容器这是因为容器内再启动 Podman 容器涉及命名空间、overlayfs 挂载、用户命名空间等特权操作非特权容器默认会被内核拒绝具体报错见下方故障排查。故障排查容器化部署涉及宿主机与容器两侧环境以下是官方文档整理的常见问题及解决方案1.Permission denied: /tmp/ray/session_.../ports_by_node.json.lock原因Podman 容器内运行的用户与启动 Ray 集群的宿主机用户不一致。/tmp/ray目录以卷挂载方式进入 Podman 容器属主是启动 Ray 的宿主用户而容器以--usernskeep-id启动宿主用户会被映射为容器内的自身因此只有当容器内用户与宿主用户不同才会出现权限问题。例如宿主用户是root而你使用的标准 Ray 镜像默认以rayuid 1000用户启动容器该用户无法访问挂载的/tmp/ray卷。解决确保容器内运行用户与宿主机启动 Ray 的用户一致例如统一使用root或在镜像中调整默认用户。2.ERRO[0000] overlay is not supported over overlayfs: backing file system is unsupported for this graph driver原因通常只会在 Ray 集群本身运行在容器内时出现嵌套容器场景宿主机直装 Podman 一般不会遇到。解决在运行 raylet 的容器内将宿主机的容器存储目录卷挂载进去即在启动 Docker 容器的命令中追加-v /var/lib/containers:/var/lib/containers。3.cannot clone: Operation not permitted; Error: cannot re-exec process原因同样只会在 Ray 集群运行在容器内时出现。该错误说明你没有使用 Podman 启动容器的权限。解决给运行 raylet 的容器追加--privileged以授予特权权限。4. 容器启动非常慢或卡住原因通常是因为 Podman 使用了默认的vfs存储驱动配合大型容器镜像时性能极差。Podman 以 rootless 模式运行时启动流程需要修改容器内文件的权限默认存储驱动执行这一操作非常缓慢。解决将 Podman 配置为使用overlay存储驱动并可能需要配置mount_program指向/usr/bin/fuse-overlayfs或你环境中对应的本地路径。测试与回归保障image_uri容器化能力在仓库中有成体系的测试覆盖主要位于python/ray/tests/runtime_env_container/目录包括test_serve_basic.py验证image_uri新 API 与旧containerAPI 两种写法下Serve 部署在容器中能正常读写文件并返回结果test_serve_telemetry.py容器化 Serve 的遥测链路test_job.py、test_put_get.py容器内的任务执行与对象存取test_shared_memory.py共享内存相关行为test_worker_exit_intended_system_exit_and_user_error.pyworker 正常/异常退出路径。其中 test_serve_basic.py 的用法非常直观通过--use-image-uri-api命令行参数切换新旧两种 API--image指定镜像然后断言容器内读到的文件内容与预期一致是理解image_uri与 Serve 集成方式的最小可运行样例。总结image_uri运行时环境为 Ray Serve 提供了一个集群、多容器、多镜像的应用隔离能力部署层面为每个应用编写 Dockerfile、用 Podman 构建并推送镜像、在 Serve 配置中通过image_uri绑定镜像与路由即可实现不同应用运行在不同镜像中原理层面ImageURIPluginimage_uri.py负责拉取镜像、自动探测 worker 入口并以--networkhost --pidhost --ipchost --usernskeep-id等参数启动容器内 worker使其无缝接入宿主集群约束层面容器内 Ray/Python 版本必须与宿主机精确一致image_uri只能与config、env_vars组合使用集群本身容器化运行时需要--privileged权限排障层面文档与源码给出了权限问题、overlayfs 驱动、rootless 慢启动等典型问题的成因与修复方案。该特性特别适合依赖复杂、镜像差异化明显的多模型推理场景如本文的 Whisper Resnet50 组合。由于目前仍处于实验阶段实际投入生产前请务必关注 API 的演进并充分进行版本一致性与权限配置的验证。赞分享人工智能分布式训练强化学习任务调度模型推理服务【免费下载链接】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 生产部署依赖管理实战runtime_env 与按部署依赖隔离Ray Serve 生产部署依赖管理实战runtime_env 与按部署依赖隔离 导读 在生产环境中把 Ray Serve 应用真正跑起来绕不开一个基础问题人工智能分布式训练强化学习任务调度模型推理服务React Turnstile终极入门从安装到实现验证码的5分钟教程React Turnstile终极入门从安装到实现验证码的5分钟教程 React Turnstile是一个专为React应用设计的Cloudflare Tur如何快速搭建 Hermes Agent新手 10 分钟跑通第一个 AI 代理如何快速搭建 Hermes Agent新手 10 分钟跑通第一个 AI 代理 这篇文章写给第一次接触 Hermes Agent 安装的新手。它解决的问题很具体CLI开发工具包管理器上一篇Czkawka 重复文件清理完整指南离线免费快速找出重复文件十分钟收回几十 GB下一篇老 Mac 升级 macOS用 OpenCore Legacy Patcher 让 2012 年 MacBook 装上新系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考