StorageClass

之前介绍了PV及PVC的使用方式,读者从中可以发现,这是一种静态创建PV的方法,先要创建各种固定大小的PV,而这些PV都是手动创 建的,过程非常麻烦。有时开发人员在申请PVC资源时,不一定有匹配条件的PV可用,这又带来了新的问题。

为了解决这类问题,Kubernetes提供了StorageClass抽象来动态创建PV,StorageClass大大简化了PV的创建过程。

当申请PVC资源时,如果匹配到满足条件的StorageClass,就会自动为PVC创建对应大小的PV并进行绑定。

StorageClass是通过存储分配器(provisioner)来动态分配PV的,但是Kubernetes官方内置的存储分配器并不支持NFS,所以需要额 外安装NFS存储分配器。

1. 安装存储分配器

NFS 存储分配器的安装过程并不复杂。首先,执行以下命令,下载 NFS 存储分配器的deployment.yaml配置。

$ git clone https://github.com/kubernetes-retired/external-storage.git
$ cd external-storage/nfs-client/deploy/

下载完成后,使用以下命令编辑yaml配置文件。修改文件中的部分配置,然后保存,具体修改内容参见下方粗体 代码。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs
            - name: NFS_SERVER
              value: 192.168.1.60
            - name: NFS_PATH
              value: /nfstest
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.1.60
            path: /nfstest

在env属性中,PROVISIONER_NAME表示存储分配器的名称,nfs-gitee-test。

NFS_SERVER表示NFS服务器地址,在本例中为之前配置的192.168.1.60。

NFS_PATH表示NFS共享目录地址,在本例中为/nfstest。在volumes属性中,也将server与path改为之前配置的NFS服务器地址和共享目录。

class.yaml中的provisioner要与deployment.yaml中一致

安装完成后可以创建StorageClass了。首先,创建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"

该模板的主要含义如下。

  • apiVersion表示使用的API版本,storage.k8s.io/v1表示使用Kubernetes API的稳定版本。

  • kind表示要创建的资源对象,这里使用关键字StorageClass。

  • metadata中的name属性定义了当前资源的名称。

  • provisioner表示存储分配器的名称。这里需要使用之前在Deployment模板中配置的PROVISIONER_NAME,即 fuseim.pri/ifs。

  • parameters表示该资源对象的参数。若archiveOnDelete为false,表示与之关联的PVC在删除时,它所绑定的PV不会被存储 分配器保留;若为true,则相反

接下来修改测试pvc和pod,用于验证

test-claim.yaml

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
  annotations:
    volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Mi

test-pod.yaml

kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: busybox:latest
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"
    volumeMounts:
      - name: nfs-pvc
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: test-claim

接下来,执行以下命令,创建StorageClass。

$ kubectl apply -f external-storage/nfs-client/deploy/

创建完成后,可以通过以下命令查看StorageClass

$ kubectl get sc|grep managed-nfs-storage
managed-nfs-storage           fuseim.pri/ifs                                           Delete          Immediate           false                  55s

$ kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
test-claim   Bound    pvc-86a26712-ea2e-4c92-9075-6dc38343247e   1Mi        RWX            managed-nfs-storage   2m32s

$ kubectl get deployment
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
nfs-client-provisioner   1/1     1            1           2m4s

$ kubectl get pod
NAME                                      READY   STATUS      RESTARTS   AGE
nfs-client-provisioner-6794dc7784-7frn7   1/1     Running     0          2m55s
test-pod                                  0/1     Completed   0          2m54s

使用以下命令,可以查询StorageClass资源的详情。

$ kubectl describe sc managed-nfs-storage
Name:            managed-nfs-storage
IsDefaultClass:  No
Annotations:     kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{},"name":"managed-nfs-storage"},"parameters":{"archiveOnDelete":"false"},"provisioner":"fuseim.pri/ifs"}

Provisioner:           fuseim.pri/ifs
Parameters:            archiveOnDelete=false
AllowVolumeExpansion:  <unset>
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     Immediate
Events:                <none>

此时可通过$ kubectl describe pv命令查看详情。可以看到该动态PV在NFS服务器的共享目录上创建了一个专属于它的子目录,

$ kubectl describe pv pvc-86a26712-ea2e-4c92-9075-6dc38343247e
Name:            pvc-86a26712-ea2e-4c92-9075-6dc38343247e
Labels:          <none>
Annotations:     pv.kubernetes.io/provisioned-by: fuseim.pri/ifs
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    managed-nfs-storage
Status:          Bound
Claim:           default/test-claim
Reclaim Policy:  Delete
Access Modes:    RWX
VolumeMode:      Filesystem
Capacity:        1Mi
Node Affinity:   <none>
Message:
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    192.168.1.60
    Path:      /nfstest/default-test-claim-pvc-86a26712-ea2e-4c92-9075-6dc38343247e
    ReadOnly:  false
Events:        <none>

在本例中为/nfstest/default-test-claim-pvc-86a26712-ea2e-4c92-9075-6dc38343247e

我们查看nfs的数据如下

$ ll /data/nfs/nfstest/default-test-claim-pvc-86a26712-ea2e-4c92-9075-6dc38343247e/SUCCESS
-rw-r--r-- 1 root root 0 Apr 17 20:39 /data/nfs/nfstest/default-test-claim-pvc-86a26712-ea2e-4c92-9075-6dc38343247e/SUCCESS

接下来,删除PVC。先执行$ kubectl delete pvc exampleforstorageclass命令,然后再执行$ kubectl get pv命令获取PV。可以发现,当删除PVC时,它所绑定的动态PV也会自动删除。

$ kubectl delete -f external-storage/nfs-client/deploy/

删除动态PV后,NFS服务器的共享目录上创建的专属子目录也会被删除。

1.1 使用nfs-server-provisioner

kubernetes部署nfs-subdir-external-provisioner

https://blog.csdn.net/networken/article/details/86697018

1.2 在Kubernetes中部署nfs storageClass

1.3 NFS动态存储卷报错问题记录

我的问题:

创建pvc后状态一直是pending

排查步骤: 1.通过kubectl describe命令查看错误提示信息,信息中有:

waiting for a volume to be created, either by external provisioner “fuseim.pri/ifs” or manually created by system administrator。

2.通过kubectl logs命令查看pod(nfs-client-provisioner)日志,日志中有:

unexpected error getting claim reference: selfLink was empty, can’t make reference。

3.使用第二步骤的信息去网上查找,原来是1.20版本(我的是1.20.2)默认禁止使用selfLink。

解决方法

1.如果是通过yaml文件部署kube-apiserver的(我的是这种方式)
(1) 通过find / -name kube-apiserver.yaml 命令,找到kube-apiserver.yaml文件
(2)在文件中添加 - --feature-gates=RemoveSelfLink=false,

# 然后重启kube-api的pod让其生效
kubectl delete pod/kube-apiserver-gitee-k8s-m1 -n kube-system
kubectl delete pod/kube-apiserver-gitee-k8s-m2 -n kube-system
kubectl delete pod/kube-apiserver-gitee-k8s-m3 -n kube-system



2.如果是通过二进制部署kube-apiserver (我的是这种方式)
(1)systemctl status kube-apiserver 通过这个命令找到 kube-apiserver.service文件
(2)最好先备份,然后再修改,即在 kube-apiserver.service中添加 --feature-gates=RemoveSelfLink=false

2. PV和PVC的生命周期

PV是Kubernetes集群的存储资源,而PVC则代表着资源需求。创建PVC时对PV发起的使用申请,即为“绑定”。PV和PVC是一一对应的关系,可用于响应PVC申请的PV必须要能够容纳PVC的请求条件,它们二者的交互遵循如下生命周期。

2.1 存储供给

存储供给(Provisioning)是指为PVC准备可用PV的机制。Kubernetes支持两种PV供给方式:静态供给和动态供给。

(1)静态供给

静态供给是指由集群管理员手动创建一定数量的PV的资源供应方式。这些PV负责处理存储系统的细节,并将其抽象成易用的存储资源供用户使用。静态提供的PV可能属于某存储类(StorageClass),也可能没有存储类,这一点取决于管理员的设定。

(2)动态供给

不存在某静态的PV匹配到用户的PVC申请时,Kubernetes集群会尝试为PVC动态创建符合需求的PV,此即为动态供给。这种方式依赖于存储类的辅助,PVC必须向一个事先存在的存储类发起动态分配PV的请求,没有指定存储类的PVC请求会被禁止使用动态创建PV的方式。

另外,为了支持使用动态供给机制,集群管理员需要为准入控制器(admission controller)启用“DefaultStorageClass”选项,这一点通过“–admission-control”命令行选项为API Server进行设定即可,后文会对准入控制器予以描述。

2.2 存储绑定

用户基于一系列存储需求和访问模式定义好PVC后,Kubernetes系统的控制器即会为其查找匹配的PV,并于找到之后在此二者之间建立起关联关系,而后它们二者之间的状态即转为“绑定”(Binding)。若PV是为PVC而动态创建的,则该PV专用于其PVC。

若是无法为PVC找到可匹配的PV,则PVC将一直处于未绑定(unbound)状态,直到有符合条件的PV出现并完成绑定方才可用。

(1)存储使用(Using)

Pod资源基于persistenVolumeClaim卷类型的定义,将选定的PVC关联为存储卷,而后即可为内部的容器所使用。对于支持多种访问模式的存储卷来说,用户需要额外指定要使用的模式。一旦完成将存储卷挂载至Pod对象内的容器中,其应用即可使用关联的PV提供的存储空间。

(2)PVC保护(Protection)

为了避免使用中的存储卷被移除而导致数据丢失,Kubernetes自1.9版本起引入了“PVC保护机制”。启用了此特性后,万一有用户删除了仍处于某Pod资源使用中的PVC时,Kubernetes不会立即予以移除,而是推迟到不再被任何Pod资源使用后方才执行删除操作。

处于此种阶段的PVC资源的status字段为“Termination”,并且其Finalizers字段中包含“kubernetes.io/pvc-protection”。

2.3 存储回收(Reclaiming)

完成存储卷的使用目标之后,即可删除PVC对象以便进行资源回收。不过,至于如何操作则取决于PV的回收策略。目前,可用的回收策略有三种:Retained、Recycled和Deleted。

(1)留存(Retain)

留存策略意味着在删除PVC之后,Kubernetes系统不会自动删除PV,而仅仅是将它置于“释放”(released)状态。不过,此种状态的PV尚且不能被其他PVC申请所绑定,因为此前的申请生成的数据仍然存在,需要由管理员手动决定其后续处理方案。这就意味着,如果想要再次使用此类的PV资源,则需要由管理员按下面的步骤手动执行删除操作。

1)删除PV,这之后,此PV的数据依然留存于外部的存储之上。

2)手工清理存储系统上依然留存的数据。

3)手工删除存储系统级的存储卷(例如,RBD存储系统上的image)以释放空间,以便再次创建,或者直接将其重新创建为PV。

(2)回收(Recycle)

如果可被底层存储插件支持,资源回收策略会在存储卷上执行数据删除操作并让PV资源再次变为可被Claim。另外,管理员也可以配置一个自定义的回收器Pod模板,以便执行自定义的回收操作。不过,此种回收策略行将废弃。

(3)删除(Delete)

对于支持Deleted回收策略的存储插件来说,在PVC被删除后会直接移除PV对象,同时移除的还有PV相关的外部存储系统上的存储资产(asset)。支持这种操作的存储系统有AWS EBS、GCE PD、Azure Disk或Cinder。动态创建的PV资源的回收策略取决于相关存储类上的定义,存储类上相关的默认策略为Delete,大多数情况下,管理员都需要按用户期望的处理机制修改此默认策略,以免导致数据非计划内的误删除。

2.4 扩展PVC

Kubernetes自1.8版本起增加了扩展PV空间的特性,截至目前,它所支持的扩展PVC机制的存储卷共有以下几种。

  • gcePersistentDisk

  • awsElasticBlockStore

  • Cinder

  • glusterfs

  • rbd

“PersistentVolumeClaimResize”准入插件负责对支持空间大小变动的存储卷执行更多的验证操作,管理员需要事先启用此插件才能使用PVC扩展机制,那些将“allowVolume Expansion”字段的值设置为“true”的存储类即可动态扩展存储卷空间。随后,用户改动Claim请求更大的空间即能触发底层PV空间扩展从而带来PVC存储卷的扩展。

对于包含文件系统的存储卷来说,只有在有新的Pod资源基于读写模式开始使用PVC时才会执行文件系统的大小调整操作。换句话说,如果某被扩展的存储卷已经由Pod资源所使用,则需要重建此Pod对象才能触发文件系统大小的调整操作。支持空间调整的文件系统仅有XFS和EXT3/EXT4。