#AI#大模型#DevOps

Kubernetes 模型权重分发:节点缓存、摘要校验与回滚

大模型权重不能在每次 Pod 启动时无校验下载。本文比较 init container、共享卷与 DaemonSet 节点缓存,给出不可变摘要、原子发布、容量回收和回滚验收流程。

本文要点

  • 主题聚焦:AI、大模型、DevOps
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年4月11日·更新于 2026年7月20日

几十 GB 的模型若由每个推理 Pod 启动时从对象存储下载,会把扩容时间变成网络抖动函数:同一节点重复传输、半文件被进程读取、源站限流后启动探针连续失败。容器镜像已经通过 digest 提供不可变身份,模型权重也应采用相同原则。模型来源、转换和签名校验属于 AI 供应链安全,这里聚焦权重进入 Kubernetes 节点后的分发与生命周期。

先定义不可变的模型清单

部署对象不要只写 model: latest 或可覆盖的对象键。发布流水线先生成 manifest,至少包含逻辑模型名、格式、分片列表、每个文件大小与 SHA-256、整体版本、来源 URI 和创建时间;工作负载只引用 manifest digest。缓存目录也以 digest 命名,例如 /cache/sha256/<digest>/,同一路径一旦出现就永不原地改写。

下载器在同一文件系统创建临时目录,逐个校验长度和摘要,完成后写 READY 标记并原子 rename。令牌只通过短期凭证注入下载进程,不进入 manifest、日志和 Pod annotation。一个简化的校验步骤是:

bash
set -euo pipefail
tmp="$(mktemp /cache/.model.XXXXXX)"
trap 'rm -f "$tmp"' EXIT
curl --fail --location --retry 3 -o "$tmp" "$MODEL_URL"
printf '%s  %s\n' "$MODEL_SHA256" "$tmp" | sha256sum --check --status
chmod 0444 "$tmp"
sync -f "$tmp"
dest="/cache/objects/$MODEL_SHA256"
ln -T -- "$tmp" "$dest" 2>/dev/null ||
  printf '%s  %s\n' "$MODEL_SHA256" "$dest" | sha256sum --check --status
sync -f "$(dirname "$dest")"
rm -f "$tmp"

临时文件和目标必须位于同一文件系统;生产实现应对文件描述符执行 fsync(2),发布 hard link 或 rename 后再 fsync 父目录,示例的 sync -f 是更重的 shell 屏障。竞争失败时校验现有对象。多分片全部通过后才发布绑定 manifest root 的 READY 指针。

按复用范围选择分发方式

init container 加 emptyDir 最简单:它在主容器前完成下载,同一 Pod 内可共享文件,适合小模型或低副本任务;Pod 重建后缓存消失,扩容风暴仍会重复下载。网络文件系统或 ReadOnlyMany PVC 能跨 Pod 复用,但模型加载延迟受存储吞吐和故障域影响,必须测并发 mmap/read,而不是只测一次顺序读取。

DaemonSet 节点缓存适合固定 GPU 池。缓存代理在每个目标节点预取允许的 digest,推理 Pod 只读挂载缓存。若使用 hostPath,它绕过常规存储隔离:DaemonSet 需要最小权限,业务容器只挂载具体模型子目录且只读,不能把整个宿主缓存根目录暴露给租户。节点标签只能由受信控制器在校验完成后写入,例如 models.example.com/ready-<短摘要>=true;用户不能自行设置节点或修改 affinity。

调度器还需要知道“缓存未命中是否允许现场下载”。延迟敏感服务应只落在已就绪节点,并保留足够预热副本;批任务可以先调度,再由 init container 在带宽限速下补齐。该选择要与 GPU 推理容量规划 的冷启动预算、模型服务金丝雀发布 的流量步长一起制定,不能把下载时间隐藏在 readiness 之后。

回收必须知道谁仍在引用

节点缓存超过 80% 开始清理,降到 65% 停止。候选不得有活跃 Pod、预热租约,且不在当前/上一稳定版本集合。回收先撤销该 digest 的 ready 节点标签,等待调度传播并确认没有已绑定或新建 Pod,再进入 EVICTING。普通 hostPath 挂载不读取代理锁,引用计数无法阻止 kubelet 的竞态;需要硬挂载 lease 时必须使用 CSI/node plugin,以 Pod UID 获取和释放 lease。仍有引用或打开文件就跳过。

磁盘压力不能靠 kubelet 随机选择模型。缓存服务启动、写 ready 标签以及 Pod 使用前都重算 manifest root;不匹配时先撤标签,再隔离对象。缓存异常时不得把未校验路径标为 ready,并监控字节、inode、校验失败、命中率与引用数。

用破坏性场景验收

先测冷/热节点启动、源站字节和首个请求,再注入下载中断、错误 SHA、缺分片、并发下载、磁盘不足、发布后掉电与 READY 后篡改。断言主容器不读半文件,错误 root 先撤 ready 标签并隔离,临时文件清理,其他模型仍可服务。

bash
kubectl get daemonset -n model-system
kubectl get pods -n model-system -o wide
kubectl describe node <gpu-node>
kubectl logs -n model-system <cache-pod> --since=30m

服务层持续以版本和摘要记录请求,结合 Prometheus 与 Grafana 可观测性 对比 cache hit、模型加载秒数和 P99,避免“Pod 很快 Ready,但第一次请求才加载权重”的假优化。

回滚只切引用,不覆盖文件

新模型异常时,把 Deployment 或发布控制器的 manifest digest 切回上一稳定值,并确保对应节点缓存仍在保留集合;随后按正常滚动策略替换 Pod。不要把旧文件复制回新 digest 路径,也不要修改同名对象内容,否则审计、缓存与实际字节会分裂。

缓存代理版本回滚时先暂停清理,保留已验证对象与 READY 元数据,回滚 DaemonSet 后随机抽样重算摘要再恢复回收。若节点缓存整体不可信,cordon 该节点、排空推理 Pod、清空专用缓存分区并重新预热;不要在运行容器仍引用文件时原地修复。跨区域源站不可用时,按 LLM 灾难恢复 使用已验证副本,恢复目标仍然是同一个内容摘要。

延伸阅读

Related

相关推荐