Kubernetes存储卷¶
在容器环境中,容器内部的数据层( 见 Docker存储驱动 )存在以下限制(缺点):
通过存储驱动写入的数据(层)读写性能低下
容器删除或者奔溃时会导致数据丢失
特别是对于Kubernetes,将为了实现容器调度,更是将上述特性发挥到极致。当Kubernetes调度Pod到不同工作节点,会完全重建Pod,以完全干净的状态重启(重建)。为了使得数据的生命周期超越容器生命周期(容器销毁以后数据依然存在),Kubernetes也类似 Docker 卷 一样引入了卷,并且做了很多增强:
Kubernetes 支持很多类型的卷
Kubernetes卷有一个明确的lifetime概念
Kubernetes卷是在Pod中所有容器所共享的,并且容器重启不影响卷上的数据
卷的存在时间会超出 Pod 中运行的所有容器,并且在容器重新启动时数据也会得到保留
当Pod停止存在,则卷也停止存在 (卷和持久卷的差异点:卷和Pod共存亡,持久化卷则与"天地同寿"(玩笑))
临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长
Pod 可以同时使用任意数目的卷类型
卷的核心是包含一些数据的一个目录,Pod 中的容器可以访问该目录。
为了使用一个卷,Pod必须指定哪个卷提供给Pod(通过 .spec.volumes
字段)以及卷如何挂载到容器中(通过 .spec.containers[*].volumeMounts
字段)
Docker镜像是文件系统根,而卷则在镜像中挂载到指定目录:
卷不能挂载到另一个卷里面
卷中不能有硬连接到其他卷
每个Pod中容器必须和挂载的任何一个卷都没有依赖关系
Kubernetes卷类型¶
备注
我只摘录和实践我在学习和生产中所使用的Kubernetes卷,所以本文不会包含所有Kubernetes支持的卷类型。详细信息请参考官方原文 Kubernetes Types of Volumes
cephfs¶
cephfs
卷是现有的CephFS卷挂载到Pod中。和 emptyDir
不同, emptyDir
在Pod销毁的时候消失,而 cephfs
卷则始终存在,只是卸载了卷。这意味着CephFS可以预先填充数据,并且这些数据可以在不同的Pods之间共享。CephFS可以挂载成被多个写入器并发写入。
cinder¶
备注
cinder 是 OpenStack Atlas 的对象存储,对于Kubernetes需要基于 OpenStack Cloud Provider配置,请参考 Kubernetes底层云服务 中OpenStack部分。
configMap卷¶
configMap
资源提供了一种将配置数据插入Pod的方法。存储在一个 ConfigMap
对象中的数据可以被引用为类型 configMap
的卷,然后被容器化应用在Pod中使用。
当引用一个 configMap
对象,你可以简单提供在卷中的它的名字来引用。可以可以定制自定义路径来用于ConfigMap中特定入口。例如,要挂载 log-config
ConfigMap 到名为 configmap-pod
的Pod,可以配置YAML如下:
apiVersion: v1
kind: Pod
metadata:
name: configmap-pod
spec:
containers:
- name: test
image: busybox
volumeMounts:
- name: config-vol
mountPath: /etc/config
volumes:
- name: config-vol
configMap:
name: log-config
items:
- key: log_level
path: log_level
这里 log-config
ConfigMap被挂载成一个卷,所有的存储这个ConfigMap中的 log_level
入口被挂载到 Pod 的内部路径 /etc/config/log_level
。注意,这里路径是从volume的 mountPath
中获得的,并且这个路径的关键字是 log_level
。
也就是说,先定义了一个 /etc/config
的外部目录,这个卷目录被命名为 config-vol
卷 。然后配置这个卷 config-vol
,设置它的入口,也就是 /etc/config
目录的子目录 log_level
被命名为 log-config
(请注意其属于 configMap
,所以是子目录映射),这个子目录(入口) log_level
被挂载到容器内部对应的 /etc/config/log_level
。
备注
似乎 ConfigMap 在容器内外是完全一一对应的?只要指定 mountPath
,则该目录下子目录如果设定 configMap
就会从容器外映射到容器内部。
hostPath¶
hostPath
卷用于将主机节点文件系统上的文件或目录挂载到Pod中。使用 hostPath
的局限性在于:如果你要保证调度的pod不错乱(例如访问到不同节点的相同命名的hostPath卷),你需要在 PodTemplate 中设置 spec.nodeSelector
来指定服务器分配pod,这样通常可以避免错乱:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: helloworldanilhostpath
spec:
replicas: 1
template:
metadata:
labels:
run: helloworldanilhostpath
spec:
nodeSelector:
kubernetes.io/hostname: aks-nodepool1-39499429-1
volumes:
- name: task-pv-storage
hostPath:
path: /home/openapianil/samplePV
type: Directory
containers:
- name: helloworldv1
image: ***/helloworldv1:v1
ports:
- containerPort: 9123
volumeMounts:
- name: task-pv-storage
mountPath: /mnt/sample
由于 hostPath
限制较多,所以通常只用于所有node节点都一致都目录映射,并且应用可以自己避免数据错乱的场景。所以,通常使用本地存储会采用 local
持久化卷。
local¶
local
卷特点:
local
卷带包某个被挂载的本地存储设备,例如磁盘、分区或目录。local
卷只能用作静态创建的持久卷,尚不支持动态配置
local
卷适合以下工作场景:
缓存数据用来加速数据处理
分布式存储系统:在多个节点上分片或复制数据的存储集群。例如分部署数据库 Cassandra 或者分布式文件系统 Gluster 和 Ceph。
备注
hostPath
卷只支持文件和目录挂载,用于提供pod访问host中的文件和目录。
local
卷表示的是本地被挂载的存储 块 设备(dev),即本地磁盘或者分区到Pod内部
最大的不同是Kubernetes调度器知道 local
持久化卷属于那个节点;而 hostPath
卷可能会被调度器移动到另外一个节点而导致数据丢失。
我的理解:
hostPath
就是pod的一个文件或目录映射,调度器不固定记录它属于那个节点,所以会任意调度到某个节点再挂载hostPath
,而每个节点本地目录和文件是不同的,所以数据会不一致local
卷是跟着node节点的,scheduler会记录local
卷属于那个节点,不会任意调度pod,并且确保下次使用这个local
卷的pod依然调度到这个节点上。
通常我们持久化应用要使用 local
卷,只有所有服务器一致的配置文件(目录)才可以作为 hostPath
挂载到pod内部。
Kunbernetes挂载卷传播(特性)¶
Kubernetes的挂载卷有一个 mount propagation
(挂载传播)特性: 这个 mount propagation
指允许容器安装的卷共享到 同一个Pod 中的其他容器,甚至共享到 同一节点上的其他Pod 。
mount propagation
特性由 Container.volumeMounts
的 mountPropagation
字段控制:
None
: 卷挂载将不会感知到主机后续在此卷或其任何子目录上执行的挂载变化。类似的,容器所创建的卷挂载在主机上是不可兼得。 这是 默认 模式这个选项相当于
mount
命令的参数rprivate
当
rprivate
传播选项不适用是,CRI运行时可以转为选择rslave
挂载传播选项(即HostToContainer
)。当挂载源包含Docker daemon的根目录(/var/lib/docker
)时,cri-docker
(Docker)已知可以选择rslave
挂载传播选项
HostToContainer
: 卷挂载将会感知主机后续针对此卷或其任何子目录的挂载操作。也就是说,如果主机在这个挂载卷中挂载任何内容,容器内能够看到它被挂载在那里
类似的,配置了
Bidirectional
挂载传播选项的Pod如果在同一个卷上挂载了内容,则挂载传播选项为HostToContainer
的容器都能看到这个变化该模式相当于
mount
命令的参数rslave
Bidirectional
: 卷挂载和HostToContainer
挂载表现相同容器创建的卷挂载将被传播回至主机和使用同一卷的所有Pod的所有容器
该模式相当于
mount
命令的参数rshared
警告
Bidirectional
模式的挂载传播可能比较危险:
它可以破坏主机操作系统 ,所以只允许在特权容器
privileged container
中使用特权容器可以访问Host主机的内核,所以强烈建议你需要熟悉Linux内核特性
Bidirectional
模式的Pod中容器创建的任何卷挂载必须在终止时由容器销毁(卸载)
参考¶
Kubernetes 1.14: Local Persistent Volumes GA 的 "How is it different from a HostPath Volume?"