k8s中为什么要做持久化存储?
在 k8s 中部署的应用都是以 pod 容器的形式运行的。
假如我们部署 MySQL、Redis 等数据库,需要对 这些数据库产生的数据做备份。因为 Pod 是有生命周期的,如果 pod 不挂载数据卷,那 pod 被删除或重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到 pod 数据持久化存储。
除了希望数据不在Pod重启后丢失,有时候也需要在Pod间共享文件。因此,Kubernetes抽象出Volume对象来解决这两个问题。
容器在使用Volume时不需要关心后端存储是什么系统,对它来说,所有类型的volume都只是一个目录。
我们想要使用存储卷,需要经历如下步骤
1、定义 pod 的 volume,这个 volume 指明它要关联到哪个存储上的
2、在容器中要使用 volumemounts 挂载对应的存储 经过以上两步才能正确的使用存储卷
Volume类型
k8s中支持的卷类型比较丰富,包括:
NFS文件系统
Cephfs等分布式存储系统。
awsElasticBlockStore,azureDisk等公有云存储服务。
emptyDir,configMap,HostPath等kubernetes等内置存储类型
ISCSI,FC等....
这里主要讲的就是第四种内置存储类型。
EmptyDir
emptyDir 类型的 Volume 是在 Pod 分配到 Node 上时被创建,Kubernetes 会在 Node 上自动分配一个目录,因此无需指定宿主机 Node 上对应的目录文件。 这个目录的初始内容为空,当 Pod 从 Node 上移除时, emptyDir 中的数据会被永久删除。emptyDir Volume 主要用于某些应用程序无需永久保存的临时目录,多 个容器的共享目录等。
empttyDir是一种方便但不安全的临时存储解决方案。
创建一个使用EmptyDir的Pod
[root@master tmp]# cat empty-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: emd
spec:
containers:
- image: centos7
name: g-container
volumeMounts: //设置volume的挂载点
- mountPath: /cache
name: cache-volume
args:
- /bin/sh
- -c
- sleep 30000
volumes: //配置volume
- name: cache-volume
emptyDir: {} //默认配置
————————————————————————————————————————
volumes:
- name: cache-volume
emptyDir:
sizeLimit: 1Gi //限制emptyDir容量(1G)
更新文件,查看Pod处于running状态,(正常)。Pod创建在node1节点
[root@master tmp]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
emd 1/1 Running 0 5m20s 10.244.1.3 node1 <none> <none>
下面步骤都是在node1节点上操作的
登录node1节点,查看运行的容器。找到我们刚刚创建的容器。
[root@node1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9b55b68c67d1 centos "/bin/sh -c 'sleep 3…" 9 minutes ago Up 9 minutes k8s_g-container_emd_default_b7765e8a-60ed-4975-9089-d7c8d64d2080_0
进入容器,在emptyDir挂载目录中创建一个文件
[root@node1 ~]# docker exec -it 9b55b68c67d1 /bin/sh
sh-4.4# ls
bin cache dev etc home lib lib64 lost+found media mnt opt proc root run sbin s
sh-4.4# cd cache
sh-4.4# echo "hello.file.emptyDir" >test.txt
sh-4.4# ls
test.txt
sh-4.4# cat test.txt
hello.file.emptyDir
sh-4.4# exit
exit
使用docker inspect 查看对应容器的挂载点可以看到在容器中创建的内容主机目录也有。
[root@node1 ~]# docker inspect 9b55b68c67d1
.......
"Mounts": [ //快速定位Mounts
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/b7765e8a-60ed-4975-9089-d7c8d64d2080/volumes
"Destination": "/cache",
"Mode": "Z",
"RW": true,
"Propagation": "rprivate"
},
......
[root@node1 ~]# cd /var/lib/kubelet/pods/b7765e8a-60ed-4975-9089-d7c8d64d2080/volumes
[root@node1 volumes]# ls
kubernetes.io~empty-dir kubernetes.io~secret
[root@node1 volumes]# cd kubernetes.io~empty-dir/
[root@node1 kubernetes.io~empty-dir]# ls
cache-volume
[root@node1 kubernetes.io~empty-dir]# cd cache-volume/
[root@node1 cache-volume]# ls
test.txt
[root@node1 cache-volume]# cat test.txt
hello.file.emptyDir
[root@node1 cache-volume]#
弊端:
回到master节点删除pod
[root@master tmp]# kubectl get pods
NAME READY STATUS RESTARTS AGE
emd 1/1 Running 0 27m
[root@master tmp]# kubectl delete pods emd
pod "emd" deleted
回到node2 节点,发现文件以及目录都不见了
[root@node1 cache-volume]# cd /var/lib/kubelet/pods/b7765e8a-60ed-4975-9089-d7c8d64d2080/volumes
-bash: cd: /var/lib/kubelet/pods/b7765e8a-60ed-4975-9089-d7c8d64d2080/volumes: No such file or directory
[root@node1 cache-volume]# cd /var/lib/kubelet/pods/
[root@node1 pods]# ls
377290eb-fb56-4cc6-b0df-d5b0709a7c22 594ea3fb-cf8b-4c1a-b0da-c3bd5daca320
[root@node1 pods]#
HostPath(不建议使用)
HostPath Volume 是指 Pod 挂载宿主机上的目录或文件。 hostPath Volume 使得容器可以使用宿主机 的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在 pod 被删除,这个存储卷还是存在 的,不会被删除,
所以只要同一个 pod 被调度到同一个节点上来,在 pod 被删除重新被调度到这个节点之 后,对应的数据依然是存在的。
hostpath 存储卷缺点: 单节点 pod 删除之后重新创建必须调度到同一个 node 节点,数据才不会丢失 。
hostPath使用时需要确定节点上是否存在对象资源
创建一个使用hostPath的pod
配置与emptyDir类似
首先在master节点和node节点上创建挂载用的目录
[root@master ~]# mkdir /test-host
创建pod
[root@master tmp]# cat hostPath-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: hp-pod
spec:
containers:
- image: centos
name: hp-container
volumeMounts:
- mountPath: /hp-dir
name: hp-volume
args:
- /bin/sh
- -c
- sleep 30000
volumes:
- name: hp-volume
hostPath: #类型指定为hostPath
path: /test-host #path参数需要配置为主机上已经存在的目录
type: Directory #type指定为目录
[root@master tmp]#
看到pod创建在node1节点上
[root@master tmp]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hp-pod 1/1 Running 0 6m59s 10.244.1.4 node1 <none> <none>
[root@node1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5bba9ab5f6ea centos "/bin/sh -c 'sleep 3…" 17 minutes ago Up 17 minutes k8s_hp-container_hp-pod_default_6fd3fdb8-ff05-4705-a812-23267d569325_0
进入容器写入一个文件
[root@node1 ~]# docker exec -it 5bba9ab5f6ea /bin/sh
sh-4.4# ls /
bin etc hp-dir lib64 media opt root sbin sys usr
dev home lib lost+found mnt proc run srv tmp var
sh-4.4# cd /hp-dir
sh-4.4# echo "hello.test.hostPath">acc.txt
sh-4.4# ls
acc.txt
sh-4.4# cat acc.txt
hello.test.hostPath
sh-4.4# exit
exit
进入对应节点挂载目录,查看文件是否存在。
[root@node1 ~]# cd /test-host/
[root@node1 test-host]# ls
acc.txt
[root@node1 test-host]# cat acc.txt
hello.test.hostPath
回到master节点删除pod,在查看在pod中写入到文件是否存在
[root@master tmp]# kubectl get pods
NAME READY STATUS RESTARTS AGE
hp-pod 1/1 Running 0 27m
[root@master tmp]# kubectl delete pod hp-pod
pod "hp-pod" deleted
删除pod,发现文件没有随pod的删除而消失 。
[root@node1 /]# cd /test-host/
[root@node1 test-host]# ls
acc.txt
[root@node1 test-host]# cat acc.txt
hello.test.hostPath
[root@node1 test-host]#
PV和PVC
PV和PVC是最常见的使用存储的方式。
k8s PV 是什么?
PersistentVolume(PV)是群集中的一块存储,由管理员配置或使用存储类动态配置。 它是集群中的资源,就像 pod 是 k8s 集群资源一样。 PV 是容量插件,如 Volumes,其生命周期独立于使用 PV 的任何单个 pod。
k8s PVC 是什么?
PersistentVolumeClaim(PVC)是一个持久化存储卷,我们在创建 pod 时可以定义这个类型的存储卷。 它类似于一个 pod。 Pod 消耗节点资源,PVC 消耗 PV 资源。 Pod 可以请求特定级别的资源(CPU 和内存)。 pvc 在申请 pv 的时候也可以请求特定的大小和访问模式(例如,可以一次读写或多次只读)。
创建一个使用PV和PVC的Pod
首先配置nfs服务。
[root@master ~]# yum install nfs-utils rpcbind –y
[root@master ~]# mkdir /pv-pvc
[root@master ~]# vim /etc/exports
-bash: vim: command not found
[root@master ~]# vi /etc/exports
[root@master ~]# cat /etc/exports
/pv-pvc *(rw)
[root@master ~]# systemctl restart rpcbind
[root@master ~]# systemctl restart nfs
[root@master ~]# systemctl restart nfs-server
[root@master ~]# showmount -e 192.168.225.30 #检查nfs是否正常,IP为nfs服务器地址
Export list for 192.168.225.30:
/pv-pvc *
[root@master ~]#
进入挂载目录创建 pv1 目录
[root@master ~]# cd /pv-pvc/
[root@master pv-pvc]# mkdir pv1
[root@master pv-pvc]# ls
pv1
创建PV
[root@master tmp]# vi pv1.yaml
[root@master tmp]# kubectl apply -f pv1.yaml
persistentvolume/mypv created
[root@master tmp]# cat pv1.yaml
apiVersion: v1
kind: PersistentVolume #注意类型
metadata:
name: mypv #PVC要调用
spec:
capacity: #指定PV容量
storage: 1Gi
accessModes: #指定访问模式
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle #指定回收策略
nfs: #配置nfs服务器信息
path: /pv-pvc/pv1
server: 192.168.225.30
————————————————————————————————————————————————
accessModes
ReadWriteOnce: 该卷能够以读写模式被加载到一个节点上。
ReadOnlyMany: 该卷能够以只读模式加载到多个节点上。
ReadwriteMany: 改卷能够以读写模式加载到多个节点上。
persistentVolumeReclaimPolicy
Retain(保留) :不删除,需手动回收
Recycle(回收):基本擦除,类似rm -rf ,使它可供其他PVC申请。
Delete(删除) :关联存储将被删除。
查看PV
[root@master tmp]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv 1Gi RWO Recycle Available 22s
创建 PVC
[root@master tmp]# vi pvc1.yaml
[root@master tmp]# kubectl apply -f pvc1.yaml
persistentvolumeclaim/mypvc created
[root@master tmp]# cat pvc1.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
spec:
accessModes:
- ReadWriteOnce
volumeName: mypv
resources:
requests:
storage: 1Gi
对比查看PV的状态从Avilable变为Bound,pvc的状态也是Bound。
[root@master tmp]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc Bound mypv 1Gi RWO 47s
[root@master tmp]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mypv 1Gi RWO Recycle Bound default/mypvc 30m
创建Pod,使用该PVC
[root@master tmp]# vi testpod.yaml
[root@master tmp]# kubectl apply -f testpod.yaml
pod/pvc-pod created
[root@master tmp]# cat testpod.yaml
apiVersion: v1
kind: Pod
metadata:
name: pvc-pod
spec:
containers:
- name: pvc-pod
args:
- /bin/sh
- -c
- sleep 30000;
image: centos
volumeMounts:
- mountPath: /pv-pvc
name: pvc-volume
volumes:
- name: pvc-volume
persistentVolumeClaim:
claimName: mypvc