摘要
介绍在Kubernetes中使用存储的多种方式
emptyDir
HostPath
PV和PVC
目标
区分不同volume使用方式
使用emptyDir
使用HostPath
使用PV和PVC为Pod提供存储
目录
- EmptyDir
- hostPath
- PV和PVC
1. EmptyDir
1.1 Volume
volume用来解决以下2个问题:
1.文件的永久保存,避免pod重启后修改消失
2.pod间的文件共享
- 在一个Pod使用过程中,会进行很多操作,修改一些文件,安装一些程序等等。但当我们重启容器之后,会发现容器往往又回到了初始的状态,所有的修改都丢失了
- 除了希望数据不在Pod重启后丢失,我们有时候也需要在Pod间共享文件,因此,kubernetes抽象除了Volume对象来解决这两个问题
1.2 Volume类型
- Kubernetes支持的卷类型非常丰富,包括:
NFS文件系统。 (nas存储)
Cephfs等分布式存储系统。
awsElasticBlockStore,azureDisk等公有云存储服务。
emptyDir,configMap,hostPath等kubernetes内置存储类型。
ISCSI,FC等等…… (后端存储,如 san光纤存储)
1.3 EmptyDir
-
当 Pod 指定到某个节点上时,首先创建的是一个 emptyDir 卷,并且只要 Pod 在该节点上运行,卷就一直存在。 就像它的名称表示的那样,卷最初是空的。 尽管Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但是这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会永久删除。
image.png
备注,我们知道每个pod中都会有一个宿主容器-pause,pod的存储和ip都是挂载在pause上。这个emptydir 也是挂载在pause容器上。
1.4 创建一个使用EmptyDir的Pod
创建使用emptyDir时需要配置两个参数:
Spec.containers.volumeMounts: 设 置volume的挂载点。
Spec.volumes: 配置volume。
默认配置如下
[root@k8s-master ~]# cat emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: em
spec:
containers:
- image: ubuntu
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
args:
- /bin/sh
- -c
- sleep 30000
volumes:
- name: cache-volume
emptyDir: {}
[root@k8s-master ~]#
测试环境配置如下
1.创建U斑图pod
k apply -f /root/emptydir.yaml
[root@k8s-master ~]# k get pod
NAME READY STATUS RESTARTS AGE
em 1/1 Running 0 116s
2 登入pod em检查volume的内容,结果:空目录
k exec -it em -- /bin/bash
root@em:/# cd /cache
root@em:/cache# ls
root@em:/cache# exit
3.确认pod所在机器
[root@k8s-master lib]# k get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
em 1/1 Running 0 30m 10.244.1.196 k8s-node1 <none> <none>
4.登录pod所在机器,获取容器的信息,检查容器配置,获取volume的源位置
[root@k8s-node1 tmp]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
94907e01e6dc ubuntu "/bin/sh -c 'sleep 3…" 2 hours ago Up 2 hours k8s_test-container_em_default_cae37724-ab3
docker inspect 94907e01e6dc | grep -i cache -A5 -B5
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": [
"/var/lib/kubelet/pods/cae37724-ab3f-11ec-b35d-000c298dbe7d/volumes/kubernetes.io~empty-dir/cache-volume:/cache:Z",
"/var/lib/kubelet/pods/cae37724-ab3f-11ec-b35d-000c298dbe7d/volumes/kubernetes.io~secret/default-token-75t79:/var/run/secrets/kubernetes.io/serviceaccount:ro,Z",
"/var/lib/kubelet/pods/cae37724-ab3f-11ec-b35d-000c298dbe7d/etc-hosts:/etc/hosts:Z",
"/var/lib/kubelet/pods/cae37724-ab3f-11ec-b35d-000c298dbe7d/containers/test-container/42fb9dc6:/dev/termination-log:Z"
],
"ContainerIDFile": "",
--
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/cae37724-ab3f-11ec-b35d-000c298dbe7d/volumes/kubernetes.io~empty-dir/cache-volume",
"Destination": "/cache",
"Mode": "Z",
"RW": true,
"Propagation": "rprivate"
},
{
5.进入源目录,查看目录内容为空,与之前在master节点登入pod后查看的信息一致
[root@k8s-node1 tmp]# cd /var/lib/kubelet/pods/cae37724-ab3f-11ec-b35d-000c298dbe7d/volumes/kubernetes.io~empty-dir/cache-volume
[root@k8s-node1 cache-volume]# ls
6.在master节点/cache目录(volume)下创建文件gzg.txt,在node节点源目录检查文件是否落盘成功,结果落盘成功。与预期一致。
[root@k8s-master lib]# k exec -it em -- /bin/bash
root@em:/# cd /cache/
root@em:/cache# ls
root@em:/cache# touch gzg.txt
===============================
[root@k8s-node1 tmp]# cd /var/lib/kubelet/pods/cae37724-ab3f-11ec-b35d-000c298dbe7d/volumes/kubernetes.io~empty-dir/cache-volume
[root@k8s-node1 cache-volume]# ls
gzg.txt
7.删除乌班图pod,观察volume是否同步清理,预期结果源目录同步清理(删除容器不会清理,但是删除pod会同步清理)
[root@k8s-master lib]# k delete pod em
pod "em" deleted
=================
[root@k8s-node1 cache-volume]# pwd
/var/lib/kubelet/pods/cae37724-ab3f-11ec-b35d-000c298dbe7d/volumes/kubernetes.io~empty-dir/cache-volume
[root@k8s-node1 cache-volume]# ls
[root@k8s-node1 cache-volume]# cd ..
cd: 获取当前目录时出错: getcwd: 无法访问父目录: 没有那个文件或目录
1.5 EmptyDir容量限制
- emptyDir可以进行容量限制,如限制为1G
emptyDir: {}
volumes:
- name: cache-volume
emptyDir:
sizeLimit:1Gi
- 宿主主机无法看到这个容量限制
进入Pod查看分配文件夹的大小,可以看到空间是Host存储空间大小,并非1G
# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 44G 4.3G 40G 10% /
tmpfs 64M 0 64M 0% /dev
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/mapper/centos-root 44G 4.3G 40G 10% /cache
- 尝试在容器内写入一个2G的文件
# dd if=/dev/zero of=/cache/test2g bs=1M count=2048
- 再次查看容器状态发现进入Evicted状态,无法使用
[root@k8s-master probe]# kubectl get pod
NAME READY STATUS RESTARTS AGE
test-pd 0/1 Evicted 0 10m
2 HostPath
- hostPath 卷能将主机节点文件系统上的文件或目录挂载到Pod 中
- 但比如希望Pod使用一些docker引擎或系统已经包含的内部程序的时候,会使用到这种方式。如以下为kube-proxy中配置的hostPath
2.1 创建使用hostPath的Pod
2.2 HostPath的类型
- 创 建 hostPath 时 , 需 要 指 定 类 型(type)
- 如果选择类型不正确,或主机上不存在对应资源(如不存在指定文件夹),kubernetes系统将无法继续创建Pod,创建步骤终止。Pod状态长时间处于ContainerCreating中
取值 | 行为 |
---|---|
空白 | 空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。 |
DirectoryOrCreate | 如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为0755,具有与 Kubelet 相同的组和所有权。 |
Directory | 在给定路径上必须存在的目录 |
FileOrCreate | 如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 Kubelet 相同的组和所有权。 |
File | 在给定路径上必须存在的文件。 |
Socket | 在给定路径上必须存在的 UNIX 套接字 |
CharDevice | 在给定路径上必须存在的字符设备 |
BlockDevice | 在给定路径上必须存在的块设备 |
yaml:
[root@k8s-master ~]# cat hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: hppod
spec:
containers:
- image: ubuntu
name: hp-container
volumeMounts:
- mountPath: /hp-dir
name: hp-volume
args:
- /bin/sh
- -c
- sleep 30000
volumes:
- name: hp-volume
hostPath:
path: /testdir
type: DirectoryOrCreate
注:删除pod后 文件及目录并没有被一起删除
2.3 补充知识
- /etc/kubernetes/manifests 下存放的是静态pod
2.静态pod是不需要手工创建的pod,他们是kubelet自动创建的。
3.放在这个目录下的yaml文件会被自动创建。
[root@k8s-master manifests]# pwd
/etc/kubernetes/manifests
[root@k8s-master manifests]# ls
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
[root@k8s-master manifests]#
3 PV和PVC
PersistentVolume(pv)和PersistentVolumeClaim(pvc)是k8s提供的两种API资源
PV是集群中由管理员配置的一块存储空间,PV是卷插件,和之前介绍的volumes类似,但它有一个独立于单个Pod的生命周期。PV的后端可以是NFS,iSCSI或者云存储等。
PVC是用户的存储请求,PVC可以请求PV特定的接入模式(读写等)和大小
3.1 创建pv
ReadWriteOnce:简写RWO
ReadOnlyMany:简写ROX
ReadWriteMant:简写RWX
3.2 创建pvc
注意:根据存储大小选择最匹配的pv ,如申请100g,pv有200g,会选200g的pv,但是没有用到的100g空间无法使用,且无法被别人使用。
3.3 实现示例
-
准备nfs存储,master作为nfs的server,node作为nfs的client端
1 安装2个软件 nfs-utils 和 rpcbind(master执行)
yum install nfs-utils rpcbind -y
2创建5个目录备用
mkdir -p /nfs/pv{1..5}
3 设置nfs的配置文件
[root@k8s-master nfs]# cat /etc/exports
/nfs *(rw,sync)
4 设置开机自动启动
systemctl enable nfs
systemctl start nfs
systemctl status nfs
5检查nfs状态
[root@k8s-master nfs]# showmount -e
Export list for k8s-master:
/nfs *
6 在node1 node2 上重复执行 步骤 1 步骤4
7 执行完成后验证
[root@k8s-node1 ~]# showmount -e k8s-master
Export list for k8s-master:
/nfs *
-
配置pv
1 获取maser的ip地址
192.168.227.12
2 编写yaml文件
[root@k8s-master ~]# cat pv1.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mypv1
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /nfs/pv1
server: 192.168.227.10
3.创建pv1(在master上1G)
[root@k8s-master ~]# k apply -f /root/pv1.yaml
persistentvolume/mypv1 created
[root@k8s-master ~]# k get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv1 1Gi RWO Recycle Available 29s
4同理创建pv2(在master上2G)
[root@k8s-master ~]# k apply -f pv2.yaml
persistentvolume/mypv2 created
[root@k8s-master ~]# k get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv1 1Gi RWO Recycle Available 28m
mypv2 2Gi RWO Recycle Available 3s
- 建立pvc调用pv(pvc不指定pv,仅通过大小,由系统自动选择,预期结果,绑定在pv1上)
[root@k8s-master ~]# cat pvc1.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
- 创建pvc和检查挂载情况
[root@k8s-master ~]# k apply -f pvc1.yaml
persistentvolumeclaim/mypvc created
[root@k8s-master ~]# k get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc Bound mypv1 1Gi RWO 4s
[root@k8s-master ~]# k get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv1 1Gi RWO Recycle Bound default/mypvc 37m
mypv2 2Gi RWO Recycle Available 9m39s
pvc创建成功后,绑定在了pv1上
- 创建pod调用pvc实现对pv的管理(实现了pod 与存储的解耦)
yaml文件
apiVersion: v1
kind: Pod
metadata:
labels:
test: pvctest
name: pvcpod
spec:
containers:
- name: busybox
args:
- /bin/sh
- -c
- sleep 30000;
image: busybox
volumeMounts:
- mountPath: /pvcdir
name: pvc-volume
volumes:
- name: pvc-volume
persistentVolumeClaim:
claimName: mypvc
创建和检查
[root@k8s-master ~]# k apply -f pvpod.yaml
pod/pvcpod created
[root@k8s-master ~]# k get pod
NAME READY STATUS RESTARTS AGE
httpd-deployment-859778b7b6-5rwrn 1/1 Running 16 38d
httpd-deployment-859778b7b6-hfw9t 1/1 Running 16 38d
httpd-deployment-859778b7b6-l7vwd 1/1 Running 15 37d
pvcpod 1/1 Running 0 23s
在pod中创建个文件,观察宿主主机(master)中是否建立
#给宿主主机的存储目录授权
chmod -R 777 /nfs/pv{1..5}
#进入pod
k exec -it pvcpod -- /bin/sh
#在挂载目录创建文件,并登出pod
/ # cd /pvcdir
/pvcdir # touch abc.abc
/pvcdir # exit
#进入宿主主机的存储目录,观察abc.abc是否存在
[root@k8s-master /]# cd /nfs
[root@k8s-master nfs]# cd pv1
[root@k8s-master pv1]# ls
abc.abc
删除pod观察 pvc、pv、及其上的数据是否存在(persistentVolumeReclaimPolicy: Recycle)
结果:pv pvc 和数据均存在未删除
[root@k8s-master ~]# k delete pods pvcpod
pod "pvcpod" deleted
[root@k8s-master ~]# k get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv1 1Gi RWO Recycle Bound default/mypvc 91m
mypv2 2Gi RWO Recycle Available 62m
[root@k8s-master ~]# k get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc Bound mypv1 1Gi RWO 54m
[root@k8s-master ~]# cd /nfs/pv1
[root@k8s-master pv1]# ls
abc.abc
再次建立pod,使用mypvc,建立后观察该pod是否能看到abc.abc文件
结果:可以看到abc.abc文件
结论:pv pvc recycle属性可以持久化保留数据。
k apply -f /root/pvpod.yaml
k get pod
k exec -it pvcpod -- /bin/sh
/pvcdir # ls
abc.abc
删除pvc观察pv状态 和数据
删除pvc前要删除关联的pod,否则删除pvc的命令会hung住
结论:
1. recycle属性,删除pvc是通过一个临时的pod完成的,叫recycler-for-mypv1
[root@k8s-master ~]# k get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc Bound mypv1 1Gi RWO 4d1h
[root@k8s-master ~]# k delete pvc mypvc;k get pods -w
persistentvolumeclaim "mypvc" deleted
NAME READY STATUS RESTARTS AGE
httpd-deployment-859778b7b6-5rwrn 1/1 Running 18 42d
httpd-deployment-859778b7b6-hfw9t 1/1 Running 18 42d
httpd-deployment-859778b7b6-l7vwd 1/1 Running 17 41d
recycler-for-mypv1 0/1 ContainerCreating 0 0s
NAME AGE
recycler-for-mypv1 18s
recycler-for-mypv1 18s
recycler-for-mypv1 18s
2.pvc删除成功
[root@k8s-master ~]# k get pvc
No resources found in default namespace.
3.pv的状态恢复为avalible
[root@k8s-master ~]# k get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv1 1Gi RWO Recycle Available 4d1h
4.存储的目录依然存在
[root@k8s-master ~]# cd /nfs/
[root@k8s-master nfs]# ls
pv1 pv2 pv3 pv4 pv5
5.存储的内容被删除
[root@k8s-master nfs]# cd pv1
[root@k8s-master pv1]# ls
[root@k8s-master pv1]#
4 pv与pvc的状态
5 PV回收
- 由于创建时选择的回收策略是Recycle,删除PVC的时候kubernetes会删除原有PV的数据。它采用的方式是创建一个回收专用Pod来完成这一操作
[root@k8s-master volume]# kubectl delete pvc mypvc && kubectl get pod
persistentvolumeclaim "mypvc" deleted
NAME READY STATUS RESTARTS AGE
recycler-for-mypv 0/1 ContainerCreating 0 0s
- 如果不希望数据被删除,可以配置回收策略为Retain,这样在删除PVC后,PV的数据仍然存在,PV状态如下
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM REASON AGE
mypv 1Gi RWO Retain Released default/mypvc 19s
- 数据未被删除,但由于PV处于Released状态,依然无法直接被PVC使用。这是由
于PV保存了之前关联的PVC状态,如需关联新PVC,需要删除其中ClaimRef参数
6 PV和PVC的绑定
- 如果我们重复PV和PVC的实验,将PVC中volumeName:mypv这一参数删除可以发现PVC仍能申请到PV。那么PVC是以什么机制找到匹配的PV呢?
PVC首先根据筛选条件,如容量大小和访问模式筛选掉不符合条件的PV。
筛选掉不符合volumeName的PV。在前文的实验实例中我们使用的是这个方式。
筛选掉不符合StorageClass的PV。 根据其他条件筛选符合的PV。
7 pv与pvc示例
1 使用storageclass的模式,创建pv和pvc,观察pvc的状态
pvc中不指定pv的名字,大小,只指定storageclass
结果:
pvc使用了pv-sc这个pv,关联了storageclass
------------------------------------------------------------
[root@k8s-master ~]# cat ~/pv-sc.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-sc
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: highio
nfs:
path: /nfs/pv3
server: 192.168.227.10
[root@k8s-master ~]# k apply -f /root/pv-sc.yaml
persistentvolume/pv-sc created
[root@k8s-master ~]# k get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-sc 1Gi RWO Recycle Available highio 2s
--------------------------------------------
2 创建pvc
--------------------------------------------
[root@k8s-master ~]# cat ~/pvc2.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-sc
spec:
accessModes:
- ReadWriteOnce
storageClassName: highio
resources:
requests:
storage: 1Gi
[root@k8s-master ~]# k apply -f pvc2.yaml
persistentvolumeclaim/pvc-sc created
[root@k8s-master ~]# k get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-sc Bound pv-sc 1Gi RWO highio 9s
8 动态卷供给(动态分配存储,对用户无感)
1) pv的回收机制采用Delete机制
3)插件部署(只描述流程)
将插件的多个yaml文件进行生效配置(k apply -f *.yaml)
包括class.yaml deployment.yaml rbac.yaml 3个
注意根据测试环境进行客户化(deployment.yaml)
结果:
1)创建pvc时,系统会自动创建pv,并且会创建系统底层的存储目录
2)删除pvc时,系统会自动删除关联的pv 和 底层的存储目录
以上2点实现的存储的自动供给和回收对管理对用户透明