本章概述

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 服务器容器服务数据时,保存内容管理器容器获取的文件。

[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 

img

[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

[root@k8s-master pv]# kubectl get pod -o wide

img

[root@k8s-node1 ~]# docker inspect k8s_test-container_test-pod-emptydir_default_7a310cd3-8d93-422b-bbe1-1c405b22721f_0

img

[root@k8s-node1 ~]# cat /var/lib/kubelet/pods/7a310cd3-8d93-422b-bbe1-1c405b22721f/volumes/kubernetes.io~empty-dir/cache-volume/a.txt 
linux

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

[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 卷。

[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

img

[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的引入

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

yum install -y nfs-common nfs-utils

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

[root@k8s-master k8s]# mkdir /nfsdata

授权共享目录

[root@k8s-master k8s]# chmod 666 /nfsdata

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

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

配置生效

启动rpc和nfs(注意顺序)

[root@k8s-master k8s]# systemctl start rpcbind
[root@k8s-master k8s]# systemctl start nfs

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

测试NFS挂载是否可用

[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查看,成功

[root@k8s-master ~]# ls /nfsdata/
ip.txt

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

2.2 创建PV

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

[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

[root@k8s-master ~]# kubectl apply -f nfs-pv1.yml

image-20240805233806644

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

2.3 创建PVC

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

[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

[root@k8s-master ~]# kubectl apply -f nfs-pvc1.yml

img

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

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

2.4 创建pod

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

[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

[root@k8s-master ~]# kubectl apply -f pod1.yml

2.5 验证

[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

img

删除pod

[root@k8s-master pvc]# kubectl delete pod mypod1

删除pvc

[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

[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

[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

img

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

[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

img

① 切换到数据库 mysql。

② 创建数据库表 my_id。

③ 插入一条数据。

④ 确认数据已经写入。

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

[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分钟左右。。

img

进入新的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-2y3zSFTY-1770860451121)]



```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 服务恢复,数据也完好无损。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐