加入公众号一起发现美好技术

在这里插入图片描述

在Kubernetes中,因为deployment默认使用的是hostpath,当我们pod重启或删除pod后数据会丢失。这时候我们就需要一个持久化存储来解决这个问题。 本次介绍的是kubernetes pv与pvc,同时使用nfs作为后端存储进行演示。 当然kubernetes pv 支持不同的volume,为了环境快速构建学习本次以NFS为主

首先我们来了解一下什么是PV和PVC

PV的全称是: PersistentVolume (持久化卷),是对底层的共享存储的一种抽象,PV由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如Ceph、GlusterFS、NFS等,都是通过插件机制完成与共享存储的对接

PVC的全称是: PersistenVolumeClaim (持久化卷声明),PVC是用户存储的一种声明,PVC和Pod比较类型,Pod是消耗节点,PVC消耗的是PV资源,Pod可以请求CPU的内存,而PVC可以请求特定的存储空间和访问模式。对于真正存储的用户不需要关心底层的存储实现细节,只需要直接使用PVC即可

PV和PVC的生命周期

PV可以看作可用的存储资源,PVC则是对存储资源的需求,PV和PVC的互相关系遵循如下图
在这里插入图片描述

1、资源供应 (Provisioning)

Kubernetes支持两种资源的供应模式:静态模式(Staic)和动态模式(Dynamic)。资源供应的结果就是创建好的PV。

- 静态模式:集群管理员手工创建许多PV,在定义PV时需要将后端存储的特性进行设置
- 动态模式:集群管理员无须手工创建PV,而是通过StorageClass的设置对后端存储进行描述,标记为某种 "类型(Class)"。此时要求PVC对存储的类型进行声明,系统将自动完成PV的创建及PVC的绑定。PVC可以声明Class为"",说明该PVC禁止使用动态模式

2、资源绑定 (Binding)

在用户定义好PVC后,系统将根据PVC对存储资源的请求 (存储空间和访问模式)在已存在的PV中选择一个满足PVC要求的PV,一旦找到,就将该PV与用户定义的PVC进行绑定,然后用户的应用就可以使用这个PVC了。如果系统中没有满足PVC要求的PV,PVC则会无限期处于Pending状态,直到等到系统管理员创建了一个符合要求的PV。PV一旦绑定在某个PVC上,就被这个PVC独占,不能再与其他PVC进行绑定了。在这种情况下,当PVC申请的存储空间比PV的少时,整个PV的空间都能够为PVC所用,可能会造成资源的浪费。如果资源供应使用的是动态模式,则系统在PVC找到合适的StorageClass后,将会自动创建PV并完成PVC的绑定

3、资源使用 (Using)

Pod 使用volume的定义,将PVC挂载到容器内的某个路径进行使用。volume的类型为persistentVoulumeClaim,在容器应用挂载了一个PVC后,就能被持续独占使用。不过,多个Pod可以挂载同一个PVC,应用程序需要考虑多个实例共同访问一块存储空间的问题

4、资源释放 (Releasing)

当用户对存储资源使用哪个完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为已释放,但还不能立刻与其他PVC进行绑定。通过之前PVC写入的数据可能还留在存储设备上,只有在清除之后该PV才能继续使用

5、资源回收 (Reclaiming)

对于PV,管理员可以设定回收策略(Reclaim Policy)用于设置与之绑定的PVC释放资源之后,对于遗留数据如何处理。只有PV的存储空间完成回收,才能供新的PVC绑定和使用。

5.1、静态资源下,通过PV和PVC完成绑定,并供Pod使用的存储管理机制
在这里插入图片描述
5.2、动态资源下,通过StorageClass和PVC完成资源动态绑定 (系统自动生成PV,并供Pod使用的存储管理机制)
在这里插入图片描述

使用NFS进行演示

首先我们需要安装NFS服务

#这里我使用单独服务器进行演示,实际上顺便使用一台服务器安装nfs都可以 (建议和kubernetes集群分开,找单独一台机器)
[root@nfs ~]# yum install nfs-utils -y rpcbind
 
#接下来设置nfs存储目录
[root@nfs ~]# mkdir /data1/k8s-volume
[root@nfs ~]# chmod 755 /data1/k8s-volume/
 
#编辑nfs配置文件
[root@nfs ~]# cat /etc/exports
/data1/k8s-volume  *(rw,no_root_squash,sync)
 
#存储目录,*允许所有人连接,rw读写权限,sync文件同时写入硬盘及内存,no_root_squash 使用者root用户自动修改为普通用户
 
 
 
接下来启动rpcbind
[root@nfs ~]# systemctl start rpcbind
[root@nfs ~]# systemctl enable rpcbind
[root@nfs ~]# systemctl status rpcbind
● rpcbind.service - RPC bind service
   Loaded: loaded (/usr/lib/systemd/system/rpcbind.service; enabled; vendor preset: enabled)
   Active: active (running) since 四 2019-12-19 18:44:29 CST; 11min ago
 Main PID: 3126 (rpcbind)
   CGroup: /system.slice/rpcbind.service
           └─3126 /sbin/rpcbind -w
 
12月 19 18:44:29 yzsjhl82-135.opi.com systemd[1]: Starting RPC bind service...
12月 19 18:44:29 yzsjhl82-135.opi.com systemd[1]: Started RPC bind service.
#由于nfs需要向rpcbind进行注册,所以我们需要优先启动rpcbind
 
 
 
#启动NFS
[root@nfs ~]# systemctl restart nfs
[root@nfs ~]# systemctl enable nfs
[root@nfs ~]# systemctl status nfs
● nfs-server.service - NFS server and services
   Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; enabled; vendor preset: disabled)
  Drop-In: /run/systemd/generator/nfs-server.service.d
           └─order-with-mounts.conf
   Active: active (exited) since 四 2019-12-19 18:44:30 CST; 13min ago
 Main PID: 3199 (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/nfs-server.service
 
12月 19 18:44:30 yzsjhl82-135.opi.com systemd[1]: Starting NFS server and services...
12月 19 18:44:30 yzsjhl82-135.opi.com systemd[1]: Started NFS server and services.
 
 
#检查rpcbind及nfs是否正常
[root@nfs ~]# rpcinfo |grep nfs
    100003    3    tcp       0.0.0.0.8.1            nfs        superuser
    100003    4    tcp       0.0.0.0.8.1            nfs        superuser
    100227    3    tcp       0.0.0.0.8.1            nfs_acl    superuser
    100003    3    udp       0.0.0.0.8.1            nfs        superuser
    100003    4    udp       0.0.0.0.8.1            nfs        superuser
    100227    3    udp       0.0.0.0.8.1            nfs_acl    superuser
    100003    3    tcp6      ::.8.1                 nfs        superuser
    100003    4    tcp6      ::.8.1                 nfs        superuser
    100227    3    tcp6      ::.8.1                 nfs_acl    superuser
    100003    3    udp6      ::.8.1                 nfs        superuser
    100003    4    udp6      ::.8.1                 nfs        superuser
    100227    3    udp6      ::.8.1                 nfs_acl    superuser
 
#查看nfs目录挂载权限
[root@nfs ~]# cat /var/lib/nfs/etab
/data/k8s   *(rw,sync,wdelay,hide,nocrossmnt,secure,no_root_squash,no_all_squash,no_subtree_check,secure_locks,acl,no_pnfs,anonuid=65534,anongid=65534,sec=sys,rw,secure,no_root_squash,no_all_squash)

我们nfs server端已经完毕,接下来在所有需要nfs挂载的集群节点安装以下

[root@所有节点 ~]# yum install -y nfs-utils rpcbind
 
[root@所有节点 ~]# systemctl start rpcbind
[root@所有节点 ~]# systemctl enable rpcbind
[root@所有节点 ~]# systemctl start nfs
[root@所有节点 ~]# systemctl enable nfs
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.

客户端挂载测试

#首先检查nfs挂载目录是否正常
[root@nfs ~]# showmount -e localhost
Export list for localhost:
/data1/k8s-volume *
 
#现在进行节点挂载
#先在客户端创建数据目录(挂载点位置)
[root@所有节点 ~]# mkdir -p /data1/k8s/
[root@所有节点 ~]# mount -t nfs 10.4.82.118:/data1/k8s-volume /data1/k8s
 
#现在进行挂载 分别是ip:nfs目录  节点存储目录
 
挂在完成后我们使用df -h 就可以看到挂载点
[root@所有节点 ~]# df -h|grep 10.4.82.135
10.4.82.118:/data1/k8s-volume   50G   23G   27G   46% /data1/k8s
 
#所有需要nfs节点这样挂载就可以

创建PV

有了我们NFS共享存储,下面就可以来使用PV和PVC。PV作为存储资源,主要包括存储能力、访问模式、存储类型、回收策略等关键信息。这里使用nfs类型的后端存储,1g存储空间,访问模式为ReadWriteOnce,回收策略为Recyle,对应文件如下

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1     #pv名称
spec:
  capacity:          #存储能力,一个pv对象都要指定一个存储能力,目前仅支持存储空间的设置
    storage: 1Gi    #存储空间
  accessModes:
  - ReadWriteOnce       #访问模式
  persistentVolumeReclaimPolicy: Recycle        #回收策略
  nfs:          #服务模式 (nfs、ceph、hostpath等)
    path: /data1/k8s-volume      #共享数据目录挂载点
    server: 10.4.82.118         #nfs服务器地址

创建pv

[root@yzsjhl82-135 pv]# kubectl apply -f  test-pv.yaml
persistentvolume/pv1 created
[root@yzsjhl82-135 pv]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS        CLAIM              STORAGECLASS   REASON   AGE
pv1     1Gi        RWO            Recycle          Available                                                5m40s
 
# 参数解释
pv1 名称
1Gi 代表存储空间大小
RWO 访问模式(ReadWriteOnce缩写)
Recycle 回收策略
 
Available PV状态

PV相关配置说明

Capacity 存储能力

通过PV的capacity属性来设置存储空间,目前仅支持storage=数据大小,未来可能会加入IOPS、吞吐量等指标配置

AccessModes 访问模式

AccessModes 是用来对PV进行访问模式的设置,用于描述用户应用对存储资源的访问权限

ReadWriteOnce (RWO):读写权限,但是只能被单个节点挂载

ReadOnlyMany (ROX):只读权限,可能被多个节点挂载

ReadWriteMany (RWX):读写权限,可以被多个节点挂载
注意:一些PV可能支持多种访问模式,但挂载的时候只可以使用一种访问模式,多种访问模式不会生效
下面是一些常用的Volume插件支持的访问模式(需要根据我们配置的类型进行选择对应的访问模式)
在这里插入图片描述
persistentVolumeReclaimPolicy回收策略

Retain (保留) 保留数据,需要管理员手动清理

Recycle (回收) 清除PV中的数据,效果相当于执行删除命令

Delete (删除) 与PV相连的后端存储完成volume的删除操作,常见于云服务商的存储服务

不过需要注意的是,目前只有NFS和HostPath两类支持回收策略,一般设置Retain比较保险

状态

1.Available (可用): 表示可用状态,还未被任何PVC绑定

2.Bound (已绑定):已经绑定到某个PVC

3.Released (已释放):对应的PVC已经删除,但资源还没有被集群收回

4.Failed:PV自动回收失败

创建PVC

前面说过,PV实际上没有创建存储,相当于我们node一样,还需要创建Pod进行消费,接下来我们进行PVC的创建与配置

#前面我们已经在集群上都安装nfs客户端,并且进行挂载了。下面进行创建pvc
[root@k8s-01 ~]# kubectl get node
NAME     STATUS   ROLES    AGE   VERSION
k8s-01   Ready       15h   v1.14.2
k8s-02   Ready       15h   v1.14.2
k8s-03   Ready       15h   v1.14.2
k8s-04   Ready       15h   v1.14.2

新建pvc同样需要建立一个数据卷声明

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-nfs
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

接下来我们进行创建,pvc的yaml文件基本上和pv相同,这里不过多解释。

[root@k8s-01 test]# kubectl apply -f pvc-nfs.yaml
persistentvolumeclaim/pvc-nfs created
 
[root@k8s-01 test]# kubectl get pvc
NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-nfs   Bound    pv1      10Gi       RWO                           9s
 
[root@k8s-01 test]# kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
pv1    10Gi       RWO            Recycle          Bound    default/pvc-nfs                           10m
 
#这里我们可以看到,当我们创建pvc之后,pv的状态变成了Bound绑定状态,并且和pvc的状态相同。并且可以看到pvc已经绑定到名称为pv1的volume上,同时在pv上可以看到绑定到名称为pvc-nfs的pvc中

我们也可以详细的查看pv和pvc的信息
在这里插入图片描述
在这里插入图片描述
在Kubernetes中会自动帮我们查看pv状态为Available并且根据声明pvc容量storage的大小进行筛选匹配,同时还会根据AccessMode进行匹配。如果pvc匹配不到pv会一直处于pending状态。

使用Labels匹配PV与PVC

同时,pv与pvc中间还可以通过label标签进行匹配,配置如下

#记得我们需要修改一下名字,名字是不可以重复的
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
  labels:           #这里将pv设置一个labels
    app: nfs
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /data1/k8s-volume
    server: 192.168.0.14
 
---
 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc2-nfs
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  selector:         ##pvc匹配标签为app=nfs的pv
    matchLabels:
      app: nfs
 
 
##接下来进行创建
[root@k8s-01 test]# kubectl  apply -f test.yaml
persistentvolume/pv2 unchanged
persistentvolumeclaim/pvc2-nfs created
 
[root@k8s-01 test]# kubectl get pv,pvc
NAME                   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
persistentvolume/pv1   10Gi       RWO            Recycle          Bound    default/pvc-nfs                            26m
persistentvolume/pv2   10Gi       RWO            Recycle          Bound    default/pvc2-nfs                           62s
 
NAME                             STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/pvc-nfs    Bound    pv1      10Gi       RWO                           16m
persistentvolumeclaim/pvc2-nfs   Bound    pv2      10Gi       RWO                           18s
 
#这里我们可以看到创建的名称为pv2何pv2-nfs已经进行绑定

有一点需要注意,当我们pvc申请的容量小于我们pv的容量是可以进行绑定的,当我们申请pvc的容量大于pv的容量是无法进行绑定的。 这里需要注意
Deployment引用pvc

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: pv-nfs-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: pv-nfs-nginx
  template:
    metadata:
      labels:
        app: pv-nfs-nginx
    spec:
      containers:
      - name: pv-nfs-nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:           #挂载,首先添加需要挂载的目录
        - name: pv-nginx        #挂载点的名称
          mountPath: /usr/share/nginx/html   #挂载点的路径
      volumes:    #绑定
      - name: pv-nginx
        persistentVolumeClaim:    #将镜像中的nginx目录挂载到下面名称的pvc中
          claimName: pvc-nfs   #pvc名称
---
 
apiVersion: v1
kind: Service
metadata:
  name: nfs-pvc
  labels:
    app: pv-nfs-nginx
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: pv-nfs-nginx

接下来我们进行创建nginx deployment

#创建nginx deployment
[root@k8s-01 test]# kubectl apply -f pv-nginx.yaml
deployment.extensions/pv-nfs-nginx created
service/nfs-pvc created

检查pod和svc状态

[root@k8s-01 test]# kubectl get pod,svc|grep pv
pod/pv-nfs-nginx-5c889ccd97-2772n   1/1     Running   0          102s
pod/pv-nfs-nginx-5c889ccd97-hxb66   1/1     Running   0          102s
pod/pv-nfs-nginx-5c889ccd97-rwmth   1/1     Running   0          102s
service/nfs-pvc      NodePort    10.254.223.165           80:21322/TCP   102s
 
#这里我们可以看到pod已经正常启动,并且svc也已经暴露端口了。

接下来我们直接访问nginx是无法访问的,因为在我们nfs挂载点的目录下面没有文件,所以无法访问
在这里插入图片描述
接下来我们到nfs挂载点创建一个index.html

[root@k8s-01 test]#  echo "I am abcdocker" >>/data1/k8s/index.html

然后我们在进行访问查看
在这里插入图片描述
由于我们的index.html直接挂在到了/data1/k8s目录下面,如果有很多个pod都使用pvc进行挂载,会造成我们数据目录的文件比较乱

这里我们添加一个subpathsubPath的目的是为了在单一Pod中多次使用同一个volume而设计的。
在这里插入图片描述

#deployment文件如下
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: pv-nfs-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: pv-nfs-nginx
  template:
    metadata:
      labels:
        app: pv-nfs-nginx
    spec:
      containers:
      - name: pv-nfs-nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:           #挂载,首先添加需要挂载的目录
        - name: pv-nginx        #挂载点的名称
          mountPath: /usr/share/nginx/html   #挂载点的路径
          subPath: nginx-pvc
      volumes:    #绑定
      - name: pv-nginx
        persistentVolumeClaim:    #将镜像中的nginx目录挂载到下面名称的pvc中
          claimName: pvc-nfs   #pvc名称
---
 
apiVersion: v1
kind: Service
metadata:
  name: nfs-pvc
  labels:
    app: pv-nfs-nginx
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: pv-nfs-nginx

当我们更新完pod之后,等pod正常启动。就可以看到在我们nfs存储目录下面单独创建了一个名称为nginx-pvc的目录,这个目录实际上就是我们subpath后面指定的名称

[root@k8s-01 test]# kubectl apply -f pv-nginx.yaml
deployment.extensions/pv-nfs-nginx configured
 
[root@k8s-01 test]# ls /data1/k8s/
index.html  nginx-pvc  test.txt

这个目录下面也是没有任何文件的,我们需要将原来index.html拷贝过去即可

如果我们直接删除或者有pod在使用pv或者pvc是无法直接删除的,当我们使用Recycle模式时,删除所有pv和pvc后,数据也会进行删除。所以删除pv和pvc请谨慎操作

Logo

开源、云原生的融合云平台

更多推荐