☰
KServe 使用 `oci+native://` 通过 Kubernetes ImageVolume 直接挂载 OCI 模型镜像
2026/10/10 1:47:08 网站建设 项目流程
  • 模型推理服务
  • 云原生
  • 后端
  • 微服务
  • MLOps
  • 人工智能

【免费下载链接】kserve

Standardized Distributed Generative and Predictive AI Inference Platform for Scalable, Multi-Framework Deployment on Kubernetes

项目地址:https://gitcode.com/gh_mirrors/ks/kserve
点击查看免费下载

导读

本文基于 KServe 仓库中的 OCI Image Volume 示例,系统讲解如何用oci+native://这一显式 URI scheme,让 Kubernetes 以原生 ImageVolume(KEP-4639,即spec.volumes[].image)方式直接挂载模型容器镜像,替代传统 modelcar 边车(sidecar)方案。读完本文,你将掌握:oci+native://与oci://(modelcar)、oci+fetch://三类模式的区别与选型依据;如何配置集群级默认ociModelMode或按服务显式覆盖;如何验证准入 webhook 注入的 ImageVolume 与subPath: "models"挂载;以及 native 模式在不同规模模型下的冷/热启动实测数据与版本兼容性(含OciImageVolumeCompatible咨询条件)的完整知识。

背景:OCI 模型交付的三种路径

KServe 在 OCI(Open Container Initiative)模型交付上规划了三条并存路径,对应 KServe issue #4083 中定义了三种模式常量:

模式URI scheme工作方式适用场景
nativeoci+native://,或oci://(当集群默认配置为 native)Kubernetes ImageVolume 直接挂载,无 sidecarK8s ≥ 1.33,模型镜像已是标准 OCI 格式
modelcaroci://(默认)边车容器与主容器共享/mnt/models任意 K8s 版本、已有 modelcar 镜像
fetch(规划中)oci+fetch://storage initializer 拉取镜像层离线(air-gapped)集群、遗留运行时

oci+native://方案最大的收益是省掉了 modelcar 边车及其生命周期开销,直接复用容器运行时的镜像拉取与缓存能力。

前置条件

要求最低版本
Kubernetes1.35+(ImageVolume beta 默认开启;完整支持,含 subPath)
Kubernetes(手动开启 feature gate,完整支持)1.33–1.34 + 在 apiserver 与 kubelet 上设置--feature-gates=ImageVolume=true
Kubernetes(手动开启 feature gate,无 subPath)1.31–1.32 +--feature-gates=ImageVolume=true——ImageVolume 挂载上的 subPath 被禁止;KServe 会给出OciImageVolumeCompatible咨询条件
容器运行时containerd ≥ 2.0 或 CRI-O ≥ 1.31
KServe本分支(当前仓库)或更新版本

K8s 1.31–1.32 注意:这两个 alpha 版本不支持 ImageVolume VolumeMount 上的subPath。KServe 在检测到该组合时,会在 InferenceService 上设置咨询性条件OciImageVolumeCompatible=False。要获得完整的oci+native://支持,请升级到 K8s 1.33+。

兼容性检测的源码实现

这条咨询条件的判定逻辑位于 pkg/controller/v1beta1/inferenceservice/controller.go:当解析出的存储模式为native时,控制器调用共享辅助函数utils.CheckImageVolumeCompatibility检查集群版本,并按结果分别设置OciImageVolumeUnsupported、OciImageVolumeSubPathUnsupported、OciImageVolumeAlpha三种OciImageVolumeCompatible=False条件;当模式不是native(或版本 ≥ 1.35 / 未知)时清除该条件。重要细节:该条件从不影响 Ready 状态(不在 conditionSet 中),属于纯咨询性提示,且测试覆盖了“从 native 切回其他模式时陈旧条件会被清除”的行为(见 controller_oci_warn_test.go)。

OCI 镜像布局约定

KServe 以subPath: "models"挂载 ImageVolume,也就是说容器运行时会把你镜像内部的/models/目录暴露到配置的modelPath(默认/mnt/models)。这与 modelcar 的 OCI 镜像布局约定一致——model 文件存放在镜像内的/models/目录下。

因此,你不需要为了 native 模式重新构建镜像:只要现有 modelcar 镜像内部有/models/目录,就能直接以oci+native://引用,保持零成本迁移。默认挂载路径常量DefaultModelLocalMountPath = "/mnt/models"定义在 pkg/constants/constants.go。

如何部署:替换 storageUri 并应用 manifest

示例清单位于 docs/samples/storage/oci-image-volume/inference_service.yaml,完整内容如下:

apiVersion: serving.kserve.io/v1beta1 kind: InferenceService metadata: name: sklearn-oci-native namespace: kserve-test spec: predictor: model: modelFormat: name: sklearn # oci+native:// forces Kubernetes-native ImageVolume mounting regardless # of the cluster-wide ociModelMode setting in inferenceservice-config. # Replace with your OCI model image reference. storageUri: "oci+native://ghcr.io/my-org/my-sklearn-model:v1" resources: requests: cpu: "100m" memory: "256Mi" limits: cpu: "500m" memory: "512Mi"

操作步骤:

  1. 把inference_service.yaml中的storageUri替换为你的 OCI 模型镜像引用。镜像内必须包含/models/目录下的模型文件(通过subPath: "models"暴露在/mnt/models)。
  2. 应用清单:
kubectl apply -f inference_service.yaml

URI scheme 的解析逻辑

oci+native://之所以能“绕过全局默认配置强制 native”,是因为 webhook 在注入前会先解析 URI scheme。核心函数ParseOciScheme位于 pkg/utils/storage.go,解析规则如下:

  • "oci+native://reg/img:tag"→ 模式"native",归一化为oci://reg/img:tag
  • "oci+modelcar://reg/img:tag"→ 模式"modelcar"
  • "oci+fetch://reg/img:tag"→ 模式"fetch"
  • "oci://reg/img:tag"→ 模式为空,稍后由配置解析
  • "s3://bucket/key"→ 非 OCI URI,原样返回

在 storage_initializer_injector.go 中,注入器先取显式后缀模式;若为空(裸oci://),则回退到types.ResolveOciModelMode(config)决定有效模式,再分发到对应的 materializer:

  • native→utils.ConfigureOciNativeToContainer
  • fetch→ConfigureOciFetchToContainer
  • 默认 →utils.ConfigureModelcarToContainer

如何验证

检查 InferenceService 是否已创建:

kubectl get inferenceservice sklearn-oci-native -n kserve-test

查看已被准入 webhook 物化的 Pod spec,确认注入了image类型 volume 以及kserve-container上的只读挂载:

kubectl describe pod -n kserve-test -l serving.kserve.io/inferenceservice=sklearn-oci-native

你应该能看到类似如下的 volume 段:

Volumes: mnt-models: Type: Image (an OCI container image) Reference: ghcr.io/my-org/my-sklearn-model:v1 ...

以及带subPath: "models"的容器挂载:

Mounts: /mnt/models from mnt-models (ro, subPath=models)

注入器到底做了什么

从源码看,ConfigureOciNativeToContainer(pkg/utils/storage.go)完成了四件事:

  1. 从oci://前缀后截取镜像引用(strings.TrimPrefix(modelUri, "oci://"));
  2. 根据 modelPath 派生唯一 volume 名(GetVolumeNameFromPath,空时回退为oci-model),保证同一 Pod 内多个不同挂载路径的 adapter 互不冲突;
  3. 向 PodSpec 追加corev1.Volume,类型为corev1.ImageVolumeSource,其中Reference为镜像引用、PullPolicy: PullIfNotPresent(镜像已在节点时不再重复拉取);
  4. 为目标容器追加只读 VolumeMount:AddVolumeMountIfNotPresentWithSubPath(targetContainer, volName, modelPath, "models", true)。

同时它做了两处防呆校验:若 modelPath 已被其他 volume 占用则拒绝注入(避免静默的挂载遮蔽);若 Pod 级同名 Volume 已存在则不再重复追加(API server 会拒绝重复条目)。由于注入逻辑是幂等的(webhook 可能以reinvocationPolicy: IfNeeded多次调用),重复调用不会产生重复的 volume 或 mount。

全局默认配置 vs 显式 scheme

你可以在inferenceservice-configConfigMap 中配置ociModelMode: "native",让集群内所有oci://URI 默认走 native 挂载;而oci+native://是显式覆盖,绕过全局设置——适合“单个服务用 native、集群默认仍保持 modelcar”的场景。

示例清单文件顶部有一段被注释掉的 ConfigMap 片段(inference_service.yaml):

apiVersion: v1 kind: ConfigMap metadata: name: inferenceservice-config namespace: kserve data: storageInitializer: |- { "enableOciModelSupport": true, "ociModelMode": "native" }

对应StorageInitializerConfig结构体(pkg/types/config.go)中的字段:

  • enableOciModelSupport:总开关,开启任意 OCI 模型存储路径;兼容旧的enableModelcar字段;
  • ociModelMode:"modelcar"(默认)、"native"、"fetch"三选一,空值解析为"modelcar";
  • OciInsecureRegistry:仅供oci+fetch://路径关闭 TLS 校验(默认 false,必须显式开启)。

模式解析优先级(ResolveOciModelMode,pkg/types/config.go):显式OciModelMode> 旧的EnableOciImageSource/EnableOciModelSupport(向后兼容)> 空(禁用/默认 modelcar)。

native 模式下多模型挂载的冲突规则

如果你在单个 InferenceService 中挂载多个 OCI 源,native 与 modelcar 的冲突规则不同(ValidateOCIMountPaths,pkg/utils/storage.go):

  • native 模式:只禁止完全相同的挂载路径重复出现;同一父目录下的兄弟路径完全合法(因为每个 ImageVolume 独立挂载到自己的精确路径);
  • modelcar 模式:每个 sidecar 把 emptyDir 挂在 modelPath 的父目录上,因此共享父目录的两个 URI 会在目标容器上造成挂载遮蔽,必须使用父目录不同的挂载路径。

实测启动行为

仓库文档给出了一份单节点基准数据(kind,Kubernetes v1.36.1,containerd 2.3.1,KServe master 构建;单云虚拟机 AWS m6i.2xlarge:8 vCPU / 32 GB RAM,1 TB gp3 EBS 默认预置 3,000 IOPS / 125 MiB/s,控制平面、registry、MinIO 与 predictor Pod 同机部署,140 GB 档受默认磁盘吞吐限制)。测试产物为 2 / 14 / 140 GB(对应 fp16 权重下的 1B / 7B / 70B 级模型)。数据为从InferenceServiceapply 到首次成功预测的中位秒数:

路径2 GB 冷2 GB 热14 GB 冷14 GB 热140 GB 冷140 GB 热
oci+native://38.611.5443.411.44,585.511.7
modelcar(oci://)59.615.4363.315.24,603.015.8
s3://39.630.0137.8128.42,318.82,441.6

“热”= 模型镜像已存在于节点 containerd 存储中;对s3://而言节点层没有缓存,因此每次 Pod 启动都会重新下载产物。

关键结论:

  • OCI 路径的热启动与模型大小无关:镜像缓存在节点后,无论 2 GB 还是 140 GB,oci+native://都在约 12 秒内完成首次预测;在热节点上为 140 GB 模型加一个副本只需约 11.7 秒,而s3://要重新下载约 41 分钟。
  • 首次冷拉取的成本高于纯下载(140 GB 时约 2×):containerd 先写 layer blob 再解包进 snapshot,需要两遍磁盘;而 storage initializer 只流式写一遍。如果节点多次服务同一模型,缓存拉取很快就能摊薄这笔开销。
  • modelcar 与 native 表现相同的拉取行为,外加每 Pod 约 4 秒的 sidecar 生命周期开销(这与文档基准中 15.4s vs 11.5s 的差异吻合,也解释了 modelcar 热启动普遍比 native 慢 3–4 秒的原因)。

绝对数值随磁盘/网络吞吐伸缩,路径间的相对行为才是稳定结论。完整方法学、脚本与原始数据见文档末尾引用的外部基准仓库(oci-model-delivery-bench),该 harness 可在任意集群上运行以获取你环境的数据。

何时选择 native 而非替代方案

  • 选native:集群 K8s ≥ 1.33、模型镜像已是 OCI 格式时,直接用oci+native://(或集群默认配置ociModelMode: "native")。它避开了 modelcar 边车开销,直接复用容器运行时的镜像拉取与缓存。
  • 选modelcar:任何 K8s 版本都能用,且已存在 modelcar 镜像时保持默认oci://即可;代价是每 Pod 多出约 4 秒的 sidecar 生命周期开销。
  • fetch(规划中):面向离线集群与遗留运行时,由 storage initializer 拉取镜像层,属于路线图中的未来能力(oci+fetch://前缀已由ParseOciScheme预留解析)。

参考链接

  • OCI Image Volume 示例 README
  • 示例 InferenceService 清单
  • URI scheme 解析与 native 注入实现
  • 存储初始化器注入 webhook
  • StorageInitializerConfig 与模式常量
  • OciImageVolumeCompatible 咨询条件实现
  • KServe issue #4083:OCI 存储统一路线图
  • KEP-4639:OCI Volume Source
  • 模型推理服务
  • 云原生
  • 后端
  • 微服务
  • MLOps
  • 人工智能

【免费下载链接】kserve

Standardized Distributed Generative and Predictive AI Inference Platform for Scalable, Multi-Framework Deployment on Kubernetes

项目地址:https://gitcode.com/gh_mirrors/ks/kserve
点击查看免费下载

相关推荐

上一篇:微信AI机器人防封号终极指南:安全部署与智能回复完整方案
下一篇:深度解析Shell脚本远程调试协议:基于sh1/sh项目的高效调试方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询