theboyaply
theboyaply
发布于 2020-04-01 / 752 阅读
0
1

k8s PV和PVC

参考:

https://www.kubernetes.org.cn/pvpvcstorageclass

https://kubernetes.io/docs/concepts/storage/persistent-volumes/

概述

PersistentVolume(PV)和 PersistentVolumeClaim(PVC)提供了方便的持久化卷:PV 提供网络存储资源,而 PVC 请求存储资源。这样,设置持久化的工作流包括配置底层文件系统或者云数据卷、创建持久性数据卷、最后创建 PVC 来将 Pod 跟数据卷关联起来。PV 和 PVC 可以将 pod 和数据卷解耦,pod 不需要知道确切的文件系统或者支持它的持久化引擎。

生命周期

PV 是集群中的资源。PVC 是对这些资源的请求,也是对资源的索赔检查。PV 和 PVC 之间的相互作用遵循以下生命周期:

Provisioning ---> Binding ---> Using ---> Releasing ---> Recycling

  • Provisioning:创建 PV。

    • Static:集群管理员创建多个 PV。它们携带可供集群用户使用的真实存储的详细信息。它们存在于 Kubernetes API 中,可用于消费。
    • Dynamic:当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,集群可能会尝试为 PVC 动态配置卷。此配置基于 StorageClasses:PVC 必须请求一个类,并且管理员必须已创建并配置该类才能进行动态配置。要求该类的声明有效地为自己禁用动态配置。
  • Binding:将 PV 分配给 PVC。如果匹配的卷不存在,PVC 将保持无限期。 随着匹配卷变得可用,PVC 将被绑定。 例如,提供许多 50Gi PV 的集群将不匹配要求 100Gi的PVC。 当集群中添加 100Gi PV 时,可以绑定 PVC。

    值得注意的是,PV 与 PVC 是一对一绑定的。

  • Using:Pod 使用 PVC 作为卷。 集群检查声明以找到绑定的卷并挂载该卷的卷。 对于支持多种访问模式的卷,用户在将其声明用作 Pod 中的卷时指定所需的模式。

  • Releasing:Pod 释放 Volume 并删除 PVC。当用户完成卷时,他们可以从允许资源回收的 API 中删除 PVC 对象。 当 PVC 被删除时,PV 被认为是“释放的”,但是它不能被其它 PVC 使用。 Pod 数据仍然保留在 PV 中。

  • Reclaiming:回收 PV,可以保留 PV 以便下次使用,也可以直接从云存储中删除。删除 PV 将从集群中删除PV 对象,以及删除外部基础架构(如 AWS EBS,GCE PD,Azure Disk 或 Cinder 卷)中关联的存储资产。 动态配置的卷始终被删除。

PV 类型

PersistentVolume 类型作为插件实现。Kubernetes 当前支持以下插件:

  • GCEPersistentDiskAWSElasticBlockStoreAzureFileAzureDiskCSIFC (Fibre Channel)

  • FlexVolumeFlockerNFSiSCSIRBD (Ceph Block Device)CephFSCinder (OpenStack block storage)

  • GlusterfsVsphereVolumeQuobyte VolumesPortworx VolumesScaleIO VolumesStorageOS

  • HostPath (Single node testing only – local storage is not supported in any way and WILL NOT WORK in a multi-node cluster)

PV 示例

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /tmp
    server: 172.17.0.2

capacity

storage 指定了 PV 的容量大小。

当前,存储大小是可以设置或请求的唯一资源。 将来的属性可能包括IOPS,吞吐量等。

volumeMode

文件系统类型,有两种类型:Filesystem 和 Block。这是一个可选的参数,默认为 Filesystem。

如果使用的是 Filesystem,那么在创建 Pod 时会将挂载的卷嵌入到 Pod 的文件目录中。

如果使用的是 Block,此模式对于为 Pod 提供最快的访问卷的方式很有用,而 Pod 和卷之间没有任何文件系统层。

accessModes

主机挂载 PV 的关系,这是一个集合属性,即可以在一个 PV 里面声明多个。PVC 的 accessModes 必须和 PV 的 accessModes 一样或者是 PV 的子集才能够绑定到。

  • ReadWriteOnce – 只能在一个节点中挂载,能够读写。
  • ReadOnlyMany – 能够在多个节点挂载,只能读。
  • ReadWriteMany – 能够在多个节点挂载,能够读写。

storageClassName

为 PV 声明一个类名。PVC 的 storageClassName 必须和这个一致。但是 PV 和 PVC 都可以不设置这个值而使用默认的类名。

persistentVolumeReclaimPolicy

回收策略。Retain、Delete 和 Recycle。默认为 Delete。

  • Retain:保留。删除 PVC 后,PV 仍然存在,并且该 PV 被视为”已释放“。这时 PV 不能再被其它对象绑定,并且数据以然存在该 PV 中。

    通常删除一个 PV 的步骤如下:

    1. 删除 PV,但是与之关联的外部基础架构(如 AWS EBS,GCE PD,Azure Disk 或 Cinder 卷)数据仍然存在。
    2. 手动清理外部基础架构数据。
    3. 手动删除外部基础架构。如果要重复使用这个 PV,请重新创建一个。
  • Delete:删除(rm -rf /thevolume/*)。

    从集群中删除 PV 对象以及外部基础架构中的关联存储资产,例如 AWS EBS,GCE PD,Azure Disk 或Cinder 卷。动态预配置的卷将继承其 StorageClass 的回收策略。管理员应根据用户的期望配置 StorageClass。否则,PV 必须在创建后进行编辑或打补丁。

  • Recycle:回收。不推荐使用此策略。相反,推荐的方法是使用动态配置。

mountOptions

安装选项。安装选项是一个字符串,在将卷安装到磁盘时将被累积地连接和使用。当 PV 被创建在某一节点时,管理可以指定额外的安装选项。

值得注意的是并非所有的 PV 类型都支持这个。

如果该选项没有经过验证,那么挂载将会失败。

PVC 示例

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}

accessModes

必须和 PV 的 accessModes 一样或者是 PV 的子集。

volumeMode

必须和 PV 的 volumeMode 一样。

resources

声明(如pod)可以请求的资源大小。 PVC 会自动去寻找合适大小的 PV 进行匹配。

selector

可以指定标签选择器以进一步过滤 PV。 只有标签与选择器匹配的 PV 才能绑定到 PVC。 选择器可以由两个字段组成:

  • matchLabels:PV 必须带有这个标签和值。
  • matchExpressions:通过指定关键字和值的关键字,值列表和运算符所做的要求列表。 有效运算符包括 In,NotIn,Exists 和 DoesNotExist。

storageClassName

必须和 PV 的 storageClassName 一样。PV 与 PVC 都没有这个属性。

示例 - NFS

安装 NFS

我们在 harbor 私有仓库这个机器上安装 nfs 服务,IP 地址为:192.168.19.170:

# 安装 nfs 服务及相关工具
[root@hub ~]# yum install -y nfs-common nfs-utils rpcbind
已加载插件:fastestmirror
Determining fastest mirrors
 * base: mirrors.nju.edu.cn
 * extras: mirrors.njupt.edu.cn
 * updates: mirrors.njupt.edu.cn
 ......
完毕!

# 创建4个共享文件夹
[root@hub ~]# mkdir /nfs1 /nfs2 /nfs3 /nfs4
[root@hub ~]# chmod 777 /nfs1 /nfs2 /nfs3 /nfs4
[root@hub ~]# chown nfsnobody /nfs1 /nfs2 /nfs3 /nfs4

[root@hub ~]# vim /etc/exports
/nfs1 *(rw,no_root_squash,no_all_squash,sync)
/nfs2 *(rw,no_root_squash,no_all_squash,sync)
/nfs3 *(rw,no_root_squash,no_all_squash,sync)
/nfs4 *(rw,no_root_squash,no_all_squash,sync)

[root@hub ~]# systemctl start rpcbind
[root@hub ~]# systemctl start nfs

在集群中所有节点安装 nfs 客户端:

yum install -y nfs-utils rpcbind

安装完成后在集群任一节点测试挂载 nfs 文件系统:

# 测试连接 nfs,结果显示了 nfs 服务器的可挂载目录
[root@k8s-master01 testnfs]# showmount -e 192.168.19.170
Export list for 192.168.19.170:
/nfs4 *
/nfs3 *
/nfs2 *
/nfs1 *

[root@k8s-master01 ~]# pwd
/root
[root@k8s-master01 ~]# mkdir nfs2

# 将本地 /root/nfs2/ 目录挂载到 nfs2 服务器目录
[root@k8s-master01 testnfs]# mount -t nfs 192.168.19.170:/nfs2 /root/nfs2/
[root@k8s-master01 ~]# cd nfs2/
[root@k8s-master01 testnfs]# touch test.txt
[root@k8s-master01 testnfs]# ls
test.txt

# 此时 192.168.19.170 机器上的 /nfs2 目录可以看到 test.txt 这个文件

# 退出连接
[root@k8s-master01 testnfs]# umount -l /root/nfs2/

错误排查

如果执行命令 showmount -e 192.168.19.170 提示: clnt_create: RPC: Port mapper failure - Unable to receive: errno 113 (No route to host),可以关掉 nfs 服务器的防火墙(systemctl status firewalld),或者开启某些端口(自行网上搜索下)。

创建 PV

编写资源清单 nfs1-pv.yaml:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs1-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: "nfs"
  nfs:
    path: /nfs1
    server: 192.168.19.170

然后再依此编写 nfs2-pv.yaml、nfs3-pv.yaml、nfs4-pv.yaml,基本信息一致,除了以下几点:

  • nfs2-pv.yaml
    • metadata.name: nfs2-pv
    • spec.capacity: 4Gi
    • spec.nfs.path: /nfs2
  • nfs3-pv.yaml
    • metadata.name: nfs3-pv
    • spec.capacity: 7Gi
    • spec.nfs.path: /nfs3
  • nfs4-pv.yaml
    • metadata.name: nfs4-pv
    • spec.capacity: 10Gi
    • spec.nfs.path: /nfs4

这里创建多个 PV 的目的是为了验证 PVC 可以根据容量大小自动选择合适的 PV。

创建 PV:

[root@k8s-master01 pv]# kubectl apply -f nfs1-pv.yaml 
persistentvolume/nfs1-pv created

[root@k8s-master01 pv]# kubectl apply -f nfs2-pv.yaml 
persistentvolume/nfs2-pv created

[root@k8s-master01 pv]# kubectl apply -f nfs3-pv.yaml 
persistentvolume/nfs3-pv created

[root@k8s-master01 pv]# kubectl apply -f nfs4-pv.yaml 
persistentvolume/nfs4-pv created

[root@k8s-master01 pv]# kubectl get pv -o wide
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE   VOLUMEMODE
nfs1-pv   1Gi        RWO            Recycle          Available           nfs                     51s   Filesystem
nfs2-pv   4Gi        RWO            Recycle          Available           nfs                     47s   Filesystem
nfs3-pv   7Gi        RWO            Recycle          Available           nfs                     43s   Filesystem
nfs4-pv   10Gi       RWO            Recycle          Available           nfs                     40s   Filesystem

加上 -o wide 参数,会多显示 VOLUMEMODE 字段,你会发现,默认的 VOLUMEMODE 是 Filesystem。

上面显示所有 PV 都是 Available 的,即未绑定状态。

创建 Pod 和 PVC

编写资源清单 nfs-pvc-pod.yaml:

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx-app
spec:
  ports:
  - port: 80
    name: web
  clusterIP: "None"
  selector:
    app: nginx-app
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  replicas: 1
  serviceName: "nginx-app"
  selector:
    matchLabels:
      app: nginx-app
  template:
    metadata:
      labels:
        app: nginx-app
    spec:
      containers:
      - name: nginx-container
        image: hub.xixihaha.com/library/mynginx:v1
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: pvc-volume
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: pvc-volume
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "nfs"
      resources:
        requests:
          storage: 6Gi

上面是使用 volumeClaimTemplates 创建的 PVC,这种方式会每次都创建一个新的 pvc,即有几个 Pod 就会有几个 pvc。

因此个人建议,将 PVC 单独拿出来创建。这样的话,创建的这个 pvc 能够同时被多个 Pod 绑定:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-volume
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: "nfs"
  resources:
    requests:
      storage: 6Gi

如果是单独创建的 PVC,那么我们可以在 Pod 里这样配置它:

# 部分配置
metadata:
  labels:
    app: nginx-app
spec:
  containers:
  - name: nginx-container
    image: hub.xixihaha.com/library/mynginx:v1
    ports:
    - containerPort: 80
      name: web
    volumeMounts:
    - name: pvc-volume
       mountPath: /usr/share/nginx/html
  volumes:
  - name: pvc-volume
    persistentVolumeClaim:
      claimName: pvc-volume

创建 Pod 及 PVC(pvc 使用 volumeClaimTemplates 一并创建):

[root@k8s-master01 pv]# kubectl apply -f nfs-pvc-pod.yaml 
service/nginx-svc created
statefulset.apps/web created

查看此时的 pvc、pv 以及 pod:

[root@k8s-master01 pv]# kubectl get pvc
NAME               STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-volume-web-0   Bound    nfs3-pv   7Gi        RWO            nfs            8s

[root@k8s-master01 pv]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                      STORAGECLASS   REASON   AGE
nfs1-pv   1Gi        RWO            Recycle          Available                              nfs                     88s
nfs2-pv   4Gi        RWO            Recycle          Available                              nfs                     84s
nfs3-pv   7Gi        RWO            Recycle          Bound       default/pvc-volume-web-0   nfs                     80s
nfs4-pv   10Gi       RWO            Recycle          Available                              nfs                     77s

[root@k8s-master01 pv]# kubectl get pod
NAME                   READY   STATUS      RESTARTS   AGE
web-0                  1/1     Running     0          3m13s

从结果看到,pvc 绑定到了 nfs3-pv 上面。因为它的大小 7Gi 更适合 pvc 的 6Gi。

因为指定了 pvc 的 accessModes 为 ReadWriteOnce,即一个 pvc 只能被一个 pod 绑定,所以现在我们验证一下。

我们将 statefulSet 扩容到 2 个 Pod,然后查看 pvc 的绑定情况:

[root@k8s-master01 pv]# kubectl scale statefulSet web --replicas 2
statefulset.apps/web scaled

[root@k8s-master01 pv]# kubectl get statefulSet
NAME   READY   AGE
web    2/2     10m

[root@k8s-master01 pv]# kubectl get pod
NAME                   READY   STATUS      RESTARTS   AGE
web-0                  1/1     Running     0          10m
web-1                  1/1     Running     0          12s

[root@k8s-master01 pv]# kubectl get pvc
NAME               STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-volume-web-0   Bound    nfs3-pv   7Gi        RWO            nfs            10m
pvc-volume-web-1   Bound    nfs4-pv   10Gi       RWO            nfs            18s

[root@k8s-master01 pv]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                      STORAGECLASS   REASON   AGE
nfs1-pv   1Gi        RWO            Recycle          Available                              nfs                     12m
nfs2-pv   4Gi        RWO            Recycle          Available                              nfs                     12m
nfs3-pv   7Gi        RWO            Recycle          Bound       default/pvc-volume-web-0   nfs                     11m
nfs4-pv   10Gi       RWO            Recycle          Bound       default/pvc-volume-web-1   nfs                     11m

因为 nfs3-pv 已经被绑定了,所以扩容的 Pod 只能绑定在除了 nfs3-pv 之外的合适的 nfs4-pv 上面。

-- end --


评论