K8S存储管理:从Volume到PV/PVC全解析
本章概述K8S存储管理按照发展的历程涉及到有Volume、PV/PVC、StorageClassVolume是最早提出的存储卷主要解决容器和数据存储的依赖关系抽象底层驱动以及支持不同的存储类型使用Volume需要了解底层存储细节因此提出了PVPersistent Volume是由k8s管理员定义的存储单元应用端使用PersistentVolumeClaims声明去调用PV存储进一步抽象了底层存储随着PV数量的增加管理员需要不停的定义PV的数量衍生了通过StorageClass动态生成PVStorageClass通过PVC中声明存储的容量会调用底层的提供商生成PV。本文介绍Volume的使用下篇文章介绍PVPVC和StorageClass。Volume 存储卷独立于容器后端和不同的存储驱动对接PV Persistent Volume持久化存储卷和node类似是一种集群资源由管理员定义对接不同的存储PVC PersistentVolumeClaims持久化存储声明和pod类似作为PV的使用者StorageClass 动态存储类型分为静态和动态两种类型通过在PVC中定义存储类型自动创建所需PV存储的概述kubernetes容器中的数据是临时的即当重启重启或crash后容器的数据将会丢失此外容器之间有共享存储的需求所以kubernetes中提供了volume存储的抽象volume后端能够支持多种不同的plugin驱动通过.spec.volumes中定义一个存储然后在容器中.spec.containers.volumeMounts调用最终在容器内部以目录的形式呈现。本地临时存储本地临时存储包括emptyDir等。emptyDir是一种临时存储pod创建的时候会在node节点上为容器申请一个临时的目录跟随容器的生命周期如容器删除emptyDir定义的临时存储空间也会随之删除容器发生意外crash则不受影响同时如果容器发生了迁移其上的数据也会丢失emptyDir一般用于测试或者缓存场景。注意一个容器崩溃了不会导致数据的丢失因为容器的崩溃并不移除pod。emptyDir 的一些用途缓存空间例如基于磁盘的归并排序。为耗时较长的计算任务提供检查点以便任务能方便地从崩溃前状态恢复执行。在 Web 服务器容器服务数据时保存内容管理器容器获取的文件。[rootk8s-master pv]# cat emptydir.yamlapiVersion: v1 kind: Pod metadata: name: test-pod-emptydir spec: containers: - image: busybox:1.27 name: test-container command:[/bin/sh,-c,sleep 600]volumeMounts: - mountPath: /cache name: cache-volume volumes: - name: cache-volume emptyDir:{}[rootk8s-master pv]# kubectl apply -f emptydir.yaml[rootk8s-master pv]# kubectl exec -it test-pod-emptydir /bin/shkubectlexec[POD][COMMAND]is DEPRECATED and will be removedina future version. Use kubectlexec[POD]--[COMMAND]instead. /# cd /cache//cache# ls/cache# echo linux a.txt/cache# cat a.txtlinux创建文件的路径可以通过如下步骤进行查找。首先查找改pod所在的node然后登陆这台node进行grep[rootk8s-master pv]# kubectl get pod -o wide[rootk8s-node1 ~]# docker inspect k8s_test-container_test-pod-emptydir_default_7a310cd3-8d93-422b-bbe1-1c405b22721f_0[rootk8s-node1 ~]# cat /var/lib/kubelet/pods/7a310cd3-8d93-422b-bbe1-1c405b22721f/volumes/kubernetes.io~empty-dir/cache-volume/a.txtlinux备注当删除完这个pod时这个文件也会自动删除的。[rootk8s-master pv]# kubectl delete pod test-pod-emptydirpodtest-pod-emptydirdeleted[rootk8s-node1 ~]# cat /var/lib/kubelet/pods/7a310cd3-8d93-422b-bbe1-1c405b22721f/volumes/kubernetes.io~empty-dir/cache-volume/a.txtcat: /var/lib/kubelet/pods/7a310cd3-8d93-422b-bbe1-1c405b22721f/volumes/kubernetes.io~empty-dir/cache-volume/a.txt: No suchfileor directoryhostPath主机存储hostPath 卷能将node宿主机节点文件系统上的文件或目录挂载到您的 Pod 中。基础主机上创建的文件或目录只能由 root 用户写入。您需要在特权容器中以 root 身份运行进程或者修改主机上的文件权限以便容器能够写入 hostPath 卷。[rootk8s-node1 ~]# mkdir /data[rootk8s-node1 ~]# ls /data/[rootk8s-master pv]# cat hostpath.ymlapiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - image: nginx name: test-container volumeMounts: - mountPath: /test-pod name: test-volume volumes: - name: test-volume hostPath: path: /data type: Directory[rootk8s-master pv]# kubectl create -f hostpath.ymlpod/nginx created[rootk8s-master pv]# kubectl get pod[rootk8s-node1 ~]# echo cloud /data/b.txt[rootk8s-master pv]# kubectl exec -it nginx /bin/bashkubectlexec[POD][COMMAND]is DEPRECATED and will be removedina future version. Use kubectlexec[POD]--[COMMAND]instead. rootnginx:/# cd /test-pod/rootnginx:/test-pod# lsb.txt持久化存储PV和PVC1.PV和PVC的引入pvPersistentVolume 持久卷 PV 是集群中的一块网络存储它独立于 Pod 存在。PV 可以是各种存储系统如云提供商的存储、NFS、iSCSI、本地存储等。 管理员负责创建 PV并配置其细节如容量、访问模式ReadWriteOnce、ReadOnlyMany、ReadWriteMany、存储类别等。 PV 有自己的生命周期它的状态包括可用Available、绑定Bound、释放Released、回收Retained等状态。 pvcPersistentVolumeClaim 持久卷声明 PVC 是对 PV 的请求它定义了 Pod 对存储的需求。在创建 Pod 时可以通过 PVC 来请求存储资源。 PVC 可以指定所需的存储容量、访问模式等参数但通常不需要指定具体的 PV而是通过标签选择器来动态匹配 PV。 PVC 的存在使得 Pod 与具体的存储实现解耦提高了可移植性。 关系 PVC 与 PV 之间是一种声明与提供的关系。PVC 声明了对存储资源的需求而 PV 则是提供这些资源的实际载体。 当 PVC 被创建时Kubernetes 会尝试将其与满足其要求的 PV 进行绑定。匹配的过程是根据 PVC 的标签选择器和 PV 的标签进行匹配只有匹配成功的 PV 才能被绑定到 PVC。一旦绑定成功Pod 可以通过 PVC 访问 PV 提供的存储资源。 如果没有合适的 PV 可以绑定PVC 将处于 Pending 状态直到有合适的 PV 可用为止。 总之PV 和 PVC 之间的关系是一种动态的匹配和绑定关系它们使得 Pod 与存储资源的具体实现解耦提高了灵活性和可移植性。2.通过NFS实现持久化存储2.1 配置nfsk8s-master nfs-serverk8s-node1 k8s-node2 nfs-client所有节点安装nfsyuminstall-ynfs-common nfs-utils在nfs-server节点创建共享目录[rootk8s-master k8s]# mkdir /nfsdata授权共享目录[rootk8s-master k8s]# chmod 666 /nfsdata编辑exports文件no_root_squash#root用户具有根目录的完全管理访问权限no_all_squash#保留共享文件的UID和GID默认sync所有数据在请求时写入共享数据实时同步[rootk8s-master k8s]# vim /etc/exports/nfsdata *(rw,no_root_squash,no_all_squash,sync)配置生效启动rpc和nfs注意顺序[rootk8s-master k8s]# systemctl start rpcbind[rootk8s-master k8s]# systemctl start nfs作为准备工作我们已经在 k8s-master 节点上搭建了一个 NFS 服务器目录为/nfsdata测试NFS挂载是否可用[rootk8s-node2 ~]# mkdir /test[rootk8s-node2 ~]# mount -t nfs 10.8.166.252:/nfsdata /test/[rootk8s-node2 ~]# df -Th|grep /test10.8.166.252:/nfsdata nfs4 19G9.9G9.0G53% /test[rootk8s-node2 ~]# touch /test/ip.txt[rootk8s-node2 ~]# ls /test/ip.txt来到nfs-server查看成功[rootk8s-master ~]# ls /nfsdata/ip.txt[rootk8s-node2 ~]# umount /test #测试完成之后就可以卸载了2.2 创建PV下面创建一个 PVmypv1配置文件nfs-pv1.yml如下[rootk8s-master ~]# vim nfs-pv1.ymlapiVersion: v1 kind: PersistentVolume metadata: name: mypv1 spec: capacity: storage: 5Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: nfs nfs: path: /nfsdata server:192.168.153.148#指定nfs目录所在的机器的地址①capacity指定 PV 的容量为 5G。②accessModes指定访问模式为ReadWriteOnce支持的访问模式有ReadWriteOnce – PV 能以 read-write 模式 mount 到单个节点。ReadOnlyMany – PV 能以 read-only 模式 mount 到多个节点。ReadWriteMany – PV 能以 read-write 模式 mount 到多个节点。③persistentVolumeReclaimPolicy指定当 PV 的回收策略为Recycle支持的策略有Retain – 当PVC被删除时与之绑定的PV不会自动删除需要手动清理。Recycle – 清除 PV 中的数据效果相当于执行rm -rf /nfsdata/*已废弃。在Kubernetes 1.17中被移除不再支持。Delete – 当PVC被删除时与之绑定的PV会被自动删除经实验删除pvc之后pv不会被删除只是状态变为failed不可用了。④storageClassName自定义存储类名称相当于为 PV 设置了一个分类。⑤ 指定 PV 在 NFS 服务器上对应的目录。创建mypv1[rootk8s-master ~]# kubectl apply -f nfs-pv1.ymlSTATUS为Available表示mypv1就绪可以被 PVC 申请/绑定。2.3 创建PVC接下来创建 PVCmypvc1配置文件nfs-pvc1.yml如下[rootk8s-master ~]# cat nfs-pvc1.ymlapiVersion: v1 kind: PersistentVolumeClaim metadata: name: mypvc1 spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi storageClassName: nfsPVC 就很简单了只需要指定 PV 的容量访问模式和 class。执行命令创建mypvc1[rootk8s-master ~]# kubectl apply -f nfs-pvc1.ymlkubectl get pvc -o yaml/json 查看实际申请空间是多少从kubectl get pvc和kubectl get pv的输出可以看到mypvc1已经 Bound 到mypv1申请成功。2.4 创建pod上面已经创建好了pv和pvcpod中直接使用这个pvc即可[rootk8s-master ~]# vim pod1.ymlapiVersion: v1 kind: Pod metadata: name: mypod1 labels: app: jiangege spec: containers: - name: mypod1 image: nginx ports: - containerPort:80volumeMounts: - mountPath:/usr/share/nginx/htmlname: mydata volumes: - name: mydata persistentVolumeClaim: claimName: mypvc1与使用普通 Volume 的格式类似在volumes中通过persistentVolumeClaim指定使用mypvc1申请的 Volume。通过命令创建mypod1[rootk8s-master ~]# kubectl apply -f pod1.yml2.5 验证[rootk8s-master ~]# kubectl exec -it mypod1 /bin/sh/# ls /usr/share/nginx/html/# echo jiangege /usr/share/nginx/html/index.html[rootk8s-master ~]# ls /nfsdata/ #也可在nfs的共享目录中查看到说明卷共享成功index.html[rootk8s-master ~]# cat /nfsdata/index.htmljiangege 可见在 Pod 中创建的文件 /usr/share/nginx/html/index.html 确实已经保存到了 NFS 服务器目录 /nfsdata中。 如果不再需要使用 PV可用删除 PVC 回收 PV。 在这里可以尝试在任何一方删除文件文件在两端都会消失3.PV的回收当 PV 不再需要时可通过删除 Pod, PVC 回收。未删除pvc之前 pv的状态是Bound删除pod[rootk8s-master pvc]# kubectl delete pod mypod1删除pvc[rootk8s-master pvc]# kubectl delete pvc mypvc1查看/nfsdata下面的内容应该还在4.PVPVC在应用在Mysql的持久化存储实战项目下面演示如何为 MySQL 数据库提供持久化存储步骤为创建 PV 和 PVC。部署 MySQL。向 MySQL 添加数据。模拟节点宕机故障Kubernetes 将 MySQL 自动迁移到其他节点。验证数据一致性。首先创建 PV 和 PVC配置如下mysql-pv.yml[rootk8s-master mysqlpv]# cat mysql-pv.ymlapiVersion: v1 kind: PersistentVolume metadata: name: mysql-pv spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: nfs nfs: path: /nfsdata/mysql-pv server:192.168.153.148[rootk8s-master mysqlpv]# kubectl apply -f mysqlpv.ymlmysql-pvc.yml[rootk8s-master mysqlpv]# cat mysql-pvc.ymlapiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi storageClassName: nfs[rootk8s-master mysqlpv]# kubectl apply -f mysql-pvc.yml接下来部署 MySQL配置文件如下[rootk8s-master mysqlpv]# cat mysqlpod.ymlapiVersion:v1kind:Servicemetadata:name:mysqlspec:ports:-port:3306selector:app:mysql---apiVersion:apps/v1kind:Deploymentmetadata:name:mysqlspec:selector:matchLabels:app:mysqltemplate:metadata:labels:app:mysqlspec:containers:-image:mysql:5.7#这里的镜像一定要选对能确保拉取到而且能使用变量name:mysqlenv:-name:MYSQL_ROOT_PASSWORDvalue:jiange2024ports:-containerPort:3306name:mysqlvolumeMounts:-name:mysql-persistent-storagemountPath:/var/lib/mysqlvolumes:-name:mysql-persistent-storagepersistentVolumeClaim:claimName:mysql-pvc[rootk8s-master mysqlpv]# kubectl apply -f mysqlpod.ymlPVCmysql-pvcBound 的 PVmysql-pv将被 mount 到 MySQL 的数据目录/var/lib/mysql。MySQL 被部署到k8s-node1① 切换到数据库 mysql。② 创建数据库表 my_id。③ 插入一条数据。④ 确认数据已经写入。关闭k8s-node1模拟节点宕机故障。[rootk8s-master mysqlpv]# kubectl exec -it mysql-6654fcb867-mqtcl /bin/bashrootmysql-6654fcb867-mqtcl:/# mysql -uroot -pjiange2024mysqlcreate database xiaofeifei;mysqlcreate table xiaofeifei.t1(id int);mysqlinsert into xiaofeifei.t1 values(2);验证数据的一致性第一种删除deploymentpvcpv然后重新创建pvpvcdeployment数据在Mysql中仍然挂载成功第二种由于node1节点已经宕机node2节点接管了这个任务,pod转移需要等待一段时间我这里等待了8分钟左右。。进入新的pod中数据仍然存在持久化成功。很安全[rootk8s-master mysqlpv]# kubectl exec -it mysql-6654fcb867-mqtcl /bin/bashrootmysql-6654fcb867-mqtcl:/# mysql -uroot -ppasswordmysqlselect* from xiaofeifei.t1;------|id|------|1||2|------2rowsinset(0.01sec)MySQL 服务恢复数据也完好无损。xiaofeifei.t1(id int);mysql insert into xiaofeifei.t1 values(2);验证数据的一致性 第一种 删除deploymentpvcpv然后重新创建pvpvcdeployment数据在Mysql中仍然挂载成功 第二种 由于node1节点已经宕机node2节点接管了这个任务,pod转移需要等待一段时间我这里等待了8分钟左右。。 [外链图片转存中...(img-gQK2t7UA-1775782968211)] shell 进入新的pod中数据仍然存在持久化成功。很安全 [rootk8s-master mysqlpv]# kubectl exec -it mysql-6654fcb867-mqtcl /bin/bash rootmysql-6654fcb867-mqtcl:/# mysql -uroot -ppassword mysql select * from xiaofeifei.t1; ------ | id | ------ | 1 | | 2 | ------ 2 rows in set (0.01 sec)MySQL 服务恢复数据也完好无损。