WHCSRL 技术网

《Kubernetes 知识:Kubernetes 中的持久化存储》



一、持久化存储概述

在k8s中为什么要做持久化存储?
在k8s中部署的应用都是以pod容器的形式运行的,因为Pod是有生命周期的,如果pod不挂载数据卷,那pod被删除或重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到pod数据持久化存储。


二、PV/PVC/StorageClass

2.1、PV概述

PersistentVolume:简称PV,是外部存储系统中的一块存储空间,由管理员配置或使用存储类动态配置。与Volume一样,PV是容量插件,如Volumes,其生命周期独立于使用PV的任何单个pod。严格来说,PV是k8s里面的一个概念,它本身不是存储,只不过是创建PV的资源清单文件中指定了网络存储的地址,同时也指定了一些存储的参数,例如大小,性能等指标。


2.2、PVC概述

PersistentVolumeClaim:简称PVC,是对PV的申请(Claim)。PVC通常由普通用户创建和维护。需要为Pod分配存储资源时,用户可以创建一个PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,Kubernetes 会查找并提供满足条件的PV与之绑定。它类似于一个pod。 Pod消耗节点资源,PVC消耗PV资源。 Pod可以请求特定级别的资源(CPU和内存)。 pvc在申请pv的时候也可以请求特定的大小和访问模式(例如,可以一次读写或多次只读)。


2.3、StorageClass概述

在大规模的kubernetes生产集群中,可能有非常多的PVC,随着项目的增加,可能需要不断添加新的PV,否则就有可能新的pod因为PVC绑定不到PV而创建失败。所以,Kubernetes提供了一套可以自动创建PV的机制,即:Dynamic Provisioning.而这个机制的核心在于:StorageClass这个API对象。
StorageClass对象会定义下面两部分内容:
1,PV的属性.比如,存储类型,Volume的大小等
2,创建这种PV需要用到的存储插件
有了这两个信息之后,Kubernetes就能够根据用户提交的PVC,找到一个对应的StorageClass,之后Kubernetes就会调用该StorageClass声明的存储插件,进而创建出需要的PV。

总结:
1、PV是一个具体的Volume属性,比如Volume的类型,挂载目录等
2、PVC描述的是Pod想要使用的持久化存储的属性,比如存储的大小,读写权限等
3、StorageClass的作用,则是充当PV的模板,从而在找不到合适的PV之后可以动态创建PV的一个机制


2.4、PV/PVC/StorageClass关系图

参考如下链接:看懂StorageClass pv pvc关系


2.5、PV类型

PV 持久卷是用插件的形式来实现的。Kubernetes 目前支持以下插件,如下图所示:
在这里插入图片描述


2.6、PV卷阶段状态

 Available – 资源尚未被claim使用
 Bound – 卷已经被绑定到claim了
 Released – claim被删除,卷处于释放状态,但未被集群回收。
 Failed – 卷自动回收失败
  • 1
  • 2
  • 3
  • 4

PV是群集中的资源。PVC是对这些资源的请求,并且还充当对资源的检查。PV和PVC之间的相互作用遵循以下生命周期:Provisioning —> Binding —>Using—>Releasing—>Recycling

供应准备Provisioning—通过集群外的存储系统或者云平台来提供存储持久化支持。
  1、静态提供Static:集群管理员创建多个PV。 它们携带可供集群用户使用的真实存储的详细信息。 它们存在于Kubernetes API中,可用于消费。
  2、动态提供Dynamic:当管理员创建的静态PV都不匹配用户的PersistentVolumeClaim时,集群可能会尝试为PVC动态配置卷。 此配置基于StorageClasses:PVC必须请求一个类,并且管理员必须已创建并配置该类才能进行动态配置。

绑定Binding—用户创建pvc并指定需要的资源和访问模式。在找到可用pv之前,pvc会保持未绑定状态。
使用Using—用户可在pod中像volume一样使用pvc。

释放Releasing—用户删除pvc来回收存储资源,pv将变成“released”状态。由于还保留着之前的数据,这些数据需要根据不同的策略来处理,否则这些存储资源无法被其他pvc使用。

回收Recycling—pv可以设置三种回收策略:保留(Retain),回收(Recycle)和删除(Delete)。

 - 保留策略:允许人工处理保留的数据。
 - 删除策略:将删除pv和外部关联的存储资源,需要插件支持。
 - 回收策略:将执行清除操作,之后可以被新的pvc使用,需要插件支持。
 注:目前只有NFS和HostPath类型卷支持回收策略,AWS EBS,GCE PD,Azure Disk和Cinder支持删除(Delete)策略。
  • 1
  • 2
  • 3
  • 4

2.6、PVC访问模式

PVC访问模式

ReadWriteOnce
卷可以被一个节点以读写方式挂载。 ReadWriteOnce访问模式也允许运行在同一节点上的多个Pod访问卷。

ReadOnlyMany
卷可以被多个节点以只读方式挂载。

ReadWriteMany
卷可以被多个节点以读写方式挂载。

ReadWriteOncePod
卷可以被单个Pod以读写方式挂载。如果你想确保整个集群中只有一个Pod可以读取或写入该PVC,请使用ReadWriteOncePod访问模式。这只支持CSI卷以及需要Kubernetes1.22以上版本。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

参考链接: Introducing Single Pod Access Mode for PersistentVolumes

在命令行接口(CLI)中,访问模式也使用以下缩写形式:

RWO - ReadWriteOnce
ROX - ReadOnlyMany
RWX - ReadWriteMany
RWOP - ReadWriteOncePod
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

示例如下:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: shared-cache
spec:
  accessModes: #定义pvc的访问模式
  - ReadWriteMany # Allow many pods to access shared-cache simultaneously.
  resources: #定义所需存储空间大小
    requests:
      storage: 1Gi
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

三、emptyDir存储卷

当Pod分派到某个Node上时,emptyDir卷会被创建,并且在 Pod在该节点上运行期间,卷一直存在。就像其名称表示的那样,卷最初是空的。 尽管Pod中的容器挂载emptyDir卷的路径可能相同也可能不同,这些容器都可以读写 emptyDir 卷中相同的文件。当Pod因为某些原因被从节点上删除时,emptyDir卷中的数据也会被永久删除。

说明: 容器崩溃并不会导致 Pod 被从节点上移除,因此容器崩溃期间 emptyDir 卷中的数据是安全的。

示例如下:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pd
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: nginx-containers
    ports:
    - name: http
      containerPort: 80
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  - name: busybox
    image: busybox:latest
    name: busybox-containers
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: html
      mountPath: /data/    #在容器内定义挂载存储名称和挂载路径
    command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']
  volumes:
  - name: html
    emptyDir: {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

在上面,我们定义了2个容器,其中一个容器是输入日期到index.html中,然后验证访问nginx的html是否可以获取日期。以验证两个容器之间挂载的emptyDir实现共享。如下访问验证:
在这里插入图片描述
两个容器之间挂载的emptyDir是可以实现共享的,如下所示:
在这里插入图片描述
当 Pod 因为某些原因被从节点上删除时,emptyDir卷中的数据也会被永久删除,如下所示:
在这里插入图片描述
说明: 生产环境不建议使用emptyDir存储作为pod的后端存储方式。


四、hostPath存储卷

hostPath Volume是指Pod挂载宿主机上的目录或文件。 hostPath Volume使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在pod被删除,这个存储卷还是存在的,不会被删除,所以只要同一个pod被调度到同一个节点上来,在pod被删除重新被调度到这个节点之后,对应的数据依然是存在的。

除了必需的path属性之外,用户可以选择性地为hostPath卷指定type
在这里插入图片描述
示例如下:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: test-container
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      path: /data/nginx #宿主机存储目录地址
      type: DirectoryOrCreate #如果存储目录不存在,则会重新创建该目录,权限设置为 0755,具有与kubelet相同的组和属主信息。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

测试存储卷是可以正常使用的,如下所示:
在这里插入图片描述
删除pod数据还是存在,如下所示:
在这里插入图片描述
重新调度pod到另外一台主机,无法访问,之前创建的index.html文件不存在
在这里插入图片描述
总结: pod删除之后重新创建必须调度到同一个node节点,数据才不会丢失。


五、NFS共享存储卷

nfs卷能将 NFS (网络文件系统) 挂载到你的Pod 中。 不像 emptyDir那样会在删除Pod的同时也会被删除,nfs卷的内容在删除Pod时会被保存,卷只是被卸载。 这意味着nfs卷可以被预先填充数据,并且这些数据可以在Pod之间共享。

5.1、安装nfs服务端

说明:建议找一台单独且磁盘容量足够大的服务器作为nfs-server端。

# 1、安装nfs-server端
yum -y install nfs-utils rpcbind
systemctl start nfs && systemctl enable nfs
systemctl start rpcbind && systemctl enable rpcbind

# 3、创建共享目录
mkdir -p /data/volumes/{v1,v2,v3} 

# 3、编辑配置文件
cat > /etc/exports << EOF
/data/volumes/v1  192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes/v2  192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes/v3  192.168.1.0/24(rw,no_root_squash,no_all_squash)
EOF

# 4、发布
[root@k8s-client volumes]# exportfs -arv
exporting 192.168.1.0/24:/data/volumes/v1
exporting 192.168.1.0/24:/data/volumes/v2
exporting 192.168.1.0/24:/data/volumes/v3

# 5、查看
[root@k8s-client ~]# showmount -e 192.168.1.17
Export list for 192.168.1.17:
/data/volumes/v1 192.168.1.0/24
/data/volumes/v2 192.168.1.0/24
/data/volumes/v3 192.168.1.0/24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

5.2、安装nfs客户端

说明:当前环境kubernetes集群master节点和worker节点则为nfs客户端。

# 客户端不需要创建共享目录和编辑配置文件,只安装服务就行
yum -y install nfs-utils
  • 1
  • 2

5.3、创建nfs存储卷的使用清单

说明:具有kubectl权限的主机。

apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-nfs
  namespace: default
spec:
  containers:
  - name: myapp
    image: nginx
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
    - name: html
      nfs:
        path: /data/volumes/v2  #nfs共享目录
        server: 192.168.1.17  #nfs server端ip地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

经测试pod被k8s-worker-16主机上,在共享目录下创建index.html文件,nginx可以正常访问该文件
在这里插入图片描述
pod被调度到其它主机上,nginx也是可以正常访问
在这里插入图片描述
总结: pod删除之后重新创建,及时被调度到其它主机,数据不会丢失,依然可以访问。
缺点: nfs如果宕机了,数据也就丢失了,所以建议使用分布式存储,常见的分布式存储有glusterfs和cephfs。


六、NFS使用PV和PVC

6.1、安装nfs服务端

说明:建议找一台单独且磁盘容量足够大的服务器作为nfs-server端。

# 1、安装nfs-server端
yum -y install nfs-utils rpcbind
systemctl start nfs && systemctl enable nfs
systemctl start rpcbind && systemctl enable rpcbind

# 3、创建共享目录
mkdir -p /data/volumes_test/{v1,v2,v3,v4} 

# 3、编辑配置文件
cat > /etc/exports << EOF
/data/volumes_test/v1  192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes_test/v2  192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes_test/v3  192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes_test/v4  192.168.1.0/24(rw,no_root_squash,no_all_squash)

EOF

# 4、发布
[root@k8s-client volumes]# exportfs -arv
exporting 192.168.1.0/24:/data/volumes_test/v4
exporting 192.168.1.0/24:/data/volumes_test/v3
exporting 192.168.1.0/24:/data/volumes_test/v2
exporting 192.168.1.0/24:/data/volumes_test/v1


# 5、查看
[root@k8s-client ~]# showmount -e 192.168.1.17
Export list for 192.168.1.17:
/data/volumes_test/v4 192.168.1.0/24
/data/volumes_test/v3 192.168.1.0/24
/data/volumes_test/v2 192.168.1.0/24
/data/volumes_test/v1 192.168.1.0/24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

6.2、安装nfs客户端

说明:当前环境kubernetes集群master节点和worker节点则为nfs客户端。

# 客户端不需要创建共享目录和编辑配置文件,只安装服务就行
yum -y install nfs-utils
  • 1
  • 2

6.3、定义PV

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-1
spec:
  capacity: #存储大小
    storage: 100Mi
  accessModes: ["ReadWriteMany","ReadWriteOnce"] #该存储目录允许访问的模式,ReadWriteMany表示可以被多个节点已读写方式挂载,ReadWriteOnce表示可以被一个节点以读写方式挂载
  nfs:
    server: 192.168.1.17
    path: "/data/volumes_test/v1"
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-2
spec:
  capacity: #存储大小
    storage: 200Mi
  accessModes: ["ReadWriteMany","ReadWriteOnce"] #该存储目录允许访问的模式,ReadWriteMany表示可以被多个节点已读写方式挂载,ReadWriteOnce表示可以被一个节点以读写方式挂载
  nfs:
    server: 192.168.1.17
    path: "/data/volumes_test/v2"
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-3
spec:
  capacity: #存储大小
    storage: 300Mi
  accessModes: ["ReadOnlyMany"] #该存储目录允许访问的模式,ReadOnlyMany表示可以被多个节点以只读方式挂载
  nfs:
    server: 192.168.1.17
    path: "/data/volumes_test/v3"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

如下所示,则表明pv创建成功
在这里插入图片描述


6.4、定义PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
  namespace: default
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 100Mi
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-pvc
  namespace: default
spec:
  containers:
  - name: myapp
    image: nginx
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
    - name: html
      persistentVolumeClaim:
        claimName: mypvc
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

这里定义了pvc的访问模式为多路读写,该访问模式必须在前面pv定义的访问模式之中。定义PVC申请的大小为100Mi,此时PVC会自动去匹配多路读写且大小为100Mi的PV,匹配成功获取PVC的状态即为Bound
在这里插入图片描述


七、使用nfs-client实现storageclass

7.1、安装nfs服务端

说明:建议找一台单独且磁盘容量足够大的服务器作为nfs-server端。

# 1、安装nfs-server端
yum -y install nfs-utils rpcbind
systemctl start nfs && systemctl enable nfs
systemctl start rpcbind && systemctl enable rpcbind

# 3、创建共享目录
mkdir -p /data/volumes/{v1,v2,v3} 

# 3、编辑配置文件
cat > /etc/exports << EOF
/data/volumes/v1  192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes/v2  192.168.1.0/24(rw,no_root_squash,no_all_squash)
/data/volumes/v3  192.168.1.0/24(rw,no_root_squash,no_all_squash)
EOF

# 4、发布
[root@k8s-client volumes]# exportfs -arv
exporting 192.168.1.0/24:/data/volumes/v1
exporting 192.168.1.0/24:/data/volumes/v2
exporting 192.168.1.0/24:/data/volumes/v3

# 5、查看
[root@k8s-client ~]# showmount -e 192.168.1.17
Export list for 192.168.1.17:
/data/volumes/v1 192.168.1.0/24
/data/volumes/v2 192.168.1.0/24
/data/volumes/v3 192.168.1.0/24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

7.2、安装nfs客户端

说明:当前环境kubernetes集群master节点和worker节点则为nfs客户端。

# 客户端不需要创建共享目录和编辑配置文件,只安装服务就行
yum -y install nfs-utils
  • 1
  • 2

7.3、安装nfs插件

说明:具有kubectl权限的主机。

官方测试资源下载
NFS动态存储StorageClass资源清单文件

1、修改deployment.yaml文件
在这里插入图片描述
2、创建账号

kubectl create -f rbac.yaml
  • 1

3、创建nfs-client

kubectl create -f deployment.yaml
  • 1

4、创建nfs-client kubernetes storage class

kubectl create -f class.yaml
  • 1

5、设置StorageClass为默认
说明:默认的 StorageClass 将被用于动态的为没有特定 storage class 需求的PersistentVolumeClaims 配置存储:(只能有一个默认StorageClass),如果没有默认StorageClass,PVC 也没有指定storageClassName 的值,那么意味着它只能够跟 storageClassName相同的PV进行绑定。

kubectl patch storageclass managed-nfs-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
  • 1

如下图所示:
在这里插入图片描述
6、测试pvc

[root@k8s-client-17 nfs]# kubectl apply -f test-claim.yaml
  • 1

问题:创建的pvc一直处于pending状态
在这里插入图片描述
解决方案:分别在三个master节点上,修改/etc/kubernetes/manifests/kube-apiserver.yaml 文件,添加添加–feature-gates=RemoveSelfLink=false,当前kubernetes集群是使用kubeadm部署的,修改kube-apiserver.yaml 文件会自动重启apiserver服务,不用手动重启,如下图所示:
在这里插入图片描述
如下图所示,则表明pvc创建成功
在这里插入图片描述
7、测试pod

[root@k8s-client-17 nfs]# kubectl apply -f test-pod.yaml
  • 1

如下图所示,则表示pod运行成功
在这里插入图片描述
查看共享目录/data/volumes/v1/,下面有SUCCESS文件
在这里插入图片描述


总结:整理不易,如果对你有帮助,可否点赞关注一下?

更多详细内容请参考:企业级K8s集群运维实战

推荐阅读