投影卷

本文档介绍了 Kubernetes 中的投射卷(Projected Volumes)。建议先熟悉卷(Volumes)

引言

projected 卷将多个现有的卷源映射到同一个目录中。

目前,可以投射以下类型的卷源:

所有来源都必须与 Pod 位于同一个命名空间中。有关详细信息,请参阅 all-in-one 卷设计文档。

包含 secret、downwardAPI 和 configMap 的示例配置

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox:1.28
    command: ["sleep", "3600"]
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
          - key: username
            path: my-group/my-username
      - downwardAPI:
          items:
          - path: "labels"
            fieldRef:
              fieldPath: metadata.labels
          - path: "cpu_limit"
            resourceFieldRef:
              containerName: container-test
              resource: limits.cpu
      - configMap:
          name: myconfigmap
          items:
          - key: config
            path: my-group/my-config

示例配置:设置了非默认权限模式的 secrets

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox:1.28
    command: ["sleep", "3600"]
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
          - key: username
            path: my-group/my-username
      - secret:
          name: mysecret2
          items:
          - key: password
            path: my-group/my-password
            mode: 0777

每个投射卷源在 spec 的 sources 下列出。参数几乎相同,但有两个例外:

  • 对于 secrets,secretName 字段已更改为 name,以与 ConfigMap 的命名保持一致。
  • defaultMode 只能在投射级别指定,不能为每个卷源单独指定。然而,如上所示,您可以为每个单独的投射显式设置 mode

serviceAccountToken 投射卷

您可以将当前 服务账号(Service Account) 的令牌注入到 Pod 的指定路径中。例如:

apiVersion: v1
kind: Pod
metadata:
  name: sa-token-test
spec:
  containers:
  - name: container-test
    image: busybox:1.28
    command: ["sleep", "3600"]
    volumeMounts:
    - name: token-vol
      mountPath: "/service-account"
      readOnly: true
  serviceAccountName: default
  volumes:
  - name: token-vol
    projected:
      sources:
      - serviceAccountToken:
          audience: api
          expirationSeconds: 3600
          path: token

示例 Pod 有一个包含注入的服务账号令牌的投射卷。该 Pod 中的容器可以使用该令牌访问 Kubernetes API 服务器,并以 Pod 服务账号的身份进行身份验证。audience 字段包含令牌的预期受众。令牌的接收方必须使用令牌受众中指定的标识符来标识自己,否则应拒绝该令牌。此字段是可选的,默认为 API 服务器的标识符。

expirationSeconds 是服务账号令牌的预期有效期。它默认为 1 小时,且必须至少为 10 分钟(600 秒)。管理员也可以通过为 API 服务器指定 --service-account-max-token-expiration 选项来限制其最大值。path 字段指定了相对于投射卷挂载点的路径。

说明

使用投射卷源作为 subPath 卷挂载的容器将不会收到这些卷源的更新。

clusterTrustBundle 投射卷

特性状态: Kubernetes v1.33 [beta](默认禁用)

说明

要在 Kubernetes 1.36 中使用此功能,必须通过 ClusterTrustBundle 特性门控(feature gate)--runtime-config=certificates.k8s.io/v1beta1/clustertrustbundles=true kube-apiserver 标志启用对 ClusterTrustBundle 对象的支持,然后启用 ClusterTrustBundleProjection 特性门控。

clusterTrustBundle 投射卷源将一个或多个 ClusterTrustBundle 对象的内容作为自动更新的文件注入到容器文件系统中。

ClusterTrustBundles 可以通过 名称签署者名称(signer name) 进行选择。

要按名称选择,请使用 name 字段指定单个 ClusterTrustBundle 对象。

要按签署者名称选择,请使用 signerName 字段(以及可选的 labelSelector 字段)来指定一组使用给定签署者名称的 ClusterTrustBundle 对象。如果不存在 labelSelector,则选择该签署者的所有 ClusterTrustBundles。

kubelet 会对所选 ClusterTrustBundle 对象中的证书进行去重,标准化 PEM 表示(丢弃注释和标题),重新排序证书,并将它们写入 path 指定的文件中。随着所选 ClusterTrustBundles 集合或其内容的变化,kubelet 会保持文件更新。

默认情况下,如果找不到指定的 ClusterTrustBundle,或者 signerName / labelSelector 不匹配任何 ClusterTrustBundles,kubelet 将阻止 Pod 启动。如果这不是您想要的行为,请将 optional 字段设置为 true,Pod 将在 path 处启动并生成一个空文件。

apiVersion: v1
kind: Pod
metadata:
  name: sa-ctb-name-test
spec:
  containers:
  - name: container-test
    image: busybox
    command: ["sleep", "3600"]
    volumeMounts:
    - name: token-vol
      mountPath: "/root-certificates"
      readOnly: true
  serviceAccountName: default
  volumes:
  - name: token-vol
    projected:
      sources:
      - clusterTrustBundle:
          name: example
          path: example-roots.pem
      - clusterTrustBundle:
          signerName: "example.com/mysigner"
          labelSelector:
            matchLabels:
              version: live
          path: mysigner-roots.pem
          optional: true

podCertificate 投射卷

特性状态: Kubernetes v1.35 [beta](默认禁用)

说明

在 Kubernetes 1.36 中,您必须使用 PodCertificateRequest 特性门控--runtime-config=certificates.k8s.io/v1beta1/podcertificaterequests=true kube-apiserver 标志启用对 Pod 证书的支持。

podCertificate 投射卷源安全地提供私钥和 X.509 证书链,供 Pod 用作客户端或服务器凭据。Kubelet 将处理在私钥和证书链接近过期时的刷新工作。应用程序只需确保在文件更改时通过 inotify 或轮询等机制及时重新加载文件即可。

每个 podCertificate 投射支持以下配置字段:

  • signerName:您希望颁发证书的 签署者(signer)。请注意,签署者可能有自己的访问要求,并可能拒绝为您的 Pod 颁发证书。
  • keyType:应生成的私钥类型。有效值为 ED25519ECDSAP256ECDSAP384ECDSAP521RSA3072RSA4096
  • maxExpirationSeconds:您接受的颁发给 Pod 的证书的最长寿命。如果未设置,将默认为 86400(24 小时)。必须至少为 3600(1 小时),至多为 7862400(91 天)。Kubernetes 内置签署者被限制在 86400(1 天)的最大寿命。签署者可以颁发寿命短于您指定的证书。
  • credentialBundlePath:投射内应写入凭据包的相对路径。凭据包是一个 PEM 格式的文件,其中第一个块是包含 PKCS#8 序列化私钥的“PRIVATE KEY”块,剩余块是构成证书链(叶证书和任何中间证书)的“CERTIFICATE”块。
  • keyPathcertificateChainPath:Kubelet 应当写入私钥或证书链的单独路径。
  • userAnnotations:允许您向签署者实现传递附加信息的 map。它会被原样复制到 Kubelet 创建的 PodCertificateRequest 对象的 spec.unverifiedUserAnnotations 字段中。条目遵循与对象元数据注解相同的验证规则,此外所有键必须以域名为前缀。除了整个字段的大小限制外,对值没有其他限制。除了这些基本验证外,API 服务器不进行任何额外的验证。签署者实现在使用此数据时应非常小心。签署者在没有先执行适当验证步骤的情况下,不得默认信任此数据。签署者应记录其支持的键和值。签署者应拒绝包含其无法识别的键的请求。

说明

大多数应用程序应优先使用 credentialBundlePath,除非出于兼容性原因需要将密钥和证书分别存放在单独的文件中。Kubelet 使用基于符号链接的原子写入策略,确保您打开投射文件时,读取到的要么是旧内容,要么是新内容。但是,如果您从单独的文件中读取密钥和证书链,Kubelet 可能会在您第一次读取和第二次读取之间旋转凭据,导致您的应用程序加载不匹配的密钥和证书。
# Sample Pod spec that uses a podCertificate projection to request an ED25519
# private key, a certificate from the `coolcert.example.com/foo` signer, and
# write the results to `/var/run/my-x509-credentials/credentialbundle.pem`.
apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: podcertificate-pod
spec:
  serviceAccountName: default
  containers:
  - image: debian
    name: main
    command: ["sleep", "infinity"]
    volumeMounts:
    - name: my-x509-credentials
      mountPath: /var/run/my-x509-credentials
  volumes:
  - name: my-x509-credentials
    projected:
      defaultMode: 0644
      sources:
      - podCertificate:
          keyType: ED25519
          signerName: coolcert.example.com/foo
          credentialBundlePath: credentialbundle.pem
          userAnnotations:
            example.com/annotation1: "value1"
            example.com/annotation2: "value2"

SecurityContext 的相互作用

在投射服务账号卷增强功能中关于文件权限处理的提案引入了设置正确所有者权限的投射文件。

Linux

在具有投射卷且在 Pod SecurityContext 中设置了 RunAsUser 的 Linux Pod 中,投射文件会设置正确的所有权,包括容器用户所有权。

当 Pod 中的所有容器在它们的 PodSecurityContext 或容器 SecurityContext 中设置了相同的 runAsUser 时,kubelet 会确保 serviceAccountToken 卷的内容由该用户拥有,并且令牌文件的权限模式设置为 0600

说明

在 Pod 创建后添加到 Pod 的 临时容器(Ephemeral containers) 不会更改 Pod 创建时设置的卷权限。

如果 Pod 的 serviceAccountToken 卷权限设置为 0600(因为 Pod 中的所有其他容器都有相同的 runAsUser),则临时容器必须使用相同的 runAsUser 才能读取该令牌。

Windows

在具有投射卷且在 Pod SecurityContext 中设置了 RunAsUsername 的 Windows Pod 中,由于 Windows 管理用户帐户的方式,所有权不会被强制执行。Windows 在名为安全帐户管理器 (SAM) 的数据库文件中存储和管理本地用户和组帐户。每个容器维护自己的 SAM 数据库实例,在容器运行时,主机无法访问该数据库。Windows 容器旨在将操作系统的用户模式部分与主机隔离,因此维护虚拟 SAM 数据库。因此,运行在主机上的 kubelet 没有能力为虚拟化容器帐户动态配置主机文件所有权。建议如果主机上的文件要与容器共享,则应将它们放置在 C:\ 之外的各自卷挂载中。

默认情况下,投射文件将具有以下所有权(如示例投射卷文件所示):

PS C:\> Get-Acl C:\var\run\secrets\kubernetes.io\serviceaccount\..2021_08_31_22_22_18.318230061\ca.crt | Format-List

Path   : Microsoft.PowerShell.Core\FileSystem::C:\var\run\secrets\kubernetes.io\serviceaccount\..2021_08_31_22_22_18.318230061\ca.crt
Owner  : BUILTIN\Administrators
Group  : NT AUTHORITY\SYSTEM
Access : NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Administrators Allow  FullControl
         BUILTIN\Users Allow  ReadAndExecute, Synchronize
Audit  :
Sddl   : O:BAG:SYD:AI(A;ID;FA;;;SY)(A;ID;FA;;;BA)(A;ID;0x1200a9;;;BU)

这意味着所有管理员用户(如 ContainerAdministrator)将具有读取、写入和执行权限,而非管理员用户将具有读取和执行权限。

说明

通常情况下,不鼓励向容器授予对主机的访问权限,因为它可能会导致潜在的安全漏洞。

SecurityContext 中设置 RunAsUser 创建 Windows Pod 将导致 Pod 永久停留在 ContainerCreating 状态。因此,建议不要在 Windows Pod 中使用 Linux 特有的 RunAsUser 选项。


最后修改于 2026 年 2 月 19 日下午 3:34 (PST): 修复 En 文档中的一些链接 (95b7685f71)