【云原生】Kubernetes(k8s)——本地存储卷介绍与简单使用(emptyDir,hostPath,local volume)
一、概述
存储卷,简称卷,卷是pod的一部分,卷在pod创建时创建,删除pod时卷也会被销毁,卷可以为pod中的所有容器使用,前提是所有容器都将卷挂载到容器里,卷可以挂载到容器的文件系统中的任意位置。一个pod可以定义多个不同类型的卷,一个容器也可以使用不同类型的多个卷。pod需要设置卷来源(spec.volume)和挂载点(spec.containers.volumeMounts)两个信息后才可以使用相应的Volume。
几种常用的卷
emptyDir
:用于存储临时数据的简单空目录;hostPath
:用于将目录从工作节点的文件系统挂载到pod中;local volume
:Local volume 允许用户通过标准PVC接口以简单且可移植的方式访问node节点的本地存储。 PV的定义中需要包含描述节点亲和性的信息,k8s系统则使用该信息将容器调度到正确的node节点。(StorageClass local模式)CongfigMap、secret
:特殊的卷,不是用于存储数据,而是用于将配置文件公开给pod中的应用程序;
二、emptyDir
emptyDir类型的Volume在Pod分配到Node上时被创建,Kubernetes会在Node上自动分配一个目录,因此无需指定宿主机Node上对应的目录文件。 这个目录的初始内容为空,当Pod从Node上移除时,emptyDir中的数据会被永久删除。这个目录对应的是pod里的挂载目录。
【温馨提示】容器的crashing事件并不会导致emptyDir中的数据被删除。会随着Pod的结束而销毁。
使用场景:
- 临时空间,例如基于磁盘的合并排序
- 设置检查点以从崩溃事件中恢复未执行完毕的长计算
- 保存内容管理器容器从Web服务器容器提供数据时所获取的文件
【温馨提示】在使用tmpfs文件系统作为emptyDir的存储后端时,如果遇到node节点重启,则emptyDir中的数据也会全部丢失。同时,你编写的任何文件也都将计入Container的内存使用限制。
【示例】
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- image: busybox
name: test-emptydir
command: [ "sleep", "3600" ]
volumeMounts:
- mountPath: /opt/emptyDir
name: data-volume
volumes:
- name: data-volume
emptyDir: {}
可以进入到容器中查看下实际的卷挂载结果:
kubectl exec -it test-pod -c test-emptydir /bin/sh
df -h
三、hostPath
hostPath类型则是映射node文件系统中的文件或者目录到pod里,与宿主机目录映射。在使用hostPath类型的存储卷时,也可以设置type字段,支持的类型有文件、
Directory
、File
、Socket
、CharDevice
和BlockDevice
。不适用于生产环境,为什么说不适合在生产环境中使用呢?有以下节点原因:
-
由于集群内每个节点的差异化,要使用 hostPath Volume,我们需要通过NodeSelector 等方式进行精确调度,这种事情多了,你就会不耐烦了。
-
注意 Directory 和 File 两种类型的 hostPath,必须得提前创建目录或文件。
-
另外,如果 Node 上的文件或目录是由 root 创建的,挂载到容器内之后,你通常还要保证容器内进程有权限对该文件或者目录进行写入,比如你需要以 root 用户启动进程并运行于 privileged 容器,或者你需要事先修改好 Node 上的文件权限配置。
-
Scheduler 并不会考虑 hostPath volume 的大小,hostPath 也不能申明需要的 storage size,这样调度时存储的考虑,就需要人为检查并保证。
使用场景:
- 当运行的容器需要访问Docker内部结构时,如使用hostPath映射/var/lib/docker到容器;
- 当在容器中运行cAdvisor时,可以使用hostPath映射/dev/cgroups到容器中;
注意事项:
- 配置相同的pod(如通过podTemplate创建),可能在不同的Node上表现不同,因为不同节点上映射的文件内容不同;
- 当Kubernetes增加了资源敏感的调度程序,hostPath使用的资源不会被计算在内;
- 宿主机下创建的目录只有root有写权限。你需要让你的程序运行在privileged container上,或者修改宿主机上的文件权限。
volumes.hostPath.path
对应宿主机上的目录,pod不会自动创建,必须得提前手动创建目录。
【示例】
apiVersion: v1
kind: Pod
metadata:
name: test-pod2
spec:
containers:
- image: busybox
name: test-hostpath
command: [ "sleep", "3600" ]
volumeMounts:
- mountPath: /test-data
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /opt/test-hostpath
# this field is optional
type: Directory
我们登录到容器中,进入挂载的/test-data目录中,创建个测试文件。
kubectl exec -it test-pod2 -c test-hostpath /bin/sh
echo 'test' > /test-data/test.log
我们在运行该pod的node节点上,可以看到如下的文件和内容:
# 先查看pod调度到哪个node节点上
kubectl get pods -owide
在local-168-182-112节点上查看
cat /opt/test-hostpath/test.log
【温馨提示】在使用hostPath volume卷时,即便pod已经被删除了,volume卷中的数据还在!
四、emptyDir和hostPath异同
- 二者都是node节点的本地存储卷方式;
- emptyDir可以选择把数据存到tmpfs类型的本地文件系统中去,hostPath并不支持这一点;
- hostPath除了支持挂载目录外,还支持File、Socket、CharDevice和BlockDevice,既支持把已有的文件和目录挂载到容器中,也提供了“如果文件或目录不存在,就创建一个”的功能;
- emptyDir是临时存储空间,完全不提供持久化支持;
- hostPath的卷数据是持久化在node节点的文件系统中的,即便pod已经被删除了,volume卷中的数据还会留存在node节点上。
五、local volume概述(常用)
-
这是一个很新的存储类型,建议在k8s v1.10+以上的版本中使用。目前这种类型是企业里用的最多的一种方式了,结合存储类(
StorageClass
)一起使用。 -
Local volume 允许用户通过标准PVC接口以简单且可移植的方式访问node节点的本地存储。 PV的定义中需要包含描述节点亲和性的信息,k8s系统则使用该信息将容器调度到正确的node节点。
-
Local volume 就是用来解决 hostPath volume 面临的 portability, disk accounting, and scheduling 的缺陷。PV Controller 和 Scheduler 会对 local PV 做特殊的逻辑处理,以实现 Pod 使用本地存储时发生Pod re-schedule 的情况下能再次调度到 local volume 所在的 Node。
使用场景:
- 使用local-volume插件时,要求使用到了存储设备名或路径都相对固定,不会随着系统重启或增加、减少磁盘而发生变化。
- 静态provisioner配置程序仅支持发现和管理挂载点(对于Filesystem模式存储卷)或符号链接(对于块设备模式存储卷)。 对于基于本地目录的存储卷,必须将它们通过bind-mounted的方式绑定到发现目录中。
关于PV和PVC的介绍,可以参考我之前的文章:Kubernetes(k8s)Deployment、StatefulSet、DaemonSet、Job、CronJob五种控制器详解
六、StorageClass 本地存储(常用)
1)创建本地存储类(StorageClass )
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
【 注意】到这里
volumeBindingMode
字段的值是WaitForFirstConsumer
。这种bindingmode意味着:kubernetes的pv控制器会将这类pv的binding延迟,直到有一个使用了对应pvc的pod被创建出来且该pod被调度完毕。这时候才会将pv和pvc进行binding,并且这时候pv的选择会结合调度的node和pv的nodeaffinity。
- 此存储类无法动态提供存储功能,所有PV需手动创建;
volumeBindingMode: WaitForFirstConsumer
:Pod调度前不先绑定PVC与PV,而是等待Pod被调度时,这样可根据Pod资源等请求合理调度,如:selectors, affinity and anti-affinity policies;
2)创建PV
手动创建两PV:local-pv-1、local-pv-2分别绑定两主机存储,如绑定主机名的本地卷如下:
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv-1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /data/k8s-install/StorageClass-local/test
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- local-168-182-111
local-pv-2
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv-2
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
volumeMode: Filesystem
local:
path: /data/k8s-install/StorageClass-local/test
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- local-168-182-112
kubernetes.io/hostname是node的标签,这个标签是自带的,当然也可以自定义,查看标签/自定义标签:
kubectl get node -o wide --show-labels
# 自定义标签
kubectl label nodes k8s-node1 team=a
3)创建PVC
local-pvc-1
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: local-pvc-1
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: local-storage
volumeName: local-pv-1
volumeMode: Filesystem
local-pvc-2
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: local-pvc-2
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: local-storage
volumeName: local-pv-2
volumeMode: Filesystem
【温馨提示】PVC与PV是
一对一
,pod与PVC可以一对多
,volumeName如果不指定具体PV,PVC则会自动绑定空闲的PV
4)创建应用
apiVersion: extensions/v1
kind: Deployment
metadata:
labels:
app: local-volume-test
name: local-volume-test
spec:
replicas: 1
selector:
matchLabels:
app: local-volume-test
template:
metadata:
labels:
app: local-volume-test
spec:
containers:
- image: busybox
name: local-volume-test
imagePullPolicy: IfNotPresent
command: [ "/bin/sh", "-c", "while true; do sleep 2; echo $(date) $(hostname)>> /mnt/test/test.log; done" ]
volumeMounts:
- name: local-data
mountPath: /mnt/test
volumes:
- name: local-data
persistentVolumeClaim:
claimName: local-pvc-1
注意:
- 此时删除Pod,可发现其仍然被调度到本地存储
local-pv-2
所在的主机; - 删除deployment后,PVC不会与PV解绑,即第一次Pod调度后,PVC就与PV关联了;
- 删除PVC后,发现PV一直处于Released状态,导致PV无法被重用,需管理员手动删除PV并重建PV以便被重用。
【温馨提示】如果调度的是master节点且master不可调度,会导致pod起不来
5)设置master节点可调度
# 不同版本可能不一样,先查看NoSchedule的标识
kubectl describe node local-168-182-110
kubectl taint nodes local-168-182-110 node-role.kubernetes.io/control-plane:NoSchedule-
CongfigMap、secret的介绍,可以参考我之前的文章:Kubernetes(k8s)ConfigMap详解及应用
k8s的本地存储卷介绍和简单使用就先到这里了,有疑问的小伙伴欢迎给我留言哦~