kubernetes 静态PV与动态PV

大番茄 2020年03月22日 1,222次浏览

v1.16.7

https://kubernetes.io/docs/concepts/storage/storage-classes/

一、PV、PVC

https://kubernetes.io/docs/concepts/storage/persistent-volumes/
https://kubernetes.io/zh/docs/tasks/configure-pod-container/configure-persistent-volume-storage/

虽然pod也可以直接挂载存储,但是管理不方便,特别是pod的数量越来越多。 而且pod可能是由开发维护的,而存储却是由运维负责。通过PV,PVC分开就方便多了。

PV

PV(PersistentVolumes)
定义了后端存储以及其他的存储参数。用来配置存储的。只关心后端存储。

PV可以通过静态与动态两种方式创建。静态就是手动创建PV资源。
如果是静态,就需要创建大量的PV来定义不同的存储与参数,如性能,冗余等,来满足不同需求的PVC。

PV有下面几种状态:

Available(可用)-- PV是一个空闲资源,尚未绑定到任何pvc;
Bound(已绑定)-- PV已经绑定到pvc;
Released(已释放)-- 所绑定的pvc已被删除,但是资源尚未被集群回收;
Failed(失败)-- PV自动回收操作失败。


静态与动态是可以并存的,因为PVC会先匹配静态PV,如果不匹配才会尝试动态的方式。

当管理员创建的所有静态PV均与用户的PVC不匹配时,群集可能会尝试动态地为PVC专门配置一个卷。此供应基于StorageClasses:PVC必须请求storage class,并且管理员必须已经创建并配置了该类,才能进行动态供应。如果请求的类是"",就是禁用了动态配置。

静态的配置方式类似于这样,这是一个连接NFS的PV:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadOnlyMany
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
    - noatime
    - _netdev
  nfs:
    path: /data/nfs
    server: 172.100.102.66

capacity

设置存储容量,只有小于这个容量的PVC才能绑定。
现在存储大小是可以设置的唯一资源。将来的属性可能包括IOPS,吞吐量等。

volumeMode

卷模式,Filesystem, blockFilesystem是默认值。

storageClassName

存储的类别。手动指定。主要用来区分服务级别。只有相同存储类的PV,PVC才能绑定。 没有设置存储类的PVC,只能绑定同样没有类的PV(如果启用访问控制:DefaultStorageClass,可以给没有提供类的PVC设置默认的类)。

注意:类只是用来区分类型的参数。不管是静态还是动态。虽然动态里面需要创建存储类,但他们都是一个意思。先找静态指定类的PV,找不到再找指定类的动态供给。单独创建类也是为了对应不同的动态供给。

以前使用annotations里的volume.beta.kubernetes.io/storage-class代替storageClassName属性。此注释现在仍然有效;但是,将来的Kubernetes版本将不再支持它。

如:

  annotations:
    volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"

persistentVolumeReclaimPolicy

回收策略, PV是否自动回收。删除PVC以后是否删除对应存储里的数据,然后PV自动变为可用状态。
Retain: 手动回收。 pvc删除以后不会删除数据,pv会变成Released状态,无法使用。 需要手动删除重建。手动删除pv不会清除数据。 或者编辑pv,把claimRef 删除。
Recycle: 自动回收。 相当于 rm -rf /thevolume/*。 pvc删除,数据清理完毕以后Pv会自动恢复到可用状态。
Delete: 删除pv,pv连接的卷也会自动删除。 自动供给的NFS使用的是Delete,作用是删除NFS根目录下的卷目录。手动创建的不能使用Delete,缺少删除卷的插件。

目前只有NFS 和 HostPath 支持Recycle, AWS EBS, GCE PD, Azure Disk, and Cinder volumes 只支持Delete.

注意,nfs测试发现,就算是多个pv都在使用nfs里的东西,删除一个pvc也会执行rm -rf,如果各个pv都是使用的nfs根目录,那么数据就都没了。

accessModes

访问模式. 只有相同访问模式的PV,PVC才能绑定。
ReadWriteOnce: 该卷可以通过单个节点以读写方式安装
ReadOnlyMany: 该卷可以被许多节点只读安装
ReadWriteMany: –该卷可以被许多节点读写安装

在CLI中,访问模式缩写为:
RWO-ReadWriteOnce
ROX-ReadOnlyMany
RWX-ReadWriteMany
存储卷一次只能使用一种模式。

测试发现nfs的pv,pvc访问模式不管用。
使用ReadWriteOnce 也可以多节点写。
ReadOnlyMany 也可以写。

这些模式应该只是一个标识, 具体的实现还是后端的存储, 在nfs的测试中发现挂载参数都是PV mountOptions指定的, 没有只读方面的参数。

mountOptions

挂载选项

以前使用annotations里的volume.beta.kubernetes.io/mount-options代替mountOptions属性。此注释仍然有效;但是,它将在以后的Kubernetes版本中完全弃用。

PVC

PVC(PersistentVolumeClaim)
PVC与PV是一一对应的绑定关系。Pod挂载PVC。只需要创建满足需求的PVC,而PVC会再自动与满足需求的PV绑定,如果没有找到匹配的PV并且没有动态供给,Pod会一直处于Pending状态。

PVC只能手动创建,只有PV可以动态创建。 Pod使用PVC,因为PVC的名称不变,所以不管pod在哪个节点,都是使用相同的数据。

pvc创建很简单,如:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: task-pv-claim1
spec:
  storageClassName: slow
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 3Gi

storageClassName、accessModes、resources: pvc与pv的匹配会根据这些参数。

二、容器是怎么使用的

pod挂载PVC。

    spec:
      containers:
      - image: nginx
        name: nginx
        volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage
      volumes:
      - name: task-pv-storage
        persistentVolumeClaim:
          claimName: task-pv-claim1

kubele挂载存储到节点目录。 因为实际实际是kubelet执行挂载的,所以节点需要安装挂载需要的软件。 如:NFS的nfs-utils。 ceph的ceph-common。

172.100.102.66:/data/nfs nfs4      146G  1.8G  145G    2% /data/kubelet-data/pods/00a5d85d-fbc4-4c5c-b921-986b05f7b617/volumes/kubernetes.io~nfs/pv0003

mount -t nfs -o _netdev,noatime 172.100.102.66:/data/nfs /data/kubelet-data/pods/00a5d85d-fbc4-4c5c-b921-986b05f7b617/volumes/kubernetes.io~nfs/pv0003

然后docker会挂载目录到容器里。

      "Mounts": [
            {
                "Type": "bind",
                "Source": "/data/kubelet-data/pods/00a5d85d-fbc4-4c5c-b921-986b05f7b617/volumes/kubernetes.io~nfs/pv0003",
                "Destination": "/usr/share/nginx/html",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },

三、动态PV

https://kubernetes.io/docs/concepts/storage/storage-classes/

动态PV又叫做动态供给。就是在创建PVC以后,自动创建出PV。
只需要两步:

  1. 创建一个StorageClass,指定一些PV的回收策略以及挂载选项。最主要的是指定Provisioner
  2. Provisioner: 用来动态创建和管理PV的插件。每个存储都有不同的插件来管理PV,毕竟不同存储指定PV的参数是不一样的,从存储里清除数据的方式也都不一样。

Kubernetes内置了一些存储的Provisioner, 而一些没有内置的,就需要使用外部的Provisioner,通常就是运行一个Deployment。

查看内部支持的存储:
https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner

不支持的常用的有: NFS, CerphFS。

创建StorageClass

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain
allowVolumeExpansion: true
mountOptions:
  - debug
volumeBindingMode: Immediate

只要需要StorageClass可以动态供给PV,都需要provisionerparametersreclaimPolicy参数。

provisioner

就是上面提到的Provisioner。一般以kubernetes.io开头的都是内部支持的。

reclaimPolicy

回收策略,就是PV里的回收策略。只有RetainDelete两个值。默认Delete

allowVolumeExpansion

一些特定的存储支持卷扩展,通过修改PVC可以修改卷的大小。没有测试过。

mountOptions

挂载选项。

parameters

用来描述存储的参数,不同的存储可能会有不同的参数。

volumeBindingMode

绑卷定模式。有两个值ImmediateWaitForFirstConsumer
Immediate: 表示 PVC 创建完便会立即绑定PV和动态预配置。
WaitForFirstConsumer: 该模式将延迟PV的绑定和供应,直到创建使用PVC的Pod。将根据Pod的调度约束所指定的拓扑来选择或设置PV。

外部Provisioner

网址里是外部Provisioner的仓库。
https://github.com/kubernetes-incubator/external-storage

这里测试一下安装NFS的外部Provisioner
https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client/deploy

其实网址里已经包含了所有需要用到的yaml文件。
image.png
分别是创建StorageCalssProvisioner, 以及给Provisioner授权的RBAC文件。 外部Provisioner就是一个deployment。
两个test文件,一个是创建PVC的,一个是创建POD使用PVC的。

[root@k8s-master nfs]# ls
class.yaml  deployment.yaml  rbac.yaml
[root@k8s-master nfs]#

class文件一般不需要修改,里面的name一般也不修改,担心代码里有调用这个名称的。

[root@k8s-master nfs]# cat class.yaml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  archiveOnDelete: "false"

archiveOnDelete: 参数表示是否归档。 默认的回收模式是delete,pvc删除以后,pv也会删除,而pv所指定的nfs目录也会删除。
这个参数如果为 "true", pv所指定的目录会重命名,不会删除。如:
image.png

如果回收模式是Retain, 则不会重命名。


deployment里面的nfs地址与路径需要修改。如:

          env:
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs
            - name: NFS_SERVER
              value: 172.100.102.66
            - name: NFS_PATH
              value: /data/nfs
      volumes:
        - name: nfs-client-root
          nfs:
            server: 172.100.102.66
            path: /data/nfs

全部应用以后,看一下storageclass。

[root@k8s-master nfs]# kubectl get sc
NAME                  PROVISIONER      AGE
managed-nfs-storage   fuseim.pri/ifs   55s

name就是创建pvc的时候需要指定的。
PROVISIONER 指定的是deployment里面那个。

[root@k8s-master nfs]# kubectl get pods
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-865d4bfccc-d89w6   1/1     Running   0          35s

等deployment里的pod启动完成就可以了。

测试

创建一个pvc:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: task-pv-claim15
spec:
  storageClassName: managed-nfs-storage
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 4Gi

应用以后再看看pv与pvc。

[root@k8s-master pv]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS          REASON   AGE
pvc-0b7f5bbd-b1d8-4e31-94b7-20e8b5b22ff7   4Gi        ROX            Delete           Bound    default/task-pv-claim15   managed-nfs-storage            2s
[root@k8s-master pv]# kubectl get pvc
NAME              STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
task-pv-claim15   Bound    pvc-0b7f5bbd-b1d8-4e31-94b7-20e8b5b22ff7   4Gi        ROX            managed-nfs-storage   12s
[root@k8s-master pv]#

自动生成了pv,并且与pvc绑定了。

NFS存储里会创建一个目录来存放这个PVC的数据。

[root@haloackup nfs]# ls
default-task-pv-claim15-pvc-0b7f5bbd-b1d8-4e31-94b7-20e8b5b22ff7
[root@haloackup nfs]#

现在PV的回收模式是Delete, PVC一旦删除,NFS里的这个目录又会被删除。
如果担心数据,可以修改class.yaml,添加reclaimPolicy: Retain。如:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  archiveOnDelete: "false"
reclaimPolicy: Retain

class不能更新,需要删除重建,对已存在的pv没有影响。然后再创建PVC,所生成的PV回收模式就是Retain了。

Pod挂载PVC就不演示了, 它只是挂载PVC的名称, 动态静态都没有什么变化。
而PVC始终是要手动创建的。


注意: 如果手动创建了相同StorageClass的PV, 并且条件也匹配。 这个PVC会与PV绑定,不会再动态的创建PV。 动态与静态是没有冲突的。

删除保护

https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/#storage-object-in-use-protection

pvc删除以后, 数据是否保留是通过pv的回收模式。

而pvc的删除,只有在所有使用这个pvc的pod都删除以后才会删除。
pv也是一样, 只有与pvc解绑以后才能够删除。

其它

每个 PV 卷可以通过设置 节点亲和性 来定义一些约束,进而限制从哪些节点上可以访问此卷。 使用这些卷的 Pod 只会被调度到节点亲和性规则所选择的节点上执行。
https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/#node-affinity


pvc可以设置selector 来进一步的过滤pv。只有标签相匹配的pv能够绑定到pvc上。
https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/#selector