容器内部存储的生命周期是短暂的,会随着容器环境的销毁而销毁,具有不稳定性。如果多个容器希望共享同一份存储,则仅仅依赖容器本身是很难实现的。在Kubernetes系统中,将对容器应用所需的存储资源抽象为存储卷(Volume)概念来解决这些问题。
Volume是与Pod绑定的(独立于容器)与Pod具有相同生命周期的资源对象。我们可以将Volume的内容理解为目录或文件,容器如需使用某个Volume,则仅需设置volumeMounts将一个或多个Volume挂载为容器中的目录或文件,即可访问Volume中的数据。Volume具体是什么类型,以及由哪个系统提供,对容器应用来说是透明的。
Kubernetes目前支持的Volume类型包括Kubernetes的内部资源对象类型、开源共享存储类型、存储厂商提供的硬件存储设备和公有云提供的存储等。
将Kubernetes特定类型的资源对象映射为目录或文件,包括以下类型的资源对象。
◎ ConfigMap:应用配置。
◎ Secret:加密数据。
◎ DownwardAPI:Pod或Container的元数据信息。
◎ ServiceAccountToken:Service Account中的token数据。
◎ Projected Volume:一种特殊的存储卷类型,用于将一个或多个上述资源对象一次性挂载到容器内的同一个目录下。
Kubernetes管理的宿主机本地存储类型如下。
◎ EmptyDir:临时存储。
◎ HostPath:宿主机目录。
持久化存储(PV)和网络共享存储类型如下。
◎ CephFS:一种开源共享存储系统。
◎ Cinder:一种开源共享存储系统。
◎ CSI:容器存储接口(由存储提供商提供驱动程序和存储管理程序)。
◎ FC(Fibre Channel):光纤存储设备。
◎ FlexVolume:一种基于插件式驱动的存储。
◎ Flocker:一种开源共享存储系统。
◎ Glusterfs:一种开源共享存储系统。
◎ iSCSI:iSCSI存储设备。
◎ Local:本地持久化存储。
◎ NFS:网络文件系统。
◎ PersistentVolumeClaim:简称PVC,持久化存储的申请空间。
◎ Portworx Volumes:Portworx提供的存储服务。
◎ Quobyte Volumes:Quobyte提供的存储服务。
◎ RBD(Ceph Block Device):Ceph块存储。
存储厂商提供的存储卷类型如下。
◎ ScaleIO Volumes:DellEMC的存储设备。
◎ StorageOS:StorageOS提供的存储服务。
◎ VsphereVolume:VMWare提供的存储系统。
公有云提供的存储卷类型如下。
◎ AWSElasticBlockStore:AWS公有云提供的Elastic Block Store。
◎ AzureDisk:Azure公有云提供的Disk。
◎ AzureFile:Azure公有云提供的File。
◎ GCEPersistentDisk:GCE公有云提供的Persistent Disk。
在Kubernetes中有一些资源对象可以以存储卷的形式挂载为容器内的目录或文件,目前包括ConfigMap、Secret、Downward API、ServiceAccountToken、Projected Volume。下面对这几种类型如何以存储卷的形式使用进行说明。
ConfigMap主要保存应用程序所需的配置文件,并且通过Volume形式挂载到容器内的文件系统中,供容器内的应用程序读取。
例如,一个包含两个配置文件的ConfigMap资源如下:


在Pod的YAML配置中,可以将ConfigMap设置为一个Volume,然后在容器中通过volumeMounts将ConfigMap类型的Volume挂载到/configfiles目录下:

在Pod成功创建之后,进入容器内查看,可以看到在/configfiles目录下存在server.xml和logging.properties文件:


ConfigMap中的配置内容如果是UTF-8编码的字符,则将被系统认为是文本文件。如果是其他字符,则系统将以二进制数据格式进行保存(设置为binaryData字段)。
关于Pod如何使用ConfigMap的详细说明请参见3.5节的说明。
假设在Kubernetes中已经存在如下Secret资源:

与ConfigMap的用法类似,在Pod的YAML配置中可以将Secret设置为一个Volume,然后在容器内通过volumeMounts将Secret类型的Volume挂载到/etc/foo目录下:


关于Secret的详细说明请参见6.5节。
通过Downward API可以将Pod或Container的某些元数据信息(例如Pod名称、Pod IP、Node IP、Label、Annotation、容器资源限制等)以文件的形式挂载到容器内,供容器内的应用使用。下面是一个将Pod的标签通过Downward API挂载为容器内文件的示例:


关于Downward API的更详细说明请参见3.6节。
Projected Volume是一种特殊的存储卷类型,用于将一个或多个上述资源对象(ConfigMap、Secret、Downward API)一次性挂载到容器内的同一个目录下。
从上面的几个示例来看,如果Pod希望同时挂载ConfigMap、Secret、Downward API,则需要设置多个不同类型的Volume,再将每个Volume都挂载为容器内的目录或文件。如果应用程序希望将配置文件和密钥文件放在容器内的同一个目录下,则通过多个Volume就无法实现了。为了支持这种需求,Kubernetes引入了一种新的Projected Volume存储卷类型,用于将多种配置类数据通过单个Volume挂载到容器内的单个目录下。
Projected Volume的一些常见应用场景如下。
◎ 通过Pod的标签生成不同的配置文件,需要使用配置文件,以及用户名和密码,这时需要使用3种资源:ConfigMap、Secrets、Downward API。
◎ 在自动化运维应用中使用配置文件和账号信息时,需要使用ConfigMap、Secrets。
◎ 在配置文件内使用Pod名称(metadata.name)记录日志时,需要使用ConfigMap、Downward API。
◎ 使用某个Secret对Pod所在命名空间(metadata.namespace)进行加密时,需要使用Secret、Downward API。
Projected Volume在Pod的Volume定义中类型为projected,通过sources字段设置一个或多个ConfigMap、Secret、DownwardAPI、ServiceAccountToken资源。各种类型的资源的配置内容与被单独设置为Volume时基本一样,但有两个不同点。
◎ 对于Secret类型的Volume,字段名“secretName”在projected.sources.secret中被改为“name”。
◎ Volume的挂载模式“defaultMode”仅可以设置在projected级别,对于各子项,仍然可以设置各自的挂载模式,使用的字段名为“mode”。
此外,Kubernetes从1.11版本开始引入对ServiceAccountToken的挂载支持,在1.12版本时达到Beta阶段。ServiceAccountToken通常用于容器内应用访问API Server鉴权的场景中。
下面是一个使用Projected Volume挂载ConfigMap、Secret、Downward API共3种资源的示例:


下面是一个使用Projected Volume挂载两个Secret资源,其中一个设置了非默认挂载模式(mode)的示例:

下面是一个使用Projected Volume挂载ServiceAccountToken的示例:


对于ServiceAccountToken类型的Volume,可以设置Token的audience、expirationSeconds、path等属性信息。
◎ audience:预期受众的名称。Token的接收者必须使用其中的audience标识符来标识自己,否则应该拒绝该Token。该字段是可选的,默认为API Server的标识符“api”。
◎ expirationSeconds:Service Account Token的过期时间,默认为1h,至少为10min(600s)。管理员可以通过kube-apiserver的启动参数--service-account-max-token-expiration限制Token的最长有效时间。
◎ path:挂载目录下的相对路径。
关于Service Account概念和应用的详细说明请参见6.4节。
Kubernetes管理的Node本地存储卷(Volume)的类型如下。
◎ EmptyDir:与Pod同生命周期的Node临时存储。
◎ HostPath:Node目录。
◎ Local:基于持久卷(PV)管理的Node目录,详见下节的说明。
下面对这几种类型如何以存储卷的形式使用进行说明。
这种类型的Volume将在Pod被调度到Node时进行创建,在初始状态下目录中是空的,所以命名为“空目录”(Empty Directory),它与Pod具有相同的生命周期,当Pod被销毁时,Node上相应的目录也会被删除。同一个Pod中的多个容器都可以挂载这种Volume。
由于EmptyDir类型的存储卷的临时性特点,它通常可以用于以下应用场景中。
◎ 基于磁盘进行合并排序操作时需要的暂存空间。
◎ 长时间计算任务的中间检查点文件。
◎ 为某个Web服务提供的临时网站内容文件。
在默认情况下,kubelet会在Node的工作目录下为Pod创建EmptyDir目录,这个目录的存储介质可能是本地磁盘、SSD磁盘或者网络存储设备,取决于环境的配置。
另外,EmptyDir可以通过medium字段设置存储介质为“Memory”,表示使用基于内存的文件系统(tmpfs、RAM-backed filesystem)。虽然tmpfs的读写速度非常快,但与磁盘中的目录不同,在主机重启之后,tmpfs的内容就会被清空。此外,写入tmpfs的数据将被统计为容器的内存使用量,受到容器级别内存资源上限(Memory Resource Limit)的限制。
下面是使用EmptyDir类型的存储卷的Pod的YAML配置示例,该类型的存储卷的参数只有一对花括号“{}”:

HostPath类型的存储卷用于将Node文件系统的目录或文件挂载到容器内部使用。对于大多数容器应用来说,都不需要使用宿主机的文件系统。适合使用HostPath存储卷的一些应用场景如下。
◎ 容器应用的关键数据需要被持久化到宿主机上。
◎ 需要使用Docker中的某些内部数据,可以将主机的/var/lib/docker目录挂载到容器内。
◎ 监控系统,例如cAdvisor需要采集宿主机/sys目录下的内容。
◎ Pod的启动依赖于宿主机上的某个目录或文件就绪的场景。
HostPath存储卷的主要配置参数为path,设置为宿主机的目录或文件路径;还可以设置一个可选的参数type,表示宿主机路径的类型。目前支持的type配置参数和校验规则如表8.1所示。
表8.1 HostPath的type配置参数和校验规则

由于HostPath使用的是宿主机的文件系统,所以在使用时有以下注意事项。
◎ 对于具有相同HostPath设置的多个Pod(例如通过podTemplate创建的)来说,可能会被Master调度到多个Node上运行,但如果多个Node上HostPath中的文件内容(例如是配置文件)不同,则各Pod应用的运行可能出现不同的结果。
◎ 如果管理员设置了基于存储资源情况的调度策略,则HostPath目录下的磁盘空间将无法计入Node的可用资源范围内,可能出现与预期不同的调度结果。
◎ 如果是之前不存在的路径,则由kubelet自动创建出来的目录或文件的owner将是root,这意味着如果容器内的运行用户(User)不是root,则将无法对该目录进行写操作,除非将容器设置为特权模式(Privileged),或者由管理员修改HostPath的权限以使得非root用户可写。
◎ HostPath设置的宿主机目录或文件不会随着Pod的销毁而删除,在Pod不再存在之后,需要由管理员手工删除。
下面是使用HostPath类型的存储卷的Pod的YAML配置示例,其中将宿主机的/data目录挂载为容器内的/host-data目录:

对于type为FileOrCreate模式的情况,需要注意的是,如果挂载文件有上层目录,则系统不会自动创建上层目录,当上层目录不存在时,Pod将启动失败。在这种情况下,可以将上层目录也设置为一个hostPath类型的Volume,并且设置type为DirectoryOrCreate,确保目录不存在时,系统会将该目录自动创建出来。
下面是FileOrCreate的Pod示例,其中预先创建了文件的上层目录:

