Prhub

#51208 [ROCm][AMD][Installation] add LMCache kv-connector installation and runtime packages to docker image

原始 PR 作者 hongxiayang 合并时间 2026-08-18 05:58 文件变更 3 提交数 13 评论 36 代码增减 +88 / -2

执行摘要

ROCm 镜像内置 LMCache,发布默认启用并做架构裁剪

PR body 明确说明:CUDA 的 docker/DockerfileINSTALL_KV_CONNECTORS=true 时会安装 LMCache,且发布流水线对每个 vllm/vllm-openai tag 都开启;但 docker/Dockerfile.rocm 从未引用该参数,导致 vllm/vllm-openai-rocm 镜像不带 LMCache,用户必须自行构建安装。本 PR 的目标就是让 ROCm 发布镜像与 CUDA 对齐,开箱即用地提供 LMCache KV connector。

值得精读。该 PR 虽不涉及核心推理代码,但 Docker 多阶段构建的缓存正确性设计非常扎实:用 ARG 拼 stage 名实现条件构建、digest-only COPY 作为缓存键、llvm-objdump --offloading 校验 code object 覆盖、sccache 契约复用。这些模式对任何“把第三方组件塞进镜像”的场景都有直接借鉴价值,建议作为镜像工程化的模板案例。

讨论亮点

review 中最有价值的交锋集中在四处:

  • PYTHONHASHSEED 与 TP>1 哈希键一致性:jamesETsmith 建议在镜像里写死 PYTHONHASHSEED=0,避免多 worker 哈希不一致导致缓存永远 miss;hongxiayang 反驳说任何固定 seed 都行,且不同 seed 能隔离多租户共享 LMCache 后端的 keyspace,不应把 0 烤进镜像,最终只加注释提醒用户固定 seed。

  • 缓存键正确性:AndreasKaratzas 指出 wheel 只通过 bind mount 进入安装层时,LMCACHE_REF 或 arch 变化后 warm builder 可能复用旧 install 层,并引用 vLLM 线上事故 #44795 要求照搬 CUDA 的 wheel.sha256 模式;hongxiayang 进一步把 wheel 内容本身放进缓存键并加 sha256sum -c 二次校验。

  • --no-deps 与依赖一致性:AndreasKaratzas 认为 --no-deps 会留下 knowingly inconsistent 的环境,要求用上游认可的依赖集并跑 uv pip check;hongxiayang 论证去掉 --no-deps 会让 numpy<=2.2.6 降级 vLLM 自身依赖、破坏性更隐蔽,并给出端到端实测(lmcache server 启动、connector 注册、numpy/otel 版本不变)。

  • 架构覆盖:AndreasKaratzas 指出非空默认 LMCACHE_ROCM_ARCH="gfx942;gfx950" 会让“继承镜像 PYTORCH_ROCM_ARCH”的 fallback 永远不生效,且 gfx90a(MI200)没有官方 wheel;最终方案改为发布 wheel 优先 + 源码编译 fallback,缺失架构打日志跳过。

实现拆解

  1. 构建开关与阶段选择docker/Dockerfile.rocm 新增独立的 ARG INSTALL_LMCACHE=false,并将最终阶段拆成 final_lmcache_false / final_lmcache_true 两个平行 stage,通过 FROM final_lmcache_${INSTALL_LMCACHE} AS final 按开关选择。之所以用布尔值而非列表,是因为 BuildKit 的 stage 名拼接要求值直接出现在 FROM 中;若用列表则每次构建都会执行该 stage。

  2. 源码获取与依赖修正:新增 lmcache_source stage,将 LMCache 锁定到 v0.5.3 对应的 commit(140819c9...),回应了 bot 关于可编辑 tag 的供应链风险建议;克隆后校验 HEAD 与引用一致,并移除 cufile-pythonnvtx 两个 NVIDIA-only 依赖,把 numpy<=2.2.6 放宽到 numpy<=2.3.5 以匹配 release 镜像的实际版本。

  3. wheel 构建与产物校验build_lmcache stage 在 BUILD_WITH_HIP=1 CXX=hipcc 下用 setup.py bdist_wheel 编译 HIP 扩展,支持 USE_SCCACHE=1 时启用 /opt/sccache-wrappers;构建后用 llvm-objdump --offloading 校验 c_ops*.so 实际携带的 gfx 架构与目标集合完全一致,防止“名义继承、实际缺架构”的静默降级。

  4. 安装编排与层缓存:发布路径优先下载 LMCache GitHub Releases 的预编译 wheel,--no-index 避免 pip 静默回退到 PyPI 上的 CUDA wheel;当请求架构超出 wheel 覆盖(如 gfx90a)时回退源码编译。wheel 本体不出现在最终层里——安装层只 COPY 一个约 118 B 的 wheel.sha256 作为缓存键,实际 wheel 通过 bind mount 注入,避免 7.8 MB blob 被 whiteout 残留在层里;同时安装被放在 vLLM wheel 之前,使稳定的 connector 层不被源码变动连带失效。

  5. 发布与 CI 配套.buildkite/release-pipeline.yaml 的 ROCm 发布构建显式传入 --build-arg INSTALL_LMCACHE=true.buildkite/scripts/ci-bake-rocm.shlmcache_sourcebuild_lmcache 纳入 CI base 的 stage 清单。验证环节包括镜像内 lmcache server 启动(ZMQ/HTTP 均正常)、connector 注册列表、numpy 版本断言与 uv pip check

文件 模块 状态 重要度
docker/Dockerfile.rocm 镜像构建 modified 5.78
.buildkite/release-pipeline.yaml 发布流水线 modified 2.76
.buildkite/scripts/ci-bake-rocm.sh CI 脚本 modified 2.51

关键符号

lmcache_source build_lmcache final_lmcache_true final_lmcache_false final_lmcache_${INSTALL_LMCACHE}

分析完成后,这里会展示 LLM 生成的相对完整源码片段和详细注释。

评论区精华

PYTHONHASHSEED 与 TP>1 哈希键一致性 性能

jamesETsmith 根据 LMCache 博客建议镜像内设置 PYTHONHASHSEED=0,否则 TP>1 worker 因 hash 随机化导致缓存 key 不一致、性能差。

结论:hongxiayang 认为任何固定 seed 都有效,且不同 seed 的 keyspace 可隔离多租户共享后端,不应把 0 烤进镜像;改为在 Dockerfile 中加注释提示用户固定 seed。 · 已解决

INSTALL_LMCACHE 独立 arg 避免缓存键污染 设计

AndreasKaratzas 指出 umbrella arg INSTALL_KV_CONNECTORS 会泄漏进所有 stage 的 ROCm 缓存键,导致 NIXL/ROCShmem/DeepEP 等无关 stage 的远端缓存被重键切碎。

结论:hongxiayang 改为独立 ARG INSTALL_LMCACHE=false,发布 job 显式传 true,彻底解耦缓存键,同时移除 umbrella 参数。 · 已解决

wheel digest 作为安装层缓存键 正确性

AndreasKaratzas 类比 vLLM 生产事故 #44795,指出 wheel 仅 bind mount 时,LMCACHE_REF 或 arch 变更后 warm builder 可能复用旧 install 层,要求 COPY wheel.sha256 进缓存键。

结论:hongxiayang 实现 digest-only 导出 stage,安装层只 COPY 约 118 B 的 digest,wheel 通过 bind mount 注入,并加 sha256sum -c 二次校验。 · 已解决

--no-deps 与依赖版本冲突(numpy/otel) 正确性

AndreasKaratzas 认为 --no-deps 绕过解析会留下 knowingly inconsistent 环境,要求使用上游认可的依赖集并跑 uv pip check;hongxiayang 论证去掉 --no-deps 会让 numpy<=2.2.6 降级 vLLM 依赖,破坏更隐蔽。

结论:最终保留 wheel 直装 + numpy 版本断言 + uv pip check 兜底,并在 PR body 中列出完整版本对比,建议上游修正 requirements 上限。 · 已解决

架构覆盖:gfx90a 缺失与默认 arch 集 设计

AndreasKaratzas 指出非空默认 LMCACHE_ROCM_ARCH 使继承 PYTORCH_ROCM_ARCH 的 fallback 永远不生效,且 gfx90a 无官方 wheel,全局开启会向部分受支持 GPU 交付不可运行的设备代码。

结论:改为发布 wheel 优先 + 源码编译 fallback;有效架构集与上游 wheel 取交集,缺失 arch 打日志跳过,无支持的 arch 集时构建失败。gfx90a 被明确排除并文档化。 · 已解决

wheel 7.8 MB 进入镜像层的白化问题 性能

AndreasKaratzas 指出 COPY 整个 / 会把 wheel blob 提交进 layer,后续 rm -rf 只是 whiteout,blob 仍留在镜像 / 缓存中,要求对齐 CUDA 的 wheel.sha256 模式。

结论:新增 digest-only 导出 stage,安装层从 7.82 MB 降到 118 B,wheel 不在最终层中;hongxiayang 用隔离 harness 验证了 wheel 内容变化会强制重跑安装层。 · 已解决

风险与影响

  1. 依赖版本冲突被绕开而非解决--no-deps 使 LMCache 声明的 numpy<=2.2.6opentelemetry-api<=1.40.0opentelemetry-exporter-prometheus<=0.61b0 与镜像实际版本(2.3.5、1.44.0、0.65b0)不一致,最终靠 uv pip check 和实测兜底,但上游 requirements 与实测环境仍存在漂移风险。

  2. gfx90a(MI200)用户不可用:发布镜像默认只携带 gfx942;gfx950 的 LMCache 设备代码,MI200 用户拿到镜像后 LMCache 无法运行,仅有构建日志提示,缺少运行时文档说明。

  3. 镜像体积增加:整体 +921 MB(+2.19%),其中 CuPy 约 366 MB,对磁盘和拉取时间有实际影响。

  4. 构建逻辑复杂度高:arch 字符串三种分隔符归一化、FROM final_lmcache_${INSTALL_LMCACHE} 条件 stage、digest 缓存键、code-object 断言,后续维护者需要同时理解 BuildKit 层缓存和 LMCache 上游发布形态,否则容易引入静默错误。

  5. 上游 wheel 覆盖变化:若 LMCache 后续发布覆盖更多 arch,发布流水线的下载路径会自动改变行为,release 镜像的 arch 集合会随之漂移,需要配套监控。

对用户:ROCm 镜像(MI300X/MI325X、MI350X/MI355X 用户)开箱即用地获得 LMCache KV connector,省去手动编译安装;TP>1 用户需注意 PYTHONHASHSEED 固定。

对系统:镜像体积 +2.19%;构建层缓存结构显著优化,避免 wheel blob 残留在层里、避免 vLLM 源码变动连带失效 connector 层。

对团队:新增与 LMCache 上游 release 的耦合点(arch 清单、版本 pin、requirements 修改),需要定期复核;同时为后续其他 KV connector 入镜像提供了可复用的构建模式。

依赖版本冲突 (numpy/otel) gfx90a 无 LMCache 镜像体积 +921 MB --no-deps 绕过依赖解析 构建缓存键复杂度 上游 wheel 覆盖 arch 变化

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论