Overview
传统上,容器引擎(Container Engine)不提供比容器寿命更长的存储。由于容器被认为是瞬态(transient)的,这可能会导致数据丢失或复杂的外部存储选项。Kubernetes卷共享 Pod 生命周期,而不是其中的容器。如果容器终止,数据将继续可供新容器使用。
卷(volume)是一个目录,可能是预先填充的,可供 Pod 中的容器(container)使用。目录的创建、数据和内容的后端存储取决于卷类型(volume type)。截至 v1.13,有 27 种不同的卷类型,从用于访问 Ceph 的 rbd、NFS,到来自 Google 的 gcePersistentDisk 等云提供商的动态卷。每个都有特定的配置选项和依赖性。
容器存储接口 (CSI-Container Storage Interface) 的采用实现了容器container orchestration行业标准接口的目标,以允许访问任意存储系统。目前,卷插件是"树内 (in-tree)"的,这意味着它们是使用核心 Kubernetes 二进制文件编译和构建的。这个"树外(out-of-tree)"对象将允许存储供应商开发单个驱动程序并允许插件容器化。这将取代现有的 Flex 插件,后者需要提升对主机节点的访问权限,这是一个很大的安全问题。
如果您希望存储生命周期与 Pod 不同,则可以使用持久卷(Persistent Volumes)。这些允许 Pod 使用持久卷声明(Persistent Volume Claim)来声明空卷或预填充卷,使得这些volume比Pod 的寿命更长。然后,卷内的数据可以被另一个 Pod 使用,或者作为检索数据的一种方式。
已经存在两个 API 对象来向 Pod 提供数据。编码(encoded)数据可以使用 Secret 传递,非编码数据(non-encoded)可以使用 ConfigMap 传递。这些可用于传递重要数据,例如 SSH 密钥、密码,甚至是/etc/hosts等配置文件。
Introducing Volumes
Pod 规范可以声明一个或多个卷以及它们的可用位置。每个都需要名称(name)、类型(type)和安装点(a mount point)。同一卷可以供 Pod 中的多个容器使用,这可以是容器到容器通信的一种方法。一个卷可以供多个 Pod 使用,每个 Pod 都指定一个写入访问模式。没有并发检查,这意味着数据可能损坏,除非在外部进行锁定。
特定的访问模式(access mode)是 Pod 请求的一部分。作为请求,用户可能会被授予更多但不少于的访问权限,尽管首先会尝试直接匹配。集群将具有相同模式的卷分组在一起,然后按大小从最小到最大对卷进行排序。将针对该访问模式组中的每个卷检查声明,直到有足够大小的卷匹配为止。三种访问模式是:
ReadWriteOnce,允许单节点(node)读写
ReadOnlyMany,允许多个节点只读
ReadWriteMany,允许多个节点读写。
因此,同一节点上的两个 pod 可以写入 ReadWriteOnce,但不同节点上的第三个 pod 由于 FailedAttachVolume 错误而不会准备就绪。
当请求卷时,本地 kubelet 使用kubelet_pods.go脚本映射原始设备,确定并创建容器的挂载点,然后在主机节点文件系统上创建符号链接(symbolic link)以将存储与容器关联。API 服务器向StorageClass插件发出存储请求,但对后端存储的请求的具体内容取决于所使用的插件。
如果未发出对特定StorageClass 的请求,则使用的唯一参数将是访问模式和大小。该卷可以来自任何可用的存储类型,并且没有配置来确定将使用哪些可用的存储类型。
Volume Spec
可用的多种存储类型之一是emptyDir。kubelet 将在容器中创建目录,但不会挂载任何存储。创建的任何数据都会写入共享容器空间。因此,它不会是持久存储。当 Pod 被销毁时,该目录将与容器一起被删除。
bash
apiVersion: v1
kind: Pod
metadata:
name: fordpinto
namespace: default
spec:
containers:
- image: simpleapp
name: gastank
command:
- sleep
- "3600"
volumeMounts:
- mountPath: /scratch
name: scratch-volume
volumes:
- name: scratch-volume
emptyDir: {}
上面的 YAML 文件将创建一个带有单个容器的 Pod,并创建一个名为scrap-volume 的卷,这将在容器内创建/scratch目录。