10最为主流的三个存储卷 emptyDirhostPathnfs
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了10最为主流的三个存储卷 emptyDirhostPathnfs相关的知识,希望对你有一定的参考价值。
如何在pod之上添加持久化的存储能力
存储卷
存储卷基础
◼ 存储卷的基础概念
◼ 卷类型及卷插件
◼ 卷规范及基本用法
持久卷
◼ 持久卷的功用及工作逻辑
◼ PV和PVC规范及用法
◼ StorageClass及用法
Kubernetes存储架构及CSI简介
◼ 存储架构简介
◼ CSI存储组件及部署架构
◼ 示例:基于NFS的CSI
从概念上讲,存储卷是可供Pod中的所有容器访问的目录
◼ Pod规范中声明的存储卷来源决定了目录的创建方式、使用的存储介质以及目录的初始内容
◆存储卷插件(存储驱动)决定了支持的后端存储介质或存储服务,例如hostPath插件使用宿主机文件系统,而nfs插件则对接指定的NFS存储服务等
◆kubelet内置支持多种存储卷插件
◼ Pod在规范中需要指定其包含的卷以及这些卷在容器中的挂载路径
存储卷对象并非Kubernetes系统一等公民,它定义在Pod上,因而卷本身的生命周期同Pod,但其后端的存储及相关数据的生命周期通常要取决于存储介质
存储卷类型和相应的插件
In-Tree存储卷插件
临时存储卷
emptyDir #便于同一个pod的容器共享数据
节点本地存储卷
hostPath, local
网络存储卷
◆文件系统:NFS、GlusterFS、CephFS和Cinder
◆块设备:iSCSI、FC、RBD和vSphereVolume
◆存储平台:Quobyte、PortworxVolume、StorageOS和ScaleIO
◆云存储:awsElasticBlockStore、gcePersistentDisk、azureDisk和azureFile
特殊存储卷
◆Secret、ConfigMap、DownwardAPI和Projected
扩展接口
◆CSI和FlexVolume
Out-of-Tree存储卷插件
经由CSI接口扩展出的存储系统称为Out-of-Tree类的存储插件
存储卷资源规范
定义存储卷
◼ 存储卷对象并非Kubernetes系统一等公民,它需要定义在Pod上
◼ 卷本身的生命周期同Pod,但其后端的存储及相关数据的生命周期通常要取决于存储介质
存储卷的配置由两部分组成
◼ 通过.spec.volumes字段定义在Pod之上的存储卷列表,它经由特定的存储卷插件并结合特定的存储供给方的访问接口进行定义
◼ 嵌套定义在容器的volumeMounts字段上的存储卷挂载列表,
它只能挂载当前Pod对象中定义的存储卷
spec:
volumes:
- name <string> # 卷名称标识,仅可使用DNS标签格式的字符,在当前Pod中必须惟一;
VOL_TYPE <Object> # 存储卷插件及具体的目标存储供给方的相关配置;
containers:
- name: …
image: …
volumeMounts:
- name <string> # 要挂载的存储卷的名称,必须匹配存储卷列表中某项的定义;
mountPath <string> # 容器文件系统上的挂载点路径;
readOnly <boolean> # 是否挂载为只读模式,默认为否;
subPath <string> # 挂载存储卷上的一个子目录至指定的挂载点;
subPathExpr <string> # 挂载由指定的模式匹配到的存储卷的文件或目录至挂载点;
mountPropagation <string> # 挂载卷的传播模式;
实例一:emptyDir存储卷 (临时储存,共享数据,一旦删除该pod,再重建,数据就没有了)
emptyDir存储卷
◼ Pod对象上的一个临时目录
◼ 在Pod对象启动时即被创建,而在Pod对象被移除时一并被删除
◼ 通常只能用于某些特殊场景中
◆同一Pod内的多个容器间文件共享
◆作为容器数据的临时存储目录用于数据缓存系统
配置参数
◼ medium:此目录所在的存储介质的类型,可用值为“default”或“Memory”
◼ sizeLimit:当前存储卷的空间限额,默认值为nil,表示不限制
查看文档(此命令可查看到文档网址在最下面,可通过网址查看文档)
[root@K8s-master01 chapter4]#kubectl explain pods.spec.volumes.emptyDir
KIND: Pod
VERSION: v1
RESOURCE: emptyDir <Object>
DESCRIPTION:
emptyDir represents a temporary directory that shares a pods lifetime.
More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir
Represents an empty directory for a pod. Empty directory volumes support
ownership management and SELinux relabeling.
FIELDS:
medium <string>
medium represents what type of storage medium should back this directory.
The default is "" which means to use the nodes default medium. Must be an
empty string (default) or Memory. More info:
https://kubernetes.io/docs/concepts/storage/volumes#emptydir
sizeLimit <string>
sizeLimit is the total amount of local storage required for this EmptyDir
volume. The size limit is also applicable for memory medium. The maximum
usage on memory medium EmptyDir would be the minimum value between the
SizeLimit specified here and the sum of memory limits of all containers in
a pod. The default is nil which means that the limit is undefined. More
info: http://kubernetes.io/docs/user-guide/volumes#emptydir
medium 介质,既可以帮助pod在磁盘上寻找存储空间,也可以在内存上寻找存储空间。要不是默认的,要不是Menory
如果使用内存作为存储空间,要把sizelimit加上,限制最大存储值
sizeLimit 表示只允许使用多大空间
案例:使用初始化容器下载配置文件并挂载临时存储卷,然后主容器挂载临时存储卷,使用初始化容器下载的配置文件
cat volumes-emptydir-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: volumes-emptydir-demo
spec:
initContainers: #初始化容器
- name: config-file-downloader
image: ikubernetes/admin-box
imagePullPolicy: IfNotPresent
command: [/bin/sh,-c,wget -O /data/envoy.yaml #下载的文件放在了挂载的/data/目录上
https://code.aliyun.com/MageEdu/kubernetes-start/raw/master/tutorials/handson/volume-basics-01/envoy.yaml]
volumeMounts: #使用卷挂载
- name: config-file-store #卷名
mountPath: /data #挂载位置
containers:
- name: envoy
image: envoyproxy/envoy-alpine:v1.16.2
command: [/bin/sh,-c]
args: [envoy -c /etc/envoy/envoy.yaml]
volumeMounts:
- name: config-file-store #和上面初始化容器使用一个挂载卷
mountPath: /etc/envoy #但挂载位置不同,初始化容器下载的文件作为主容器所运行时使用的配置文件
readOnly: true
volumes:
- name: config-file-store
emptyDir: #每个卷插件内部有一到多个字段来配置该卷插件所对接的存储服务的属性
medium: Memory #选择内存作为存储空间
sizeLimit: 16Mi #但限制最大储存大小为16Mi
获取内置的卷插件类型 kubectl explain pods.spec.volumes
FIELDS:下对应的卷类型
手动交互式验证
生成模板
[root@K8s-master01 chapter5]#kubectl run pods-with-emptyDir-vol --image=ikubernets/admin-box:v1.2 --dry-run=client -o yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pods-with-emptyDir-vol
name: pods-with-emptyDir-vol
spec:
containers:
- image: ikubernets/admin-box:v1.2
name: pods-with-emptyDir-vol
resources:
dnsPolicy: ClusterFirst
restartPolicy: Always
status:
把生成的模板保存下来并手动配置
[root@K8s-master01 chapter5]#kubectl run pods-with-emptyDir-vol --image=ikubernets/admin-box:v1.2 --dry-run=client -o yaml > pods-with-emptyDir-vol.yaml
配置
[root@K8s-master01 chapter5]#cat pods-with-emptyDir-vol.yaml
apiVersion: v1
kind: Pod
metadata:
name: pods-with-emptydir-vol
spec:
containers:
- image: ikubernetes/admin-box:v1.2
name: admin
command: ["/bin/sh", "-c"]
args: ["sleep 99999"]
resources:
volumeMounts:
- name: data
mountPath: /data
- image: ikubernetes/demoapp:v1.0
name: demoapp
resources:
volumeMounts:
- name: data
mountPath: /var/www/html
volumes:
- name: data
emptyDir:
medium: Memory
sizeLimit: 16Mi
dnsPolicy: ClusterFirst
restartPolicy: Always
创建pod
[root@K8s-master01 chapter5]#kubectl apply -f pods-with-emptyDir-vol.yaml
pod/pods-with-emptydir-vol created
查看pod
[root@K8s-master01 chapter5]#kubectl get pods
NAME READY STATUS RESTARTS AGE
pods-with-emptydir-vol 2/2 Running 0 92s
进入admin容器的交互式窗口 -c 指定进入某容器
[root@K8s-master01 chapter5]#kubectl exec -it pods-with-emptydir-vol -c admin -- /bin/sh
root@pods-with-emptydir-vol # cd /data/
root@pods-with-emptydir-vol data# ls
root@pods-with-emptydir-vol data# touch test.txt
root@pods-with-emptydir-vol data# vi test.txt
Hi Demoapp
进入demoapp容器的交互式窗口并进入/var/www/html下查看是否有 test.txt 文件
[root@pods-with-emptydir-vol /]# cd /var/www/html/
[root@pods-with-emptydir-vol /var/www/html]# ls
test.txt
[root@pods-with-emptydir-vol /var/www/html]# cat test.txt
Hi Demoapp
追加内容到test.txt中,然后去admin下的/data下查看 test.txt
[root@pods-with-emptydir-vol /var/www/html]# echo "Hi Admin" >> test.txt
[root@pods-with-emptydir-vol /var/www/html]#cat test.txt
Hi Demoapp
Hi Admin
进入admin容器的交互式窗口
[root@K8s-master01 chapter5]#kubectl exec -it pods-with-emptydir-vol -c admin -- /bin/sh
root@pods-with-emptydir-vol # cd /data/
root@pods-with-emptydir-vol data# cat test.txt
Hi Demoapp
Hi Admin
只有共享这一个作用,一旦删除该pod,再重建,数据就没有了
删除对应的配置文件中所定义的pod
[root@K8s-master01 chapter5]#kubectl delete -f pods-with-emptyDir-vol.yaml
pod "pods-with-emptydir-vol" deleted
重建pod
[root@K8s-master01 chapter5]#kubectl apply -f pods-with-emptyDir-vol.yaml
pod/pods-with-emptydir-vol created
查看没有test.txt文件
[root@K8s-master01 chapter5]#kubectl exec -it pods-with-emptydir-vol -c admin -- /bin/sh
root@pods-with-emptydir-vol # cd /data/
root@pods-with-emptydir-vol data# ls
实例二:hostPath存储卷(持久卷,但不能跨节点进行存储,只能存储在单一节点上)
hostPath卷
◼ 将Pod所在节点上的文件系统的某目录用作存储卷
◼ 数据的生命周期与节点相同
配置参数
◼ path:指定工作节点上的目录路径,必选字段
◼ type:指定节点之上存储类型 #可以是目录和文件
查看文档(此命令可查看到文档网址在最下面,可通过网址查看文档)
[root@K8s-master01 chapter5]#kubectl explain pods.spec.volumes.hostPath
KIND: Pod
VERSION: v1
RESOURCE: hostPath <Object>
DESCRIPTION:
hostPath represents a pre-existing file or directory on the host machine
that is directly exposed to the container. This is generally used for
system agents or other privileged things that are allowed to see the host
machine. Most containers will NOT need this. More info:
https://kubernetes.io/docs/concepts/storage/volumes#hostpath
Represents a host path mapped into a pod. Host path volumes do not support
ownership management or SELinux relabeling.
FIELDS:
path <string> -required-
path of the directory on the host. If the path is a symlink, it will follow
the link to the real path. More info:
https://kubernetes.io/docs/concepts/storage/volumes#hostpath
type <string>
type for HostPath Volume Defaults to "" More info:
https://kubernetes.io/docs/concepts/storage/volumes#hostpath
volumes/hostpath/type的可取值
以下四种如果是目录事先不存在能自动创建,如果是文件事先不存在也能自动创建
DirectoryOrCreate:#指定的节点之上的路径不存在的时候,自动创建,不会报错退出,事先存在,就直接使用
Directory: #选择这项,必须要确保系统之上的路径得存在,如果不存在,pod就会报错并退出
FileOrCreate:
File:
以上四种的比较常用,对于在工作节点上选择什么路径,与以上四种类型有很大关联关系
Socket:关联socket文件
CharDevice:字符型设备文件 #在容器内部能够基于设备文件来访问设备
BlockDevice:块设备文件 #在容器内部能够基于设备文件来访问设备
范例:
[root@K8s-master01 chapter5]#cat volumes-hostpath-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: volumes-hostpath-demo
spec:
containers:
- name: filebeat #把宿主机/var/log下的所有容器日志加载进来并输出给REDIS_HOST
image: ikubernetes/filebeat:5.6.7-alpine
env:
- name: REDIS_HOST
value: redis.ilinux.io:6379
- name: LOG_LEVEL
value: info
volumeMounts:
- name: varlog
mountPath: /var/log
- name: socket
mountPath: /var/run/docker.sock
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true #只读
volumes:
- name: varlog 声明:直接把宿主机的hostPath、宿主机文件路径/var/log关联进来
hostPath: #没指类型,表示对应的Directory
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: socket
hostPath:
path: /var/run/docker.sock
手动创建新的pod
[root@K8s-master01 manifeste]#cat pod-with-hostpath-vol.yaml
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: redis:6
imagePullPolicy: IfNotPresent #本地有不下载,没有就下载
volumeMounts: #挂载一个卷
- name: redisdata #卷名
mountPath: /data #挂载路径
volumes: #指定对应的卷的声明
- name: redisdata #卷名
hostPath: #在宿主机上找路径存放
type: DirectoryOrCreate #放在目录下,目录没有就创建,有就直接使用
path: /data/redis #目录放在/data/redis
这就表示无论这个pod的redis被调度到哪个节点上,那么他就会在对应的宿主机节点上找到这个目录作为自己数据持久存储目录,如果redis的进程把自己的数据持久化都存在/data目录下,实际上就是存在于宿主机的/data/redis上
创建pod并查看
[root@K8s-master01 manifeste]#kubectl apply -f pod-with-hostpath-vol.yaml
pod/redis created
[root@K8s-master01 manifeste]#kubectl get pods
NAME READY STATUS RESTARTS AGE
redis 0/1 ContainerCreating 0 14s
查看pod运行在哪个节点上
[root@K8s-master01 manifeste]#kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis 1/1 Running 0 29s 10.244.3.29 k8s-node01 <none> <none>
查看pod的详细信息:kubectl describe pods redis
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 3m43s default-scheduler Successfully assigned default/redis to k8s-node01
Normal Pulling 3m42s kubelet Pulling image "redis:6"
Normal Pulled 3m23s kubelet Successfully pulled image "redis:6" in 18
Normal Created 3m23s kubelet Created container redis
Normal Started 3m23s kubelet Started container redis
pod被创建在k8s-node01上,去查看/data/redis下没有数据
[root@K8s-node01 ~]#cd /data/redis/ #目录原不存在,会被创建
进入redis的交互式接口,人为的生成redis数据
[root@K8s-master01 manifeste]#kubectl exec -it redis -- /bin/sh
# redis-cli #连到本地的redis server上
127.0.0.1:6379> set key mengfanchao #创建值
OK
127.0.0.1:6379> bgsave #把值存储到磁盘上
Background saving started
存完后查看本地有没有数据
# ls /data
dump.rdb
回到宿主机,查看有没有这个目录
[root@K8s-node01 redis]#ls /data/redis/
dump.rdb
发现数据存储在节点上的特定路径下
直接删除pod或基于文件删除pod,再创建一次pod,有可能还会在node01宿主机运行pod,则数据还在,调到其他节点,pod的数据就不在了
[root@K8s-master01 manifeste]#kubectl delete pod redis
pod "redis" deleted
创建pod并查看pod被调度在哪个工作节点上
[root@K8s-master01 manifeste]#kubectl apply -f pod-with-hostpath-vol.yaml
pod/redis created
[root@K8s-master01 manifeste]#kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis 1/1 Running 0 48s 10.244.3.30 k8s-node01 <none> <none>
pod还是被调度在了k8s-node01上,调度器在选择时会优先选择有目标镜像的节点
查看数据,数据保持了持久化
[root@K8s-master01 manifeste]# kubectl exec -it redis -- /bin/sh
# redis-cli
127.0.0.1:6379> get key
"mengfanchao"
实例三:NFS存储卷(网络存储)
nfs存储卷
◼ 将nfs服务器上导出(export)的文件系统用作存储卷
◼ nfs是文件系统级共享服务,它支持多路挂载请求,可由多个Pod对象同时用作存储卷后端
配置参数
◼ server <string>:NFS服务器的IP地址或主机名,必选字段
◼ path <string>:NFS服务器导出(共享)的文件系统路径,必选字段
◼ readOnly <boolean>:是否以只读方式挂载,默认为false
查看文档nfs挂载文档
[root@K8s-master01 ~]#kubectl explain pods.spec.volumes.nfs
KIND: Pod
VERSION: v1
RESOURCE: nfs <Object>
DESCRIPTION:
nfs represents an NFS mount on the host that shares a pods lifetime More
info: https://kubernetes.io/docs/concepts/storage/volumes#nfs
Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do
not support ownership management or SELinux relabeling.
FIELDS:
path <string> -required-
path that is exported by the NFS server. More info:
https://kubernetes.io/docs/concepts/storage/volumes#nfs
readOnly <boolean>
readOnly here will force the NFS export to be mounted with read-only
permissions. Defaults to false. More info:
https://kubernetes.io/docs/concepts/storage/volumes#nfs
server <string> -required-
server is the hostname or IP address of the NFS server. More info:
https://kubernetes.io/docs/concepts/storage/volumes#nfs
第一:使用server指明nfs服务器地址
第二:指明nfs挂载的路径
准备工作
第一步:
准备一个节点10.0.0.106作为nfs进行网络存储,时间配置要与kubernetes集群一致、防火墙要关闭
新装节点安装nfs服务
[root@ubuntu2004 ~]#apt -y install nfs-kernel-server
创建存储数据目录
[root@ubuntu2004 ~]#mkdir /data/redis001,2,3 -pv
mkdir: 已创建目录 /data
mkdir: 已创建目录 /data/redis001
mkdir: 已创建目录 /data/redis002
mkdir: 已创建目录 /data/redis003
指明共享目录
[root@ubuntu2004 ~]#vim /etc/exports
/data/redis001 10.0.0.0/24(rw,no_subtree_check,no_root_squash)
[root@ubuntu2004 ~]#exportfs -ar
[root@ubuntu2004 ~]#showmount -e 10.0.0.106
Export list for 10.0.0.106:
/data/redis001 10.0.0.0/24
查看nsf服务是否启动
[root@ubuntu2004 ~]#systemctl status nfs-server
查看端口2049是否开启
[root@ubuntu2004 ~]#ss -ntl
第二步:
找一kubernetes节点安装nfs客户端
[root@K8s-node03 ~]#apt -y install nfs-common
创建目录进行挂载,查看是否共享成功
[root@K8s-node03 ~]#mkdir /data
[root@K8s-node03 ~]#mount -t nfs 10.0.0.106:/data/redis001 /data
第三步:
在集群节点上的挂载目录上创建文件验证是否挂载到nfs服务器上
[root@K8s-node03 data]#touch a.txt
验证:
[root@ubuntu2004 ~]#ls /data/redis001/
a.txt
数据挂载成功,在其他集群节点上安装nfs客户端(所有节点 apt -y install nfs-common)
pod基于nfs挂载的范例
[root@K8s-master01 manifeste]#cat pod-with-nfs-vol.yaml
apiVersion: v1
kind: Pod
metadata:
name: redis-nfs
spec:
containers:
- name: redis
image: redis:6
imagePullPolicy: IfNotPresent
volumeMounts:
- name: redisdata
mountPath: /data
volumes:
- name: redisdata
nfs:
server: 10.0.0.106
path: /data/redis001
创建pod并查看
[root@K8s-master01 manifeste]#kubectl apply -f pod-with-nfs-vol.yaml
pod/redis-nfs created
[root@K8s-master01 manifeste]#kubectl get pods
NAME READY STATUS RESTARTS AGE
redis-nfs 1/1 Running 0 55s
查看redis-nfs被调度到哪个节点上去了
[root@K8s-master01 manifeste]#kubectl get pods -o wide
redis-nfs 1/1 Running 0 2m6s 10.244.5.29 k8s-node02 <none> <none>
调度到k8s-node02
交互式命令进入redis-nfspod的容器,查看并创建key
# redis-cli
127.0.0.1:6379> get key
(nil)
创建key
127.0.0.1:6379> set key mengfanchao
OK
127.0.0.1:6379> get key
"mengfanchao"
保存数据
127.0.0.1:6379> bgsave
Background saving started
查看数据
[root@K8s-master01 manifeste]#kubectl exec -it redis-nfs -- /bin/sh
# ls /data
a.txt dump.rdb
到后端nfs节点上查看该数据是否存在
[root@ubuntu2004 ~]#ls /data/redis001/
dump.rdb
即便把redis-nfs pod删除,重新创建无论被调度到哪个节点上去,只要yaml文件卷的声明是同一个nfs-server、同一个nfs-server路径,就会把数据关联到同一个nfs-server上的同一目录下。
例如:更改yaml文件pod名称,重建创建一个pod,查看是否关联到同一nfs-server上
[root@K8s-master01 manifeste]#cat pod-with-nfs-vol.yaml
apiVersion: v1
kind: Pod
metadata:
name: redis-nfs002 #修改pod名称
spec:
containers:
- name: redis
image: redis:6
imagePullPolicy: IfNotPresent
volumeMounts:
- name: redisdata
mountPath: /data
volumes:
- name: redisdata
nfs:
server: 10.0.0.106
path: /data/redis001
创建pod并查看详细信息
[root@K8s-master01 manifeste]#kubectl apply -f pod-with-nfs-vol.yaml
pod/redis-nfs002 created
[root@K8s-master01 manifeste]#kubectl get pods -o wide
redis-nfs002 1/1 Running 0 28s 10.244.3.39 k8s-node01 <none> <none>
发现redis-nfs002被调度到了k8s-node01上
由于yaml文件里定义,redis-nfs002与redis-nfs挂载的数据目录是一样的,两个pod共享同一个卷
删除redis-nfs pod,其数据不会被删除,
用交互式命令进入redis-nfs002 pod容器容,查看数据,可得到原redis-nfs pod创建的数据
[root@K8s-master01 manifeste]#kubectl exec -it redis-nfs002 -- /bin/sh
# redis-cli
127.0.0.1:6379> get key
"mengfanchao"
以上是关于10最为主流的三个存储卷 emptyDirhostPathnfs的主要内容,如果未能解决你的问题,请参考以下文章
HP-lefthand存储结构分析 / P4500存储数据恢复案例