Kubernetes 1.31:基于 OCI Artifacts 的只读卷(alpha)

Kubernetes 社区正在努力满足未来更多的人工智能 (AI) 和机器学习 (ML) 用例。虽然该项目过去主要设计用于微服务架构,但现在是时候听取最终用户的意见,并引入更侧重于 AI/ML 的特性了。

其中一个要求是直接支持与 开放容器计划 (OCI) 兼容的镜像和制品(统称为 OCI 对象),将其作为原生卷源。这使得用户可以专注于 OCI 标准,并能够使用 OCI 注册中心存储和分发任何内容。这样的特性为 Kubernetes 项目提供了扩展到运行特定镜像之外用例的机会。

鉴于此,Kubernetes 社区自豪地推出 v1.31 中引入的新 Alpha 特性:镜像卷源 (KEP-4639)。该特性允许用户在 Pod 中将镜像引用指定为卷,并在容器中将其作为卷挂载重复使用。


kind: Pod
spec:
  containers:
    - …
      volumeMounts:
        - name: my-volume
          mountPath: /path/to/directory
  volumes:
    - name: my-volume
      image:
        reference: my-image:tag

上面的例子会使得 my-image:tag 挂载到 Pod 容器中的 /path/to/directory 路径下。

用例

此增强功能的目标是尽可能地贴近 kubelet 中现有的容器镜像实现,同时引入新的 API 接口以支持更广泛的用例。

例如,用户可以在 Pod 中的多个容器之间共享配置文件,而无需将文件包含在主镜像中,从而最大程度地降低安全风险并减小整体镜像大小。他们还可以使用 OCI 镜像打包和分发二进制制品,并将其直接挂载到 Kubernetes Pod 中,例如以此来简化他们的 CI/CD 流水线。

数据科学家、MLOps 工程师或 AI 开发者,可以在 Pod 中与模型服务器一起挂载大型语言模型权重或机器学习模型权重,从而在无需将其包含在模型服务器容器镜像中的情况下高效地提供服务。他们可以将这些打包到 OCI 对象中,以利用 OCI 分发并确保高效的模型部署。这使得他们能够将模型规范/内容与处理它们的可执行文件分离开来。

另一个用例是,安全工程师可以使用公共镜像作为恶意软件扫描器,并挂载一个包含私有(商业)恶意软件签名的卷,从而无需构建自己的组合镜像(公共镜像的版权可能不允许这样做)即可加载这些签名。这些文件不受扫描器软件的操作系统或版本限制。

但从长远来看,发掘此新特性更多重要用例的责任将由您,即本项目的最终用户来承担。SIG Node 非常乐意收集任何关于进一步增强功能以支持更高级使用场景的反馈或建议。欢迎通过 Kubernetes Slack (#sig-node) 频道或 SIG Node 邮件列表提供反馈。

详细示例

为了使该特性正常工作,需要在 API Serverkubelet 上启用 Kubernetes Alpha 特性门控 ImageVolume。如果已启用,并且容器运行时支持此特性(例如 CRI-O ≥ v1.31),则可以创建如下所示的 pod.yaml 示例文件:

apiVersion: v1
kind: Pod
metadata:
  name: pod
spec:
  containers:
    - name: test
      image: registry.k8s.io/e2e-test-images/echoserver:2.3
      volumeMounts:
        - name: volume
          mountPath: /volume
  volumes:
    - name: volume
      image:
        reference: quay.io/crio/artifact:v1
        pullPolicy: IfNotPresent

该 Pod 声明了一个使用 quay.io/crio/artifact:v1 作为 image.reference 的新卷,它指向一个包含两个文件的 OCI 对象。pullPolicy 的行为与容器镜像相同,允许以下值:

  • Always:kubelet 总是尝试拉取该引用,如果拉取失败,则容器创建会失败。
  • Never:kubelet 从不拉取该引用,仅使用本地镜像或制品。如果该引用不存在,则容器创建会失败。
  • IfNotPresent:如果该引用在磁盘上不存在,则 kubelet 会拉取。如果该引用不存在且拉取失败,则容器创建会失败。

volumeMounts 字段指示名称为 test 的容器应该将该卷挂载到 /volume 路径下。

现在如果你创建这个 Pod

kubectl apply -f pod.yaml

并 exec 进入它

kubectl exec -it pod -- sh

那么你就可以查看挂载了什么内容

/ # ls /volume
dir   file
/ # cat /volume/file
2
/ # ls /volume/dir
file
/ # cat /volume/dir/file
1

你成功地使用 Kubernetes 消费了一个 OCI 制品!

容器运行时会拉取镜像(或制品),将其挂载到容器中,并最终使其可供直接使用。实现中有许多细节与 kubelet 现有的镜像拉取行为紧密对齐。例如:

  • 如果提供了 :latest 标签作为 reference,则 pullPolicy 将默认为 Always;而在其他情况下,如果未设置,则默认为 IfNotPresent
  • 如果 Pod 被删除并重新创建,则该卷会重新解析,这意味着新的远程内容在 Pod 重新创建时将变为可用。在 Pod 启动期间无法解析或拉取镜像将阻止容器启动,并可能增加显著的延迟。失败会使用正常的卷指数退避进行重试,并会在 Pod 的原因和消息中报告。
  • 拉取密钥将以与容器镜像相同的方式组装,通过查找节点凭证、服务账户镜像拉取密钥和 Pod 规约镜像拉取密钥来完成。
  • 通过以与容器镜像相同的方式合并 manifest 层,OCI 对象被挂载到单个目录中。
  • 该卷被挂载为只读 (ro) 和非可执行文件 (noexec)。
  • 不支持容器的子路径挂载 (spec.containers[*].volumeMounts.subpath)。
  • 字段 spec.securityContext.fsGroupChangePolicy 对此卷类型没有影响。
  • 如果启用,此特性也适用于 AlwaysPullImages 准入插件

感谢您阅读完这篇博客文章!SIG Node 很高兴也很自豪地将此特性作为 Kubernetes v1.31 的一部分发布。

作为这篇博客文章的作者,我要特别感谢所有为此付出努力的个人!你们都很棒,让我们继续努力吧!

延伸阅读