Kubernetes 提供了一种方式,使得 Pod 中的容器可以通过文件系统访问和共享数据。你可以使用不同类型的卷来实现不同的目的,例如:

  • 根据 ConfigMapSecret 填充配置文件
  • 为 Pod 提供一些临时暂存空间
  • 在同一个 Pod 中的两个不同容器之间共享文件系统
  • 在两个不同的 Pod 之间共享文件系统(即使这些 Pod 运行在不同的节点上)
  • 持久存储数据,即使 Pod 重启或被替换,数据仍然可用
  • 根据容器所在 Pod 的详细信息,将配置信息传递给容器中运行的应用程序(例如:告诉 边车容器 Pod 正在哪个命名空间中运行)
  • 提供对不同容器镜像中的数据的只读访问

数据共享可以在容器内部的不同本地进程之间、不同容器之间或不同 Pod 之间进行。

为什么卷很重要

  • 数据持久性: 容器中的磁盘文件是短暂的,这给在容器中运行的非简单应用程序带来了一些问题。一个问题是当容器崩溃或停止时,容器状态不会被保存,因此在容器生命周期中创建或修改的所有文件都会丢失。崩溃后,kubelet 会以干净的状态重新启动容器。

  • 共享存储: 另一个问题是当多个容器在 `Pod` 中运行并需要共享文件时。在所有容器之间设置和访问共享文件系统可能很困难。

Kubernetes 抽象可以帮助你解决这两个问题。

在你了解卷、PersistentVolume 和 PersistentVolumeClaim 之前,你应该阅读 Pod,并确保你理解 Kubernetes 如何使用 Pod 运行容器。

卷的工作原理

Kubernetes 支持多种卷类型。一个 Pod 可以同时使用任意数量的卷类型。临时卷 类型的生命周期与特定的 Pod 绑定,而 持久卷 存在于任何单个 Pod 的生命周期之外。当 Pod 不再存在时,Kubernetes 会销毁临时卷;但是,Kubernetes 不会销毁持久卷。对于给定 Pod 中的任何类型的卷,数据在容器重启后仍会保留。

从本质上讲,卷是一个目录,可能包含一些数据,可供 Pod 中的容器访问。该目录如何生成,其背后的介质以及其内容由所使用的特定卷类型决定。

要使用卷,请在 `.spec.volumes` 中指定要为 Pod 提供的卷,并在 `.spec.containers[*].volumeMounts` 中声明将这些卷挂载到容器中的位置。

当 Pod 启动时,容器中的进程会看到一个由 容器镜像 的初始内容以及挂载在容器内部的卷(如果已定义)组成的文件系统视图。进程会看到一个最初与容器镜像内容匹配的根文件系统。对该文件系统层次结构内任何(如果允许)的写入都会影响该进程在后续文件系统访问时所看到的视图。卷被挂载到容器文件系统内的指定路径。对于 Pod 中定义的每个容器,你必须独立指定该容器使用的每个卷的挂载位置。

卷不能挂载到其他卷内部(但请参阅使用 subPath 来了解相关的机制)。此外,一个卷不能包含指向不同卷中任何内容的硬链接。

卷类型

Kubernetes 支持多种类型的卷。

awsElasticBlockStore (已弃用)

在 Kubernetes 1.34 中,树内 `awsElasticBlockStore` 类型的所有操作都被重定向到 `ebs.csi.aws.com` CSI 驱动。

AWSElasticBlockStore 树内存储驱动在 Kubernetes v1.19 版本中被弃用,然后在 v1.27 版本中完全移除。

Kubernetes 项目建议你改用 AWS EBS 第三方存储驱动。

azureDisk (已弃用)

在 Kubernetes 1.34 中,树内 `azureDisk` 类型的所有操作都被重定向到 `disk.csi.azure.com` CSI 驱动。

AzureDisk 树内存储驱动在 Kubernetes v1.19 版本中被弃用,然后在 v1.27 版本中完全移除。

Kubernetes 项目建议你改用 Azure Disk 第三方存储驱动。

azureFile (已弃用)

在 Kubernetes 1.34 中,树内 `azureFile` 类型的所有操作都被重定向到 `file.csi.azure.com` CSI 驱动。

AzureFile 树内存储驱动在 Kubernetes v1.21 版本中被弃用,然后在 v1.30 版本中完全移除。

Kubernetes 项目建议你改用 Azure File 第三方存储驱动。

cephfs (已移除)

Kubernetes 1.34 不包含 `cephfs` 卷类型。

`cephfs` 树内存储驱动在 Kubernetes v1.28 版本中被弃用,然后在 v1.31 版本中完全移除。

cinder (已弃用)

在 Kubernetes 1.34 中,树内 `cinder` 类型的所有操作都被重定向到 `cinder.csi.openstack.org` CSI 驱动。

OpenStack Cinder 树内存储驱动在 Kubernetes v1.11 版本中被弃用,然后在 v1.26 版本中完全移除。

Kubernetes 项目建议你改用 OpenStack Cinder 第三方存储驱动。

configMap

ConfigMap 提供了一种将配置数据注入 Pod 的方式。ConfigMap 中存储的数据可以在 `configMap` 类型的卷中引用,然后由在 Pod 中运行的容器化应用程序使用。

当引用 ConfigMap 时,你需要在卷中提供 ConfigMap 的名称。你可以自定义 ConfigMap 中特定条目的使用路径。以下配置展示了如何将 `log-config` ConfigMap 挂载到名为 `configmap-pod` 的 Pod 上。

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
    - name: test
      image: busybox:1.28
      command: ['sh', '-c', 'echo "The app is running!" && tail -f /dev/null']
      volumeMounts:
        - name: config-vol
          mountPath: /etc/config
  volumes:
    - name: config-vol
      configMap:
        name: log-config
        items:
          - key: log_level
            path: log_level.conf

`log-config` ConfigMap 作为卷挂载,其 `log_level` 条目中存储的所有内容都被挂载到 Pod 的路径 `/etc/config/log_level.conf`。请注意,此路径是根据卷的 `mountPath` 和以 `log_level` 为键的 `path` 派生出来的。

downwardAPI

一个 `downwardAPI` 卷使 downward API 数据可供应用程序使用。在卷内,你可以找到以纯文本格式的只读文件形式公开的数据。

请参阅通过文件向容器暴露 Pod 信息了解更多信息。

emptyDir

对于定义了 `emptyDir` 卷的 Pod,当 Pod 分配到节点时,该卷就会被创建。顾名思义,`emptyDir` 卷最初是空的。Pod 中的所有容器都可以在 `emptyDir` 卷中读写相同的文件,尽管该卷可以在每个容器中挂载到相同或不同的路径。当 Pod 因任何原因从节点中移除时,`emptyDir` 中的数据将被永久删除。

`emptyDir` 的一些用途是:

  • 暂存空间,例如用于基于磁盘的归并排序
  • 对长时间计算进行检查点,以便从崩溃中恢复
  • 保存内容管理容器获取的文件,而 Web 服务器容器提供数据

`emptyDir.medium` 字段控制 `emptyDir` 卷的存储位置。默认情况下,`emptyDir` 卷存储在支持节点的任何介质上,例如磁盘、SSD 或网络存储,具体取决于你的环境。如果你将 `emptyDir.medium` 字段设置为 `"Memory"`,Kubernetes 将为你挂载一个 tmpfs(RAM 支持的文件系统)。虽然 tmpfs 速度非常快,但请注意,与磁盘不同,你写入的文件会计入写入它们的容器的内存限制。

可以为默认介质指定大小限制,这限制了 `emptyDir` 卷的容量。存储是从节点临时存储中分配的。如果该存储被其他来源(例如日志文件或镜像叠加层)填满,则 `emptyDir` 可能会在该限制之前耗尽容量。如果未指定大小,则内存支持的卷的大小默认为节点可分配内存。

emptyDir 配置示例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir:
      sizeLimit: 500Mi

emptyDir 内存配置示例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir:
      sizeLimit: 500Mi
      medium: Memory

fc(光纤通道)

`fc` 卷类型允许在 Pod 中挂载现有的光纤通道块存储卷。你可以在卷配置中使用参数 `targetWWNs` 指定一个或多个目标全球通用名称 (WWN)。如果指定了多个 WWN,`targetWWNs` 期望这些 WWN 来自多路径连接。

gcePersistentDisk (已弃用)

在 Kubernetes 1.34 中,树内 `gcePersistentDisk` 类型的所有操作都被重定向到 `pd.csi.storage.gke.io` CSI 驱动。

`gcePersistentDisk` 树内存储驱动在 Kubernetes v1.17 版本中被弃用,然后在 v1.28 版本中完全移除。

Kubernetes 项目建议你改用 Google Compute Engine Persistent Disk CSI 第三方存储驱动。

gitRepo (已弃用)

如果你明确启用 `GitRepoVolumeDriver` Feature Gate,则可以在集群中使用此已弃用的存储插件。

`gitRepo` 卷是卷插件的一个示例。此插件挂载一个空目录,并将 Git 仓库克隆到该目录中供 Pod 使用。

以下是一个 `gitRepo` 卷的示例:

apiVersion: v1
kind: Pod
metadata:
  name: server
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /mypath
      name: git-volume
  volumes:
  - name: git-volume
    gitRepo:
      repository: "git@somewhere:me/my-git-repository.git"
      revision: "22f1d8406d464b0c0874075539c1f2e96c253775"

glusterfs (已移除)

Kubernetes 1.34 不包含 `glusterfs` 卷类型。

GlusterFS 树内存储驱动在 Kubernetes v1.25 版本中被弃用,然后在 v1.26 版本中完全移除。

hostPath

一个 `hostPath` 卷将宿主机节点文件系统中的文件或目录挂载到你的 Pod 中。这不是大多数 Pod 所需要的,但它为某些应用程序提供了强大的应急出口。

`hostPath` 的一些用途是:

  • 运行需要访问节点级系统组件的容器(例如,一个将系统日志传输到中央位置的容器,通过只读挂载 `/var/log` 来访问这些日志)
  • 使存储在主机系统上的配置文件以只读方式供静态 Pod 使用;与普通 Pod 不同,静态 Pod 无法访问 ConfigMap

`hostPath` 卷类型

除了必需的 `path` 属性外,你还可以选择为 `hostPath` 卷指定 `type`。

`type` 可用的值为:

行为
‌""空字符串(默认)是为了向后兼容,这意味着在挂载 `hostPath` 卷之前不会执行任何检查。
DirectoryOrCreate如果给定路径不存在任何内容,则根据需要创建空目录,权限设置为 0755,与 Kubelet 具有相同的组和所有权。
Directory给定路径必须存在目录
FileOrCreate如果给定路径不存在任何内容,则根据需要创建空文件,权限设置为 0644,与 Kubelet 具有相同的组和所有权。
File给定路径必须存在文件
Socket给定路径必须存在 UNIX 套接字
CharDevice(仅限 Linux 节点)给定路径必须存在字符设备
BlockDevice(仅限 Linux 节点)给定路径必须存在块设备

底层主机上创建的一些文件或目录可能只能由 root 访问。然后,你需要将进程作为 root 在 特权容器 中运行,或者修改主机上的文件权限以从 `hostPath` 卷读取或写入。

hostPath 配置示例


---
# This manifest mounts /data/foo on the host as /foo inside the
# single container that runs within the hostpath-example-linux Pod.
#
# The mount into the container is read-only.
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-example-linux
spec:
  os: { name: linux }
  nodeSelector:
    kubernetes.io/os: linux
  containers:
  - name: example-container
    image: registry.k8s.io/test-webserver
    volumeMounts:
    - mountPath: /foo
      name: example-volume
      readOnly: true
  volumes:
  - name: example-volume
    # mount /data/foo, but only if that directory already exists
    hostPath:
      path: /data/foo # directory location on host
      type: Directory # this field is optional


---
# This manifest mounts C:\Data\foo on the host as C:\foo, inside the
# single container that runs within the hostpath-example-windows Pod.
#
# The mount into the container is read-only.
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-example-windows
spec:
  os: { name: windows }
  nodeSelector:
    kubernetes.io/os: windows
  containers:
  - name: example-container
    image: microsoft/windowsservercore:1709
    volumeMounts:
    - name: example-volume
      mountPath: "C:\\foo"
      readOnly: true
  volumes:
    # mount C:\Data\foo from the host, but only if that directory already exists
  - name: example-volume
    hostPath:
      path: "C:\\Data\\foo" # directory location on host
      type: Directory       # this field is optional

hostPath FileOrCreate 配置示例

以下清单定义了一个 Pod,它将 `/var/local/aaa` 挂载到 Pod 中的单个容器内部。如果节点上还没有路径 `/var/local/aaa`,kubelet 会将其创建为一个目录,然后将其挂载到 Pod 中。

如果 `/var/local/aaa` 已经存在但不是目录,则 Pod 会失败。此外,kubelet 会尝试在该目录内部(从主机角度看)创建一个名为 `/var/local/aaa/1.txt` 的文件;如果该路径已经存在且不是常规文件,则 Pod 会失败。

这是示例清单

apiVersion: v1
kind: Pod
metadata:
  name: test-webserver
spec:
  os: { name: linux }
  nodeSelector:
    kubernetes.io/os: linux
  containers:
  - name: test-webserver
    image: registry.k8s.io/test-webserver:latest
    volumeMounts:
    - mountPath: /var/local/aaa
      name: mydir
    - mountPath: /var/local/aaa/1.txt
      name: myfile
  volumes:
  - name: mydir
    hostPath:
      # Ensure the file directory is created.
      path: /var/local/aaa
      type: DirectoryOrCreate
  - name: myfile
    hostPath:
      path: /var/local/aaa/1.txt
      type: FileOrCreate

image

功能状态: `Kubernetes v1.33 [beta]` (默认启用:false)

`image` 卷源表示 kubelet 主机上可用的 OCI 对象(容器镜像或制品)。

使用 `image` 卷源的示例是

apiVersion: v1
kind: Pod
metadata:
  name: image-volume
spec:
  containers:
  - name: shell
    command: ["sleep", "infinity"]
    image: debian
    volumeMounts:
    - name: volume
      mountPath: /volume
  volumes:
  - name: volume
    image:
      reference: quay.io/crio/artifact:v2
      pullPolicy: IfNotPresent

卷在 Pod 启动时根据提供的 `pullPolicy` 值进行解析

Always
kubelet 总是尝试拉取引用。如果拉取失败,kubelet 会将 Pod 设置为 `Failed`。
Never
kubelet 永不拉取引用,仅使用本地镜像或制品。如果镜像的任何层尚未在本地存在,或者该镜像的清单尚未缓存,则 Pod 变为 `Failed`。
IfNotPresent
如果引用在磁盘上不存在,kubelet 会拉取。如果引用不存在且拉取失败,则 Pod 变为 `Failed`。

如果 Pod 被删除并重新创建,卷会重新解析,这意味着新的远程内容将在 Pod 重新创建时可用。在 Pod 启动期间解析或拉取镜像失败将阻止容器启动,并可能增加显著延迟。失败将使用正常的卷回退机制进行重试,并在 Pod 的原因和消息中报告。

该卷可挂载的对象类型由主机上的容器运行时实现定义。至少,它们必须包括容器镜像字段支持的所有有效类型。OCI 对象将挂载到单个目录 (`spec.containers[*].volumeMounts.mountPath`),并且将以只读方式挂载。在 Linux 上,容器运行时通常还会以禁止文件执行 (`noexec`) 的方式挂载该卷。

除此之外

  • 容器的 `subPath``subPathExpr` 挂载(`spec.containers[*].volumeMounts.[subPath,subPathExpr]`)仅在 Kubernetes v1.33 及更高版本中支持。
  • `spec.securityContext.fsGroupChangePolicy` 字段对这种卷类型没有影响。
  • `AlwaysPullImages` 准入控制器 对此卷源也像对容器镜像一样工作。

`image` 类型可用的字段如下:

reference
要使用的制品引用。例如,你可以指定 `registry.k8s.io/conformance:v1.34.0` 来从 Kubernetes 一致性测试镜像加载文件。其行为与 `pod.spec.containers[*].image` 相同。拉取 Secret 将以与容器镜像相同的方式进行组装,方法是查找节点凭据、Service Account 镜像拉取 Secret 和 Pod Spec 镜像拉取 Secret。此字段是可选的,允许更高级别的配置管理在工作负载控制器(如 Deployments 和 StatefulSets)中默认或覆盖容器镜像。有关容器镜像的更多信息
pullPolicy
拉取 OCI 对象的策略。可能的值有:`Always`、`Never` 或 `IfNotPresent`。如果指定了 `:latest` 标签,则默认为 `Always`;否则为 `IfNotPresent`。

请参阅使用带 Pod 的镜像卷示例,了解如何使用卷源的更多详细信息。

iscsi

`iscsi` 卷允许将现有 iSCSI (通过 IP 的 SCSI) 卷挂载到 Pod 中。与 Pod 移除时被擦除的 `emptyDir` 不同,`iscsi` 卷的内容会保留,并且卷只是被卸载。这意味着 iSCSI 卷可以预先填充数据,并且该数据可以在 Pod 之间共享。

iSCSI 的一个特性是可以被多个消费者同时以只读方式挂载。这意味着你可以用你的数据集预先填充一个卷,然后从你需要的尽可能多的 Pod 中并行提供它。不幸的是,iSCSI 卷只能由一个消费者以读写模式挂载。不允许同时写入。

local

`local` 卷表示一个已挂载的本地存储设备,例如磁盘、分区或目录。

本地卷只能作为静态创建的 PersistentVolume 使用。不支持动态供应。

与 `hostPath` 卷相比,`local` 卷以持久和可移植的方式使用,无需手动将 Pod 调度到节点。系统通过查看 PersistentVolume 上的节点亲和性来了解卷的节点约束。

然而,`local` 卷受底层节点的可用性限制,不适用于所有应用程序。如果节点变得不健康,那么 `local` 卷将对 Pod 不可访问。使用此卷的 Pod 将无法运行。使用 `local` 卷的应用程序必须能够容忍这种可用性降低以及潜在的数据丢失,具体取决于底层磁盘的耐久性特征。

以下示例显示了一个使用 `local` 卷和 `nodeAffinity` 的 PersistentVolume

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 100Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /mnt/disks/ssd1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - example-node

使用 `local` 卷时,必须设置 PersistentVolume `nodeAffinity`。Kubernetes 调度器使用 PersistentVolume `nodeAffinity` 将这些 Pod 调度到正确的节点。

PersistentVolume `volumeMode` 可以设置为 “Block”(而不是默认值 “Filesystem”),以将本地卷暴露为裸块设备。

使用本地卷时,建议创建一个将 `volumeBindingMode` 设置为 `WaitForFirstConsumer` 的 StorageClass。有关更多详细信息,请参见本地 StorageClass 示例。延迟卷绑定可确保 PersistentVolumeClaim 绑定决策也将与 Pod 可能拥有的任何其他节点约束(例如节点资源需求、节点选择器、Pod 亲和性和 Pod 反亲和性)一起进行评估。

可以单独运行一个外部静态供应器,以改进本地卷生命周期的管理。请注意,此供应器尚不支持动态供应。有关如何运行外部本地供应器的示例,请参见 本地卷供应器用户指南

nfs

`nfs` 卷允许将现有的 NFS (网络文件系统) 共享挂载到 Pod 中。与 Pod 移除时被擦除的 `emptyDir` 不同,`nfs` 卷的内容会保留,并且卷只是被卸载。这意味着 NFS 卷可以预先填充数据,并且该数据可以在 Pod 之间共享。NFS 可以被多个写入者同时挂载。

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /my-nfs-data
      name: test-volume
  volumes:
  - name: test-volume
    nfs:
      server: my-nfs-server.example.com
      path: /my-nfs-volume
      readOnly: true

persistentVolumeClaim

`persistentVolumeClaim` 卷用于将 PersistentVolume 挂载到 Pod 中。PersistentVolumeClaims 是一种让用户“声明”持久存储(例如 iSCSI 卷)而无需了解特定云环境细节的方式。

有关更多详细信息,请参阅 PersistentVolumes 的信息。

portworxVolume (已弃用)

功能状态: `Kubernetes v1.25 [已弃用]`

`portworxVolume` 是一个弹性块存储层,与 Kubernetes 超融合运行。Portworx 对服务器中的存储进行指纹识别,根据功能进行分层,并聚合多个服务器的容量。Portworx 在虚拟机或裸机 Linux 节点中以访客模式运行。

`portworxVolume` 可以通过 Kubernetes 动态创建,也可以预先配置并在 Pod 中引用。以下是一个引用预配置 Portworx 卷的 Pod 示例:

apiVersion: v1
kind: Pod
metadata:
  name: test-portworx-volume-pod
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /mnt
      name: pxvol
  volumes:
  - name: pxvol
    # This Portworx volume must already exist.
    portworxVolume:
      volumeID: "pxvol"
      fsType: "<fs-type>"

Portworx CSI 迁移

特性状态: Kubernetes v1.33 [stable] (默认启用:true)

在 Kubernetes 1.34 中,树内 Portworx 卷的所有操作默认都重定向到 `pxd.portworx.com` 容器存储接口 (CSI) 驱动。
Portworx CSI 驱动必须安装在集群上。

projected

投影卷将多个现有卷源映射到同一个目录中。有关更多详细信息,请参阅投影卷

rbd (已移除)

Kubernetes 1.34 不包含 `rbd` 卷类型。

Rados 块设备 (RBD) 树内存储驱动及其 CSI 迁移支持在 Kubernetes v1.28 版本中被弃用,然后在 v1.31 版本中完全移除。

secret

`secret` 卷用于将敏感信息(例如密码)传递给 Pod。你可以将 Secret 存储在 Kubernetes API 中,并将其作为文件挂载供 Pod 使用,而无需直接与 Kubernetes 耦合。`secret` 卷由 tmpfs(RAM 支持的文件系统)支持,因此它们永远不会写入非易失性存储。

有关更多详细信息,请参阅 配置 Secret

vsphereVolume (已弃用)

在 Kubernetes 1.34 中,树内 `vsphereVolume` 类型的所有操作都被重定向到 `csi.vsphere.vmware.com` CSI 驱动。

`vsphereVolume` 树内存储驱动在 Kubernetes v1.19 版本中被弃用,然后在 v1.30 版本中完全移除。

Kubernetes 项目建议你改用 vSphere CSI 第三方存储驱动。

使用 subPath

有时,在单个 Pod 中为多个用途共享一个卷很有用。`volumeMounts[*].subPath` 属性指定引用卷内部的子路径,而不是其根路径。

以下示例展示了如何使用单个共享卷配置具有 LAMP 堆栈(Linux Apache MySQL PHP)的 Pod。此 `subPath` 示例配置不建议用于生产环境。

PHP 应用程序的代码和资源映射到卷的 `html` 文件夹,MySQL 数据库存储在卷的 `mysql` 文件夹中。例如:

apiVersion: v1
kind: Pod
metadata:
  name: my-lamp-site
spec:
    containers:
    - name: mysql
      image: mysql
      env:
      - name: MYSQL_ROOT_PASSWORD
        value: "rootpasswd"
      volumeMounts:
      - mountPath: /var/lib/mysql
        name: site-data
        subPath: mysql
    - name: php
      image: php:7.0-apache
      volumeMounts:
      - mountPath: /var/www/html
        name: site-data
        subPath: html
    volumes:
    - name: site-data
      persistentVolumeClaim:
        claimName: my-lamp-site-data

使用带有扩展环境变量的 subPath

功能状态: `Kubernetes v1.17 [稳定]`

使用 `subPathExpr` 字段从 downward API 环境变量构建 `subPath` 目录名。`subPath` 和 `subPathExpr` 属性是互斥的。

在此示例中,一个 `Pod` 使用 `subPathExpr` 在 `hostPath` 卷 `/var/log/pods` 内创建一个目录 `pod1`。`hostPath` 卷从 `downwardAPI` 获取 `Pod` 名称。主机目录 `/var/log/pods/pod1` 在容器中挂载到 `/logs`。

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
  - name: container1
    env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.name
    image: busybox:1.28
    command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]
    volumeMounts:
    - name: workdir1
      mountPath: /logs
      # The variable expansion uses round brackets (not curly brackets).
      subPathExpr: $(POD_NAME)
  restartPolicy: Never
  volumes:
  - name: workdir1
    hostPath:
      path: /var/log/pods

资源

`emptyDir` 卷的存储介质(如磁盘或 SSD)由 kubelet 根目录(通常为 `/var/lib/kubelet`)所在文件系统的介质决定。`emptyDir` 或 `hostPath` 卷可以消耗的空间没有限制,容器或 Pod 之间也没有隔离。

要了解如何使用资源规范请求空间,请参阅如何管理资源

树外卷插件

树外卷插件包括 容器存储接口 (CSI),以及 FlexVolume(已弃用)。这些插件使存储供应商能够创建自定义存储插件,而无需将其插件源代码添加到 Kubernetes 仓库中。

以前,所有卷插件都是“树内”的。“树内”插件是随核心 Kubernetes 二进制文件一起构建、链接、编译和发布的。这意味着将新存储系统添加到 Kubernetes(一个卷插件)需要将代码签入核心 Kubernetes 代码仓库。

CSI 和 FlexVolume 都允许卷插件独立于 Kubernetes 代码库进行开发,并作为扩展部署(安装)在 Kubernetes 集群上。

对于希望创建树外卷插件的存储供应商,请参阅 卷插件常见问题

csi

容器存储接口 (CSI) 定义了一个标准接口,用于容器编排系统(如 Kubernetes)将其任意存储系统暴露给容器工作负载。

请阅读CSI 设计提案以获取更多信息。

在 Kubernetes 集群上部署 CSI 兼容卷驱动程序后,用户可以使用 `csi` 卷类型来挂载 CSI 驱动程序公开的卷。

`csi` 卷可以通过三种不同的方式在 Pod 中使用:

以下字段可供存储管理员配置 CSI 持久卷:

  • `driver`:一个字符串值,指定要使用的卷驱动程序的名称。此值必须与 CSI 驱动程序在 `GetPluginInfoResponse` 中返回的值相对应,如 CSI 规范中所定义。Kubernetes 使用它来识别要调用哪个 CSI 驱动程序,CSI 驱动程序组件使用它来识别哪些 PV 对象属于该 CSI 驱动程序。
  • `volumeHandle`:一个字符串值,唯一标识该卷。此值必须与 CSI 驱动程序在 `CreateVolumeResponse` 中返回的 `volume.id` 字段值相对应,如 CSI 规范中所定义。在引用该卷时,该值将作为 `volume_id` 传递给所有对 CSI 卷驱动程序的调用。
  • `readOnly`:一个可选的布尔值,指示卷是否应以只读方式“ControllerPublished”(挂载)。默认为 false。此值通过 `ControllerPublishVolumeRequest` 中的 `readonly` 字段传递给 CSI 驱动程序。
  • `fsType`:如果 PV 的 `VolumeMode` 为 `Filesystem`,则此字段可用于指定应用于挂载卷的文件系统。如果卷尚未格式化并且支持格式化,则此值将用于格式化卷。此值通过 `ControllerPublishVolumeRequest`、`NodeStageVolumeRequest` 和 `NodePublishVolumeRequest` 的 `VolumeCapability` 字段传递给 CSI 驱动程序。
  • `volumeAttributes`:一个字符串到字符串的映射,指定卷的静态属性。此映射必须与 CSI 驱动程序在 `CreateVolumeResponse` 中返回的 `volume.attributes` 字段中的映射相对应,如 CSI 规范中所定义。该映射通过 `ControllerPublishVolumeRequest`、`NodeStageVolumeRequest` 和 `NodePublishVolumeRequest` 中的 `volume_context` 字段传递给 CSI 驱动程序。
  • `controllerPublishSecretRef`:对包含敏感信息的 Secret 对象的引用,该信息将传递给 CSI 驱动程序以完成 CSI `ControllerPublishVolume` 和 `ControllerUnpublishVolume` 调用。此字段是可选的,如果不需要 Secret,则可以为空。如果 Secret 包含多个 Secret,则所有 Secret 都将被传递。
  • `nodeExpandSecretRef`:一个 Secret 对象的引用,其中包含要传递给 CSI 驱动程序的敏感信息,以完成 CSI `NodeExpandVolume` 调用。此字段是可选的,如果不需要 Secret,则可以为空。如果对象包含多个 Secret,则所有 Secret 都将被传递。当为节点发起的卷扩容配置了 Secret 数据时,kubelet 会通过 `NodeExpandVolume()` 调用将该数据传递给 CSI 驱动程序。所有支持的 Kubernetes 版本都提供 `nodeExpandSecretRef` 字段,并默认可用。Kubernetes v1.25 之前的版本不包含此支持。
  • 为每个 kube-apiserver 和每个节点上的 kubelet 启用名为 `CSINodeExpandSecret` 的功能门。从 Kubernetes 1.27 版本开始,此功能已默认启用,无需显式启用功能门。你还必须使用支持或要求在节点发起的存储大小调整操作期间使用 Secret 数据的 CSI 驱动程序。
  • `nodePublishSecretRef`:对包含敏感信息的 Secret 对象的引用,该信息将传递给 CSI 驱动程序以完成 CSI `NodePublishVolume` 调用。此字段是可选的,如果不需要 Secret,则可以为空。如果 Secret 对象包含多个 Secret,则所有 Secret 都将被传递。
  • `nodeStageSecretRef`:一个 Secret 对象的引用,其中包含要传递给 CSI 驱动程序的敏感信息,以完成 CSI `NodeStageVolume` 调用。此字段是可选的,如果不需要 Secret,则可以为空。如果 Secret 包含多个 Secret,则所有 Secret 都将被传递。

CSI 裸块卷支持

特性状态: Kubernetes v1.18 [stable]

具有外部 CSI 驱动程序的供应商可以在 Kubernetes 工作负载中实现裸块卷支持。

你可以像往常一样设置你的 PersistentVolume/PersistentVolumeClaim,支持裸块卷,无需任何 CSI 特定的更改。

CSI 临时卷

特性状态: Kubernetes v1.25 [稳定]

你可以在 Pod 规范中直接配置 CSI 卷。以这种方式指定的卷是临时的,并且在 Pod 重启后不会持久化。有关更多信息,请参阅临时卷

有关如何开发 CSI 驱动程序的更多信息,请参阅 kubernetes-csi 文档

Windows CSI 代理

特性状态:`Kubernetes v1.22 [stable]`

CSI 节点插件需要执行各种特权操作,例如扫描磁盘设备和挂载文件系统。这些操作对于每个主机操作系统都不同。对于 Linux worker 节点,容器化的 CSI 节点插件通常作为特权容器部署。对于 Windows worker 节点,容器化的 CSI 节点插件的特权操作通过 csi-proxy 支持,这是一个社区管理、独立的二进制文件,需要预先安装在每个 Windows 节点上。

有关更多详细信息,请参阅你希望部署的 CSI 插件的部署指南。

从树内插件迁移到 CSI 驱动

特性状态: Kubernetes v1.25 [稳定]

`CSIMigration` 功能将针对现有树内插件的操作重定向到相应的 CSI 插件(预计已安装和配置)。因此,当过渡到取代树内插件的 CSI 驱动程序时,操作员无需对现有存储类、PersistentVolume 或 PersistentVolumeClaim(引用树内插件)进行任何配置更改。

支持的操作和功能包括:卷的供应/删除、附加/分离、挂载/卸载和大小调整。

支持 `CSIMigration` 并已实现相应 CSI 驱动程序的树内插件列在 卷类型 中。

flexVolume (已弃用)

功能状态: `Kubernetes v1.23 [已弃用]`

FlexVolume 是一个树外插件接口,它使用基于执行的模型与存储驱动程序进行交互。FlexVolume 驱动程序二进制文件必须安装在每个节点上预定义的卷插件路径中,在某些情况下,控制平面节点也需要安装。

Pod 通过 `flexVolume` 树内卷插件与 FlexVolume 驱动程序交互。

以下 FlexVolume 插件,作为 PowerShell 脚本部署在主机上,支持 Windows 节点:

挂载传播

挂载传播允许将容器挂载的卷共享给同一 Pod 中的其他容器,甚至共享给同一节点上的其他 Pod。

卷的挂载传播由 `containers[*].volumeMounts` 中的 `mountPropagation` 字段控制。其值包括:

  • `None` - 此卷挂载不会接收主机后续挂载到此卷或其任何子目录的任何挂载。同样,容器创建的任何挂载在主机上将不可见。这是默认模式。

    此模式等同于 `mount(8)` 中描述的 `rprivate` 挂载传播。

    然而,当 `rprivate` 传播不适用时,CRI 运行时可能会选择 `rslave` 挂载传播(即 `HostToContainer`)。已知 cri-dockerd (Docker) 在挂载源包含 Docker 守护进程的根目录 (`/var/lib/docker`) 时选择 `rslave` 挂载传播。

  • `HostToContainer` - 此卷挂载将接收所有后续挂载到此卷或其任何子目录的挂载。

    换句话说,如果主机在该卷挂载点内部挂载任何内容,容器将看到它被挂载在那里。

    同样,如果任何具有 `Bidirectional` 挂载传播到同一卷的 Pod 在那里挂载任何内容,具有 `HostToContainer` 挂载传播的容器也将看到它。

    此模式等同于 `mount(8)` 中描述的 `rslave` 挂载传播。

  • `Bidirectional` - 此卷挂载的行为与 `HostToContainer` 挂载相同。此外,容器创建的所有卷挂载都将传播回主机以及使用相同卷的所有 Pod 的所有容器。

    此模式的典型用例是带有 FlexVolume 或 CSI 驱动程序的 Pod,或需要使用 `hostPath` 卷在主机上挂载内容的 Pod。

    此模式等同于 `mount(8)` 中描述的 `rshared` 挂载传播。

只读挂载

通过将 `.spec.containers[].volumeMounts[].readOnly` 字段设置为 `true`,可以将挂载设置为只读。这不会使卷本身只读,但该特定容器将无法向其写入。Pod 中的其他容器可以以读写方式挂载相同的卷。

在 Linux 上,默认情况下只读挂载并非递归只读。例如,考虑一个 Pod,它将主机上的 `/mnt` 作为 `hostPath` 卷挂载。如果 `/mnt/` 上有另一个文件系统以读写方式挂载(例如 tmpfs、NFS 或 USB 存储),则即使挂载本身被指定为只读,挂载到容器中的卷也将具有可写 `/mnt/`。

递归只读挂载

特性状态: Kubernetes v1.33 [stable] (默认启用:true)

可以通过为 kubelet 和 kube-apiserver 设置 `RecursiveReadOnlyMounts` 功能门,并为 Pod 设置 `.spec.containers[].volumeMounts[].recursiveReadOnly` 字段来启用递归只读挂载。

允许的值为:

  • `Disabled`(默认):无效果。

  • `Enabled`:使挂载递归只读。需要满足以下所有要求:

    • `readOnly` 设置为 `true`
    • `mountPropagation` 未设置,或设置为 `None`
    • 主机正在运行 Linux 内核 v5.12 或更高版本
    • CRI 级别容器运行时支持递归只读挂载
    • OCI 级别容器运行时支持递归只读挂载。

    如果上述任何一项不为真,则会失败。

  • `IfPossible`:尝试应用 `Enabled`,如果内核或运行时类不支持此功能,则回退到 `Disabled`。

示例

apiVersion: v1
kind: Pod
metadata:
  name: rro
spec:
  volumes:
    - name: mnt
      hostPath:
        # tmpfs is mounted on /mnt/tmpfs
        path: /mnt
  containers:
    - name: busybox
      image: busybox
      args: ["sleep", "infinity"]
      volumeMounts:
        # /mnt-rro/tmpfs is not writable
        - name: mnt
          mountPath: /mnt-rro
          readOnly: true
          mountPropagation: None
          recursiveReadOnly: Enabled
        # /mnt-ro/tmpfs is writable
        - name: mnt
          mountPath: /mnt-ro
          readOnly: true
        # /mnt-rw/tmpfs is writable
        - name: mnt
          mountPath: /mnt-rw

当 kubelet 和 kube-apiserver 识别此属性时,`.status.containerStatuses[].volumeMounts[].recursiveReadOnly` 字段将被设置为 `Enabled` 或 `Disabled`。

实现

已知以下容器运行时支持递归只读挂载。

CRI 级别

OCI 级别

  • runc,自 v1.1 起
  • crun,自 v1.8.6 起

下一步

请按照 使用持久卷部署 WordPress 和 MySQL 的示例操作。

本页上的项目指的是提供 Kubernetes 所需功能的第三方产品或项目。Kubernetes 项目作者不对这些第三方产品或项目负责。有关更多详细信息,请参阅 CNCF 网站指南

在提议添加额外第三方链接的更改之前,你应该阅读内容指南

最后修改于 2025 年 7 月 17 日太平洋标准时间上午 3:46:根据 KEP 1326 移除 Portworx 卷示例 (9fbaa42877)