k8s-storage-class

張鐵牛發表於2022-01-19

1. 簡介

StorageClass 為管理員提供了描述儲存 "類" 的方法。

通過StorageClass的定義,管理員可以將儲存資源定義為某種類別(Class),正如儲存裝置對於自身的配置描述(Profile),例如 "快速儲存" "慢速儲存" "有資料冗餘" "無資料冗餘"等。使用者根據StorageClass的描述就能夠直觀得知各種儲存資源的特性,就可以根據應用對儲存資源的需求去申請儲存資源了。

StorageClass作為對儲存資源的抽象定義,對使用者設定的PVC申請遮蔽後端的細節,一方面減輕使用者對於儲存資源細節的關注,另一方面也減輕了管理員手工管理PV的工作,由系統自動完成PV的建立和繫結,實現了動態的資源供應。

每個 StorageClass 都包含 provisionerparametersreclaimPolicy 欄位, 這些欄位會在 StorageClass 需要動態分配 PersistentVolume 時會使用到。

StorageClass 物件的命名很重要,使用者使用這個命名來請求生成一個特定的類。 當建立 StorageClass 物件時,管理員設定 StorageClass 物件的命名和其他引數,一旦建立了物件就不能再對其更新。

2. 建立Provisioner

如果需要使用StorageClass,我們就需要安裝對應的自動配置程式,比如我們這裡後端採用的是nfs,那麼我們就需要使用到一個nfs-client的自動配置程式,我們也叫它Provisioner,這個程式使用我們已經配置好的nfs伺服器,來自動建立持久卷,也就是自動幫我們建立PV。

這裡也可以直接參考nfs-client的官方文件

https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client/deploy

自動建立的PV以{namespace}-${pvcname}-${pvname}進行命名到伺服器上

如果開啟了archiveOnDelete功能,則當pv被回收後會以archieved-${namespace}-${pvcname}-${pvname}格式存在伺服器上

2.1 建立deployment

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
              # nfs server 地址
              value: 192.168.0.201
            - name: NFS_PATH
              # nfs 掛載點
              value: /nfs/data
      volumes:
        - name: nfs-client-root
          nfs:
            # nfs server 地址
            server: 192.168.0.201
            # nfs 掛載點
            path: /nfs/data

2.2 rbac

接下來我們還需要建立一個serveraccount,用於將nfs-client-provisioner中的ServiceAccount繫結到一個nfs-client-provisioner-runner的ClusterRole。而該ClusterRole宣告瞭一些許可權,其中就包括了對persistentvolumes的增刪改查,所以我們就可以利用ServiceAccount來自動建立PV

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

2.3 storage-class

provisioner值可以自定義但需要和deployment的 template.spec.containers.env PROVISIONER_NAME 保持一致

這裡我們宣告瞭一個名為managed-nfs-storage的Storageclass物件

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    # 設定為default StorageClass,如果建立pvc時不指定StorageClass,則會使用當前的StorageClass
    storageclass.kubernetes.io/is-default-class: "true"
  name: managed-nfs-storage
provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  # 刪除PVC時不會保留資料
  archiveOnDelete: "false"
# 回收策略:刪除
reclaimPolicy: Delete
#允許pvc建立後擴容
allowVolumeExpansion: true

回收策略

由 StorageClass 動態建立的 PersistentVolume 會在類的 reclaimPolicy 欄位中指定回收策略,可以是 Delete 或者 Retain。如果 StorageClass 物件被建立時沒有指定 reclaimPolicy,它將預設為 Delete

通過 StorageClass 手動建立並管理的 PersistentVolume 會使用它們被建立時指定的回收政策。

卷繫結模式

volumeBindingMode 欄位控制了卷繫結和動態製備 應該發生在什麼時候。

預設情況下,Immediate 模式表示一旦建立了 PersistentVolumeClaim 也就完成了卷繫結和動態製備。 對於由於拓撲限制而非叢集所有節點可達的儲存後端,PersistentVolume 會在不知道 Pod 排程要求的情況下繫結或者製備。

叢集管理員可以通過指定 WaitForFirstConsumer 模式來解決此問題。 該模式將延遲 PersistentVolume 的繫結和製備,直到使用該 PersistentVolumeClaim 的 Pod 被建立。 PersistentVolume 會根據 Pod 排程約束指定的拓撲來選擇或製備。這些包括但不限於 資源需求節點篩選器pod 親和性和互斥性、 以及汙點和容忍度

說明:

如果選擇使用 WaitForFirstConsumer,請不要在 Pod 規約中使用 nodeName 來指定節點親和性。 如果在這種情況下使用 nodeName,Pod 將會繞過排程程式,PVC 將停留在 pending 狀態。

相反,在這種情況下,可以使用節點選擇器作為主機名,如下所示

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  nodeSelector:
    kubernetes.io/hostname: kube-01
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
        claimName: task-pv-claim
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage

2.4 建立資源

建立資訊如下

資源資訊如下

3. 驗證動態建立pv

首先建立個pvc,看下是否會動態的建立pv

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
  annotations:
    #storageclass 名稱
    volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
spec:
  #訪問模式
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      #請求資料大小
      storage: 1024Mi

從圖中可以看出,我們不需要的手動建立pv,只需要建立一個pvc,storageclass會自動將pv給建立並且關聯,就很nice

使用pvc

我們這裡建立一個deployment,使用下剛建立的pvc: test-claim

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-pv-demo
  name: nginx-pv-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-pv-demo
  template:
    metadata:
      labels:
        app: nginx-pv-demo
    spec:
      containers:
      - image: nginx
        imagePullPolicy: IfNotPresent
        name: nginx
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
      volumes:
        - name: html
          persistentVolumeClaim:
            claimName: test-claim

測試演示步驟如下: