K8S存储管理:Volume、PV/PVC与StorageClass详解

本章概述

K8S存储管理按照发展的历程,涉及到有Volume、PV/PVC、StorageClass,Volume是最早提出的存储卷,主要解决容器和数据存储的依赖关系,抽象底层驱动以及支持不同的存储类型,使用Volume需要了解底层存储细节,因此提出了PV,Persistent Volume是由k8s管理员定义的存储单元,应用端使用PersistentVolumeClaims声明去调用PV存储,进一步抽象了底层存储;随着PV数量的增加,管理员需要不停的定义PV的数量,衍生了通过StorageClass动态生成PV,StorageClass通过PVC中声明存储的容量,会调用底层的提供商生成PV。本文介绍Volume的使用,下篇文章介绍PV,PVC和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 服务器容器服务数据时,保存内容管理器容器获取的文件。

shell 复制代码
[root@k8s-master pv]# cat emptydir.yaml 
apiVersion: 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: {}
[root@k8s-master pv]# kubectl apply -f emptydir.yaml 
shell 复制代码
[root@k8s-master pv]# kubectl exec -it test-pod-emptydir /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # cd /cache/
/cache # ls
/cache # echo "linux" > a.txt
/cache # cat a.txt 
linux

创建文件的路径可以通过如下步骤进行查找。首先查找改pod所在的node,然后登陆这台node,进行grep

shell 复制代码
[root@k8s-master pv]# kubectl get pod -o wide
shell 复制代码
[root@k8s-node1 ~]# docker inspect k8s_test-container_test-pod-emptydir_default_7a310cd3-8d93-422b-bbe1-1c405b22721f_0
shell 复制代码
[root@k8s-node1 ~]# cat /var/lib/kubelet/pods/7a310cd3-8d93-422b-bbe1-1c405b22721f/volumes/kubernetes.io~empty-dir/cache-volume/a.txt 
linux

备注:当删除完这个pod时,这个文件也会自动删除的。

shell 复制代码
[root@k8s-master pv]# kubectl delete pod test-pod-emptydir
pod "test-pod-emptydir" deleted
[root@k8s-node1 ~]# cat /var/lib/kubelet/pods/7a310cd3-8d93-422b-bbe1-1c405b22721f/volumes/kubernetes.io~empty-dir/cache-volume/a.txt 
cat: /var/lib/kubelet/pods/7a310cd3-8d93-422b-bbe1-1c405b22721f/volumes/kubernetes.io~empty-dir/cache-volume/a.txt: No such file or directory

hostPath主机存储

hostPath 卷能将node宿主机节点文件系统上的文件或目录挂载到您的 Pod 中。

基础主机上创建的文件或目录只能由 root 用户写入。您需要在特权容器中以 root 身份运行进程,或者修改主机上的文件权限以便容器能够写入 hostPath 卷。

shell 复制代码
[root@k8s-node1 ~]# mkdir /data
[root@k8s-node1 ~]# ls /data/
[root@k8s-master pv]# cat  hostpath.yml 
apiVersion: 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
[root@k8s-master pv]# kubectl create -f hostpath.yml 
pod/nginx created
[root@k8s-master pv]# kubectl get pod
shell 复制代码
[root@k8s-node1 ~]# echo "cloud" > /data/b.txt
[root@k8s-master pv]# kubectl exec -it nginx /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx:/# cd /test-pod/
root@nginx:/test-pod# ls
b.txt

持久化存储PV和PVC

1.PV和PVC的引入

plain 复制代码
pv(PersistentVolume 持久卷)
PV 是集群中的一块网络存储,它独立于 Pod 存在。PV 可以是各种存储系统,如云提供商的存储、NFS、iSCSI、本地存储等。
管理员负责创建 PV,并配置其细节,如容量、访问模式(ReadWriteOnce、ReadOnlyMany、ReadWriteMany)、存储类别等。
PV 有自己的生命周期,它的状态包括可用(Available)、绑定(Bound)、释放(Released)、回收(Retained)等状态。

pvc(PersistentVolumeClaim 持久卷声明)
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 配置nfs

k8s-master nfs-server

k8s-node1 k8s-node2 nfs-client

所有节点安装nfs

shell 复制代码
yum install -y nfs-common nfs-utils

在nfs-server节点创建共享目录

shell 复制代码
[root@k8s-master k8s]# mkdir /nfsdata

授权共享目录

shell 复制代码
[root@k8s-master k8s]# chmod 666 /nfsdata

编辑exports文件(no_root_squash#root用户具有根目录的完全管理访问权限;no_all_squash#保留共享文件的UID和GID(默认);sync所有数据在请求时写入共享(数据实时同步))

shell 复制代码
[root@k8s-master k8s]# vim /etc/exports
/nfsdata *(rw,no_root_squash,no_all_squash,sync)

配置生效

启动rpc和nfs(注意顺序)

shell 复制代码
[root@k8s-master k8s]# systemctl start rpcbind
[root@k8s-master k8s]# systemctl start nfs

作为准备工作,我们已经在 k8s-master 节点上搭建了一个 NFS 服务器,目录为 /nfsdata

测试NFS挂载是否可用

shell 复制代码
[root@k8s-node2 ~]# mkdir /test
[root@k8s-node2 ~]# mount -t nfs 10.8.166.252:/nfsdata /test/
[root@k8s-node2 ~]# df -Th|grep "/test"
10.8.166.252:/nfsdata   nfs4       19G  9.9G  9.0G  53% /test
[root@k8s-node2 ~]# touch /test/ip.txt
[root@k8s-node2 ~]# ls /test/
ip.txt

来到nfs-server查看,成功

shell 复制代码
[root@k8s-master ~]# ls /nfsdata/
ip.txt

[root@k8s-node2 ~]# umount /test  #测试完成之后,就可以卸载了

2.2 创建PV

下面创建一个 PV mypv1,配置文件 nfs-pv1.yml 如下:

shell 复制代码
[root@k8s-master ~]# vim nfs-pv1.yml 
apiVersion: 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

shell 复制代码
[root@k8s-master ~]# kubectl apply -f nfs-pv1.yml

STATUSAvailable,表示 mypv1 就绪,可以被 PVC 申请/绑定。

2.3 创建PVC

接下来创建 PVC mypvc1,配置文件 nfs-pvc1.yml 如下:

shell 复制代码
[root@k8s-master ~]# cat nfs-pvc1.yml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs

PVC 就很简单了,只需要指定 PV 的容量,访问模式和 class。

执行命令创建 mypvc1

shell 复制代码
[root@k8s-master ~]# kubectl apply -f nfs-pvc1.yml

kubectl get pvc -o yaml/json 查看实际申请空间是多少

kubectl get pvckubectl get pv 的输出可以看到 mypvc1 已经 Bound 到 mypv1,申请成功。

2.4 创建pod

上面已经创建好了pv和pvc,pod中直接使用这个pvc即可

shell 复制代码
[root@k8s-master ~]# vim pod1.yml 
apiVersion: v1
kind: Pod
metadata:
  name: mypod1
  labels:
    app: jiangege
spec:
  containers:
    - name: mypod1
      image: nginx
      ports:
      - containerPort: 80
      volumeMounts:
      - mountPath: "/usr/share/nginx/html"
        name: mydata
  volumes:
   - name: mydata
     persistentVolumeClaim:
       claimName: mypvc1

与使用普通 Volume 的格式类似,在 volumes 中通过 persistentVolumeClaim 指定使用 mypvc1 申请的 Volume。

通过命令创建mypod1

shell 复制代码
[root@k8s-master ~]# kubectl apply -f pod1.yml

2.5 验证

shell 复制代码
[root@k8s-master ~]# kubectl exec -it mypod1 /bin/sh
/ # ls /usr/share/nginx/html
/ # echo "jiangege" > /usr/share/nginx/html/index.html

[root@k8s-master ~]# ls /nfsdata/    #也可在nfs的共享目录中查看到,说明卷共享成功
index.html
[root@k8s-master ~]# cat /nfsdata/index.html 
jiangege
可见,在 Pod 中创建的文件 /usr/share/nginx/html/index.html 确实已经保存到了 NFS 服务器目录 /nfsdata中。
如果不再需要使用 PV,可用删除 PVC 回收 PV。

在这里,可以尝试在任何一方删除文件,文件在两端都会消失;

3.PV的回收

当 PV 不再需要时,可通过删除 Pod, PVC 回收。未删除pvc之前 pv的状态是Bound

删除pod

plain 复制代码
[root@k8s-master pvc]# kubectl delete pod mypod1

删除pvc

plain 复制代码
[root@k8s-master pvc]# kubectl delete pvc mypvc1

查看/nfsdata下面的内容,应该还在

4.PV&PVC在应用在Mysql的持久化存储实战项目

下面演示如何为 MySQL 数据库提供持久化存储,步骤为:

  1. 创建 PV 和 PVC。
  2. 部署 MySQL。
  3. 向 MySQL 添加数据。
  4. 模拟节点宕机故障,Kubernetes 将 MySQL 自动迁移到其他节点。
  5. 验证数据一致性。

首先创建 PV 和 PVC,配置如下:

mysql-pv.yml

shell 复制代码
[root@k8s-master mysqlpv]# cat mysql-pv.yml 
apiVersion: 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
[root@k8s-master mysqlpv]# kubectl apply -f mysqlpv.yml

mysql-pvc.yml

shell 复制代码
[root@k8s-master mysqlpv]# cat mysql-pvc.yml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs

[root@k8s-master mysqlpv]# kubectl apply -f mysql-pvc.yml

接下来部署 MySQL,配置文件如下:

yaml 复制代码
[root@k8s-master mysqlpv]# cat mysqlpod.yml 
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.7 #这里的镜像一定要选对,能确保拉取到,而且能使用变量
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "jiange@2024"
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pvc
          
[root@k8s-master mysqlpv]# kubectl apply -f mysqlpod.yml

PVC mysql-pvc Bound 的 PV mysql-pv 将被 mount 到 MySQL 的数据目录 /var/lib/mysql

MySQL 被部署到 k8s-node1

① 切换到数据库 mysql。

② 创建数据库表 my_id。

③ 插入一条数据。

④ 确认数据已经写入。

关闭 k8s-node1,模拟节点宕机故障。

shell 复制代码
[root@k8s-master mysqlpv]# kubectl exec -it mysql-6654fcb867-mqtcl /bin/bash
root@mysql-6654fcb867-mqtcl:/# mysql -uroot -p'jiange@2024'
mysql> create database xiaofeifei;
mysql> create table xiaofeifei.t1(id int);
mysql> insert into xiaofeifei.t1 values(2);

验证数据的一致性:

第一种:

删除deployment,pvc,pv;然后重新创建pv,pvc,deployment;数据在Mysql中,仍然挂载成功;

第二种:

由于node1节点已经宕机,node2节点接管了这个任务,pod转移,需要等待一段时间,我这里等待了8分钟左右。。

shell 复制代码
进入新的pod中,数据仍然存在,持久化成功。很安全
[root@k8s-master mysqlpv]# kubectl exec -it mysql-6654fcb867-mqtcl /bin/bash
root@mysql-6654fcb867-mqtcl:/# mysql -uroot -p'password'
mysql> select * from xiaofeifei.t1;
+------+
| id   |
+------+
|    1 |
|    2 |
+------+
2 rows in set (0.01 sec)

MySQL 服务恢复,数据也完好无损。

xiaofeifei.t1(id int);

mysql> insert into xiaofeifei.t1 values(2);

复制代码
验证数据的一致性:

第一种:

删除deployment,pvc,pv;然后重新创建pv,pvc,deployment;数据在Mysql中,仍然挂载成功;

第二种:

由于node1节点已经宕机,node2节点接管了这个任务,pod转移,需要等待一段时间,我这里等待了8分钟左右。。



[外链图片转存中...(img-lb3zWX7H-1775299273656)]



```shell
进入新的pod中,数据仍然存在,持久化成功。很安全
[root@k8s-master mysqlpv]# kubectl exec -it mysql-6654fcb867-mqtcl /bin/bash
root@mysql-6654fcb867-mqtcl:/# mysql -uroot -p'password'
mysql> select * from xiaofeifei.t1;
+------+
| id   |
+------+
|    1 |
|    2 |
+------+
2 rows in set (0.01 sec)

MySQL 服务恢复,数据也完好无损。

相关推荐
雨奔4 分钟前
Kubernetes 网络策略(NetworkPolicy)完全指南:声明式 Pod 通信管控
网络·容器·kubernetes
身如柳絮随风扬16 分钟前
Kubernetes v1.20.9 集群搭建
云原生·容器·kubernetes
蛐蛐蛐2 小时前
在Windows 11上安装Docker的踩坑记录
运维·docker·容器
古典和浪漫2 小时前
docker file 中设置软链接和在k8s 中配置同步时区 有什么区别,各自优缺点
docker·容器·kubernetes
成为你的宁宁2 小时前
【K8s ServiceAccount 机制原理与 RBAC 权限实战应用】
云原生·容器·kubernetes
尘世壹俗人3 小时前
知识点12---k8s进阶操作方式yaml资源文件
docker·容器·kubernetes
尘世壹俗人3 小时前
知识点13---k8s存储持久化
容器·kubernetes·flask
SilentSamsara3 小时前
Kubernetes 网络模型:CNI 插件与 Pod 间通信的底层实现
网络·云原生·容器·架构·kubernetes·k8s
wuxinyan1234 小时前
Java面试题50:Kubernetes 全栈知识体系之一
java·kubernetes·面试题
牛奶咖啡134 小时前
Docker容器实践——Docker常用基础镜像的解析与选择
docker·容器·docker基础镜像·docker基础镜像选择·docker基础镜像最佳实践·docker基础镜像的分类·docker基础镜像的对比