
模型推理服务云原生后端微服务MLOps人工智能【免费下载链接】kserveStandardized Distributed Generative and Predictive AI Inference Platform for Scalable, Multi-Framework Deployment on Kubernetes项目地址https://gitcode.com/gh_mirrors/ks/kserve点击查看免费下载ocifetch://是 KServe 提供的第三种 OCI 模型存储方案它让storage-initializer 以 init container 的形式用 oras-py 拉取模型容器镜像的分层并把镜像内/models/子树解压到共享的/mnt/models卷中。本文基于 docs/samples/storage/oci-fetch 示例与 KServe 源码完整讲解该模式的 URI 约定、镜像布局要求、私有仓库认证、TLS 配置、验证命令并对比native/modelcar两种替代模式的适用场景帮助你在不支持 Kubernetes ImageVolume 的集群上以最小资源开销部署 OCI 打包的模型。一、背景KServe 的三种 OCI 模型存储模式在 KServe 中模型可以通过 OCI 镜像即 modelcar 布局来打包和分发。围绕 OCI 镜像的加载方式KServe 提供了三种模式它们的 URI scheme 与加载机制各不相同模式URI scheme加载机制适用场景fetchocifetch://storage-initializerinit container拉取镜像分层解压/models/到共享卷不支持 ImageVolume 的集群/运行时且希望避免长期驻留的 sidecarnativeocinative://或oci://若配置为默认Kubernetes ImageVolume 直接挂载无 sidecarK8s ≥ 1.33 且运行时支持 ImageVolumemodelcaroci://默认sidecar 容器共享/mnt/models任意 K8s 版本已有 modelcar 镜像ocifetch://的核心定位是运行时无关runtime-agnostic与ocinative://不同它不依赖 Kubernetes ImageVolume该特性要求较新的 K8s 版本与运行时支持与默认的modelcar模式不同它不为 Pod 生命周期运行一个常驻 sidecar——镜像只在 init container 中拉取一次拉完即退出资源随即释放。该模式属于 KServe issue #4083OCI 存储统一化路线图的落地步骤之一对应的 Go 端注入逻辑与 Python 端拉取实现均已合入当前仓库。二、前置条件使用ocifetch://前需满足以下条件条件说明KServe 版本本分支或更新版本且启用了enableOciModelSupport: true模型镜像布局模型文件必须位于镜像的/models/目录下modelcar 约定仓库访问公共镜像可匿名拉取私有镜像需要配置imagePullSecretenableOciModelSupport属于inferenceservice-configConfigMap 中storageInitializer配置段见 pkg/types/config.go 中StorageInitializerConfig.EnableOciModelSupport字段。从源码看它还保留了enableModelcarEnableOciImageSource作为向后兼容开关当ociModelMode未显式设置时ResolveOciModelMode会按OciModelMode显式EnableOciImageSource/EnableOciModelSupport兼容 空禁用的优先级解析默认模式pkg/types/config.go。三、OCI 镜像布局约定modelcar 约定ocifetch://复用了 modelcar 的镜像布局约定模型文件必须存放在镜像的/models/目录下。拉取与解压过程只关心这一棵子树storage-initializer 拉取镜像各分层仅提取/models/子树到/mnt/models镜像其余 rootfs 内容基础镜像层、/etc/passwd修改等虽然会被流式扫描但不会写入磁盘若镜像中没有/models/目录拉取会以明确的错误失败见下文源码剖析。这一约定意味着为普通推理服务构建的模型镜像与 modelcar 镜像可以直接复用无需重新构建。多架构镜像解析由docker buildx构建的多架构镜像OCI image index / Docker manifest list会被自动解析为与 init container 架构匹配的平台 manifestlinux/GOARCH。因此即使只使用 tag 而不带 digest 指定目标也能拉取到正确的单架构模型分层不会出现index 拉下来却是空目录的问题。四、端到端工作原理ocifetch://的完整数据流如下用户在InferenceService的spec.predictor.model.storageUri中写入ocifetch://镜像引用Pod 创建时准入 webhook 识别出该 URI调用ConfigureOciFetchToContainerpkg/webhook/admission/pod/oci_fetch.go注入标准的storage-initializerinit container并把归一化后的oci://镜像引用作为存储参数传入ParseOciScheme会剥离fetch后缀见 pkg/utils/storage.go若有imagePullSecret将凭据以 dockerconfig.json形式投影进 init container若配置了自定义 CA bundle一并挂载Python 端 storage-initializer 的oci://handlerStorage._download_oci见 python/storage/kserve_storage/kserve_storage.py使用 oras-py 拉取镜像分层解压/models/到共享 emptyDir 卷/mnt/modelskserve-container从共享卷读取模型开始推理。模型通过 init container写入方与 serving 容器读取方共享的单个 emptyDir 卷传递卷名由modelPath推导GetVolumeNameFromPath路径无法推导时回退为oci-fetch-model。该函数是幂等的同一 (URI, path) 参数对不会被重复添加同一 Pod 内多个ocifetch://源可共享同一个 init container。五、部署实战完整示例 Manifest示例位于 docs/samples/storage/oci-fetch/inference_service.yaml下面是完整内容含私有仓库所需的注释块--- # 可选私有模型镜像的仓库凭据。公共镜像可匿名拉取可跳过整个块。 # webhook 会把第一个 imagePullSecret 以 docker config.json 形式投影进 init container。 # # apiVersion: v1 # kind: Secret # metadata: # name: oci-fetch-registry-creds # namespace: kserve-test # type: kubernetes.io/dockerconfigjson # data: # .dockerconfigjson: base64-docker-config-json --- apiVersion: serving.kserve.io/v1beta1 kind: InferenceService metadata: name: sklearn-oci-fetch namespace: kserve-test spec: predictor: # 私有模型镜像时取消注释必须与上面的 Secret 对应。 # imagePullSecrets: # - name: oci-fetch-registry-creds model: modelFormat: name: sklearn # ocifetch:// 让 storage-initializer 拉取镜像的 /models/ 分层。 # 公共测试镜像fetch E2E 测试使用。 # 生产环境请替换为你自己的 OCI 打包模型镜像。 storageUri: ocifetch://ghcr.io/kliukovkin/kserve-oci-test-fixture:v1 resources: requests: cpu: 100m memory: 256Mi limits: cpu: 500m memory: 512Mi部署步骤将inference_service.yaml中的storageUri替换为你自己的 OCI 模型镜像引用镜像内必须包含/models/仅私有仓库在kserve-test命名空间创建 docker-registry secret并取消Secret块与predictor.imagePullSecrets字段的注释kubectl create secret docker-registry oci-fetch-registry-creds \ --docker-serverghcr.io --docker-usernameuser \ --docker-passwordtoken -n kserve-test应用清单kubectl apply -f inference_service.yaml注意当前示例中storageUri使用的测试镜像即为 E2E 测试所引用的公共多架构 fixturetest/e2e/storage/test_oci_fetch.py镜像内含单个模型文件/models/model.joblib可匿名从 ghcr.io 拉取适合先跑通链路后再替换为自己的镜像。六、如何验证确认 InferenceService 已创建kubectl get inferenceservice sklearn-oci-fetch -n kserve-teststorage-initializer init container 负责拉取确认其成功退出退出码 0kubectl get pod -n kserve-test -l serving.kserve.io/inferenceservicesklearn-oci-fetch \ -o jsonpath{.items[0].status.initContainerStatuses[?(.namestorage-initializer)].state.terminated.exitCode} # - 0查看 init container 日志观察拉取过程kubectl logs -n kserve-test \ -l serving.kserve.io/inferenceservicesklearn-oci-fetch -c storage-initializer确认模型文件已落入共享挂载点kubectl exec -n kserve-test \ $(kubectl get pod -n kserve-test \ -l serving.kserve.io/inferenceservicesklearn-oci-fetch \ -o jsonpath{.items[0].metadata.name}) \ -c kserve-container -- ls /mnt/models这套验证流程与 E2E 测试test_oci_fetch_inference_service_pulls_and_extracts_model的断言逻辑一致init container 以退出码 0 结束证明拉取 解压成功因为 handler 在镜像缺少/models/目录时会抛错随后 exec 进kserve-container确认model.joblib出现在/mnt/models。七、私有仓库认证的工作原理当 predictor 声明imagePullSecret时准入 webhook 会把第一个 secret的.dockerconfigjson键以config.json形式投影进 init container卷名kserve-oci-fetch-docker-config挂载于/mnt/oci-fetch-auth并设置KSERVE_OCI_DOCKER_CONFIG环境变量指向该文件路径pkg/webhook/admission/pod/oci_fetch.go。Python handler 读取该路径并将其作为显式config_path传给 oras-pypython/storage/kserve_storage/kserve_storage.py。几个关键实现细节为什么挂在/mnt而非/rootstorage-initializer 镜像以非 root 用户UID 1000运行无法穿越权限为 0700 的/root目录/mnt在镜像构建时已 chown 给该用户因此挂载的凭据可读。源码注释对此有明确说明pkg/webhook/admission/pod/oci_fetch.go。为什么显式传config_pathoras-py 会忽略DOCKER_CONFIG默认只读~/.docker/config.json显式路径保证了无论$HOME或容器用户如何凭据都能被找到。若文件不存在则回退为匿名拉取适合公共仓库。权限收紧投影卷的DefaultMode为0400仅属主可读并只读挂载作为凭据防护的纵深防御pkg/webhook/admission/pod/oci_fetch.go。多个imagePullSecrets仅使用第一个并记录警告日志。若需要访问多个仓库请把凭据合并进单个dockerconfigjsonsecretpkg/webhook/admission/pod/oci_fetch.go。无 secret 时匿名拉取公共镜像。此外storageInitializer配置中的ociInsecureRegistry对应环境变量KSERVE_OCI_INSECURE_REGISTRY可显式跳过 TLS 校验用于纯 HTTP 或无法分发 CA 的自签名证书仓库。它默认关闭安全/校验 HTTPS且必须显式开启绝不会根据仓库域名自动推断pkg/types/config.go。E2E 测试test_oci_fetch_with_image_pull_secret_spec_only专门在规格层面断言了这一凭据接线当 predictor 声明imagePullSecret时webhook 生成的 init container 上应出现kserve-oci-fetch-docker-config卷与KSERVE_OCI_DOCKER_CONFIG环境变量test/e2e/storage/test_oci_fetch.py。八、自定义 CA bundle私有 TLS如果通过inferenceservice-config中的caBundleConfigMapName为 storage-initializer 配置了自定义 CA bundle它会同样被挂载进 fetch init container并通过REQUESTS_CA_BUNDLE导出使 oras-py 信任私有仓库的 TLS 证书——这与 S3 handler 的 CA bundle 处理方式保持一致。从源码看pkg/webhook/admission/pod/oci_fetch.go未配置 CA bundle 时为 no-op命名空间非 KServe 命名空间时CA bundle ConfigMap 会映射为全局默认名DefaultGlobalCaBundleConfigMapName挂载路径可配置未设置时使用默认路径Python 端_setup_oci_tls会据此设置REQUESTS_CA_BUNDLEpython/storage/kserve_storage/kserve_storage.py。九、源码级深入Go 注入器与 Python 拉取器Go 端ConfigureOciFetchToContainer入口为 pkg/webhook/admission/pod/oci_fetch.go。它复用标准的 init-container 下载机制与nativeImageVolume和modelcarsidecar都不同用utils.CreateInitContainerWithConfig构建 init container参数为(uri, path)对若 Pod 中已存在 storage-initializer init container例如还有其他 OCI 源则追加新的(uri, path)对而不是创建第二个同名容器每个源的模型卷挂载通过AddModelMount去重同 Pod 内不支持将ocifetch://与非 OCI 存储 URIS3/GCS/HTTP 等混用——共享的 init container 名会导致非 OCI 下载路径被跳过多个ocifetch://源则受支持。Python 端Storage._download_oci核心实现位于 python/storage/kserve_storage/kserve_storage.py要点多架构索引解析oras-py不会自行从 index 中选择平台因此 handler 先get_manifest判断 mediaType 是否为 OCI image index若是则用_detect_goarch探测架构、_pick_platform选择匹配的 platform entry并用 digest 重写目标后再拉取流式解压每个 layer blob 直接从 HTTP 响应流进入tarfile的流式读取器按 layer mediaType 选择r|、r|gz等模式逐成员解压到out_dir只保留models/下的条目并去掉models/前缀。这样避免了先下载完整镜像再解压再搬运的二次全量拷贝对超大 modelcar 可显著降低峰值磁盘占用与墙钟时间zstd 支持对application/vnd.oci.image.layer.v1.tarzstd分层用zstandard的流式解压器包装显式read_across_framesTrue处理多帧 blob全程不落临时文件tar 安全解压使用tarfile的filterdata要求 Python ≥ 3.11.4对 tar-slip/路径穿越做了加固明确的失败语义若所有分层中未提取到任何models/条目抛出错误并列出各层顶层条目供排查OCI image at X has no /models/ directory…。十、何时使用 fetch 而非其他模式选型建议来自原文档仓库实现与之吻合ocifetch://fetch集群/运行时不支持 ImageVolume或你不想为 Pod 生命周期保留一个常驻 sidecar。镜像在 init container 中拉取一次即退出资源随 Pod 启动过程释放。ocinative://nativeK8s ≥ 1.33或在 1.31–1.32 启用特性门控且运行时支持 ImageVolume 时由 kubelet/容器运行时直接挂载镜像无 init、无 sidecarpkg/utils/storage.go 中ConfigureOciNativeToContainer使用subPathmodels兼容既有 modelcar 布局。oci://modelcar默认任意 K8s 版本均可用且直接复用已有 modelcar 镜像代价是 sidecar 在整个 Pod 生命周期内驻留pkg/utils/storage.go 中ConfigureModelcarToContainer。需要特别说明的是目前尚无基准数据不要未经测量就假设某一种模式性能更优——请基于自身镜像大小、集群版本与资源约束实测后再做选型。十一、当前限制与后续规划LLMInferenceService尚未支持ociModelMode: fetchLLM 服务会回退到 modelcar 模式fetch 对 LLMISVC 的支持是规划中的后续工作私有仓库认证不支持旧式kubernetes.io/dockercfgsecret请使用kubernetes.io/dockerconfigjson旧式 secret 缺少.dockerconfigjson键投影文件会缺失并导致拉取失败同一 Pod 内不支持ocifetch://与非 OCI 存储 URI 混用见上文 Go 端实现说明imagePullSecrets多 secret 合并未实现仅使用第一个多仓库请合并进单个dockerconfigjsonsecretissue #4083 路线图的第 4 步ModelPack是规划中的后续工作将构建在这条 fetch 路径之上。十二、参考示例清单docs/samples/storage/oci-fetch/inference_service.yamlGo 端 webhook 注入pkg/webhook/admission/pod/oci_fetch.go配置结构pkg/types/config.goStorageInitializerConfig、OciModelMode、ResolveOciModelModeURI 解析与 OCI 挂载辅助pkg/utils/storage.goPython 端拉取实现python/storage/kserve_storage/kserve_storage.pyStorage._download_ociE2E 测试test/e2e/storage/test_oci_fetch.py关联的 ImageVolume 模式示例docs/samples/storage/oci-image-volume/README.md赞分享模型推理服务云原生后端微服务MLOps人工智能【免费下载链接】kserveStandardized Distributed Generative and Predictive AI Inference Platform for Scalable, Multi-Framework Deployment on Kubernetes项目地址https://gitcode.com/gh_mirrors/ks/kserve点击查看免费下载相关推荐pnpr OCI Pull-Through 缓存实战从 Docker Hub 与 GHCR 拉取镜像并按摘要校验pnpr OCI Pull Through 缓存实战从 Docker Hub 与 GHCR 拉取镜像并按摘要校验 本指南基于 pnpm 仓库中的 pnpm/包管理器开发工具CLImise oci build 实战从 mise.toml 一键构建可复用的 OCI 容器镜像mise oci build 实战从 mise.toml 一键构建可复用的 OCI 容器镜像 mise oci build 是 misevfox 迁移前的开发工具CLIDragonfly2与容器生态集成如何加速OCI镜像拉取和AI/ML模型分发Dragonfly2与容器生态集成如何加速OCI镜像拉取和AI/ML模型分发 Dragonfly2是一个开源的CDN加速和负载均衡器专门为云原生应用程序设计云原生网络与通信后端存储上一篇AutoclickmacOS系统级鼠标自动化事件注入技术方案下一篇终极指南如何使用Autoclick实现Mac自动点击900次/秒创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考