storageclass和本地持久化存儲


StorageClass

之前我們部署了PV PVC 的使用方法,但是前面的 PV 都是靜態的,什么意思?就是我要使用的一個 PVC 的話就必須手動去創建一個 PV,我們也說過這種方式在很大程度上並不能滿足我們的需求,比如我們有一個應用需要對存儲的並發度要求比較高,而另外一個應用對讀寫速度又要求比較高,特別是對於 StatefulSet 類型的應用簡單的來使用靜態的 PV 就很不合適了,這種情況下我們就需要用到動態 PV,也就是我們今天要講解的 StorageClass

創建

要使用 StorageClass,我們就得安裝對應的自動配置程序,比如我們這里存儲后端使用的是 nfs,那么我們就需要使用到一個 nfs-client 的自動配置程序,我們也叫它 Provisioner,這個程序使用我們已經配置好的 nfs 服務器,來自動創建持久卷,也就是自動幫我們創建 PV

  • 自動創建的 PV ${namespace}-${pvcName}-${pvName}這樣的命名格式創建在 NFS 服務器上的共享數據目錄中
  • 而當這個 PV 被回收后會以archieved-${namespace}-${pvcName}-${pvName}這樣的命名格式存在 NFS 服務器上。

當然在部署nfs-client之前,我們需要先成功安裝上 nfs 服務器,前面的課程中我們已經過了,服務地址是10.151.30.57,共享數據目錄是/data/k8s/,然后接下來我們部署 nfs-client 即可,我們也可以直接參考 nfs-client 的文檔:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client,進行安裝即可。

第一步:配置 Deployment,將里面的對應的參數替換成我們自己的 nfs 配置(nfs-client.yaml

kind: Deployment

apiVersion: extensions/v1beta1

metadata:

  name: nfs-client-provisioner

spec:

  replicas: 1

  strategy:

    type: Recreate

  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: 10.151.30.57

            - name: NFS_PATH

              value: /data/k8s

      volumes:

        - name: nfs-client-root

          nfs:

            server: 10.151.30.57

            path: /data/k8s

第二步:將環境變量 NFS_SERVER NFS_PATH 替換,當然也包括下面的 nfs 配置,我們可以看到我們這里使用了一個名為 nfs-client-provisioner serviceAccount,所以我們也需要創建一個 sa,然后綁定上對應的權限:(nfs-client-sa.yaml

apiVersion: v1

kind: ServiceAccount

metadata:

  name: nfs-client-provisioner

---

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: ["list", "watch", "create", "update", "patch"]

  - apiGroups: [""]

    resources: ["endpoints"]

    verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]

---

kind: ClusterRoleBinding

apiVersion: rbac.authorization.k8s.io/v1

metadata:

  name: run-nfs-client-provisioner

subjects:

  - kind: ServiceAccount

    name: nfs-client-provisioner

    namespace: default

roleRef:

  kind: ClusterRole

  name: nfs-client-provisioner-runner

  apiGroup: rbac.authorization.k8s.io

我們這里新建的一個名為 nfs-client-provisioner ServiceAccount,然后綁定了一個名為 nfs-client-provisioner-runner ClusterRole,而該ClusterRole聲明了一些權限,其中就包括對persistentvolumes的增、刪、改、查等權限,所以我們可以利用該ServiceAccount來自動創建 PV

第三步nfs-client Deployment 聲明完成后,我們就可以來創建一個StorageClass對象了:(nfs-client-class.yaml

apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

  name: course-nfs-storage

provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'

我們聲明了一個名為 course-nfs-storage StorageClass對象,注意下面的provisioner對應的值一定要和上面的Deployment下面的 PROVISIONER_NAME 這個環境變量的值一樣。

現在我們來創建這些資源對象吧:

$ kubectl create -f nfs-client.yaml

$ kubectl create -f nfs-client-sa.yaml

$ kubectl create -f nfs-client-class.yaml

創建完成后查看下資源狀態:

$ kubectl get pods

NAME                                             READY     STATUS             RESTARTS   AGE

...

nfs-client-provisioner-7648b664bc-7f9pk          1/1       Running            0          7h

...$ kubectl get storageclass

NAME                 PROVISIONER      AGE

course-nfs-storage   fuseim.pri/ifs   11s

新建

上面把StorageClass資源對象創建成功了,接下來我們來通過一個示例測試下動態 PV,首先創建一個 PVC 對象:(test-pvc.yaml)

kind: PersistentVolumeClaim

apiVersion: v1

metadata:  

name: test-pvc

spec:  

accessModes:  - ReadWriteMany  

resources:    

requests:      

storage: 1Mi

我們這里聲明了一個 PVC 對象,采用 ReadWriteMany 的訪問模式,請求 1Mi 的空間,但是我們可以看到上面的 PVC 文件我們沒有標識出任何和 StorageClass 相關聯的信息,那么如果我們現在直接創建這個 PVC 對象能夠自動綁定上合適的 PV 對象嗎?顯然是不能的(前提是沒有合適的 PV),我們這里有兩種方法可以來利用上面我們創建的 StorageClass 對象來自動幫我們創建一個合適的 PV

  • 在這個 PVC 對象中添加一個聲明 StorageClass 對象的標識,這里我們可以利用一個 annotations 屬性來標識,如下:

apiVersion: v1

kind: PersistentVolumeClaim

metadata:  

name: test-pvc  

annotations:

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

spec:  

accessModes:  - ReadWriteMany  

resources:    

requests:      

storage: 1Mi

$ kubectl create -f test-pvc.yaml

persistentvolumeclaim "test-pvc" created$ kubectl get pvc

NAME         STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE...

test-pvc     Bound     pvc-73b5ffd2-8b4b-11e8-b585-525400db4df7   1Mi        RWX            course-nfs-storage    2m...

我們可以看到一個名為 test-pvc PVC 對象創建成功了,狀態已經是 Bound 了,是不是也產生了一個對應的 VOLUME 對象,最重要的一欄是 STORAGECLASS,現在是不是也有值了,就是我們剛剛創建的 StorageClass 對象 course-nfs-storage

然后查看下 PV 對象呢:

$ kubectl get pv

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                STORAGECLASS          REASON    AGE...

pvc-73b5ffd2-8b4b-11e8-b585-525400db4df7   1Mi        RWX            Delete           Bound       default/test-pvc     course-nfs-storage              8m...

可以看到是不是自動生成了一個關聯的 PV 對象,訪問模式是 RWX,回收策略是 Delete,這個 PV 對象並不是我們手動創建的吧,這是通過我們上面的 StorageClass 對象自動創建的。這就是 StorageClass 的創建方法。

測試

接下來我們還是用一個簡單的示例來測試下我們上面用 StorageClass 方式聲明的 PVC 對象吧:(test-pod.yaml)

kind: Pod

apiVersion: v1

metadata:

  name: test-pod

spec:

  containers:

  - name: test-pod

    image: busybox

    imagePullPolicy: IfNotPresent

    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-pvc

上面這個 Pod 非常簡單,就是用一個 busybox 容器,在 /mnt 目錄下面新建一個 SUCCESS 的文件,然后把 /mnt 目錄掛載到上面我們新建的 test-pvc 這個資源對象上面了,要驗證很簡單,只需要去查看下我們 nfs 服務器上面的共享數據目錄下面是否有 SUCCESS 這個文件即可:

$ kubectl create -f test-pod.yaml

pod "test-pod" created

然后我們可以在 nfs 服務器的共享數據目錄下面查看下數據:

$ ls /data/k8s/

default-test-pvc-pvc-73b5ffd2-8b4b-11e8-b585-525400db4df7

我們可以看到下面有名字很長的文件夾,這個文件夾的命名方式是不是和我們上面的規則:${namespace}-${pvcName}-${pvName}是一樣的,再看下這個文件夾下面是否有其他文件:

$ ls /data/k8s/default-test-pvc-pvc-73b5ffd2-8b4b-11e8-b585-525400db4df7

SUCCESS

我們看到下面有一個 SUCCESS 的文件,是不是就證明我們上面的驗證是成功的啊。

另外我們可以看到我們這里是手動創建的一個 PVC 對象,在實際工作中,使用 StorageClass 更多的是 StatefulSet 類型的服務,StatefulSet 類型的服務我們也可以通過一個 volumeClaimTemplates 屬性來直接使用 StorageClass,如下:(test-statefulset-nfs.yaml)

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: nfs-web
spec:
  serviceName: "nginx"
  template:
    metadata:
      labels:
        app: nfs-web
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
      annotations:
        volume.beta.kubernetes.io/storage-class: course-nfs-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Mi
---
apiVersion: v1
kind: Service
metadata:
  name: nfs-web
spec:
  type: NodePort
  selector:
    app: nfs-web
  ports:
  - name: nfs-port
    protocol: TCP
    port: 80
    targetPort: web
    nodePort: 8613

實際上 volumeClaimTemplates 下面就是一個 PVC 對象的模板,就類似於我們這里 StatefulSet 下面的 template,實際上就是一個 Pod 的模板,我們不單獨創建成 PVC 對象,而用這種模板就可以動態的去創建了對象了,這種方式在 StatefulSet 類型的服務下面使用得非常多。

直接創建上面的對象:

$ kubectl create -f test-statefulset-nfs.yaml

statefulset.apps "nfs-web" created$ kubectl get pods

NAME                                             READY     STATUS              RESTARTS   AGE...

nfs-web-0                                        1/1       Running             0          1m

nfs-web-1                                        1/1       Running             0          1m

nfs-web-2                                        1/1       Running             0          33s...

創建完成后可以看到上面的3Pod 已經運行成功,然后查看下 PVC 對象:

$ kubectl get pvc

NAME            STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE...

www-nfs-web-0   Bound     pvc-cc36b3ce-8b50-11e8-b585-525400db4df7   1Gi        RWO            course-nfs-storage    2m

www-nfs-web-1   Bound     pvc-d38285f9-8b50-11e8-b585-525400db4df7   1Gi        RWO            course-nfs-storage    2m

www-nfs-web-2   Bound     pvc-e348250b-8b50-11e8-b585-525400db4df7   1Gi        RWO            course-nfs-storage    1m...

我們可以看到是不是也生成了3PVC 對象,名稱由模板名稱 name 加上 Pod 的名稱組合而成,這3PVC 對象也都是 綁定狀態了,很顯然我們查看 PV 也可以看到對應的3PV 對象:

$ kubectl get pv

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                   STORAGECLASS          REASON    AGE...                                                        1d

pvc-cc36b3ce-8b50-11e8-b585-525400db4df7   1Gi        RWO            Delete           Bound       default/www-nfs-web-0   course-nfs-storage              4m

pvc-d38285f9-8b50-11e8-b585-525400db4df7   1Gi        RWO            Delete           Bound       default/www-nfs-web-1   course-nfs-storage              4m

pvc-e348250b-8b50-11e8-b585-525400db4df7   1Gi        RWO            Delete           Bound       default/www-nfs-web-2   course-nfs-storage              4m...

查看 nfs 服務器上面的共享數據目錄:

$ ls /data/k8s/...

default-www-nfs-web-0-pvc-cc36b3ce-8b50-11e8-b585-525400db4df7

default-www-nfs-web-1-pvc-d38285f9-8b50-11e8-b585-525400db4df7

default-www-nfs-web-2-pvc-e348250b-8b50-11e8-b585-525400db4df7...

是不是也有對應的3個數據目錄,這就是我們的 StorageClass 的使用方法,對於 StorageClass 多用於 StatefulSet 類型的服務。

本地持久化存儲

本地持久化存儲(Local Persistent Volume)就是把數據存儲在POD運行的宿主機上,我們知道宿主機有hostPath和emptyDir,由於這兩種的特定不適用於本地持久化存儲。那么本地持久化存儲必須能保證POD被調度到具有本地持久化存儲的節點上。

但這里有個問題,通常我們先創建PV,然后創建PVC,這時候如果兩者匹配那么系統會自動進行綁定,哪怕是動態PV創建,也是先調度POD到任意一個節點,然后根據PVC來進行創建PV然后進行綁定最后掛載到POD中,可是本地持久化存儲有一個問題就是這種PV必須要先准備好,而且不一定集群所有節點都有這種PV,如果POD隨意調度肯定不行,如何保證POD一定會被調度到有PV的節點上呢?這時候就需要在PV中聲明節點親和,且POD被調度的時候還要考慮卷的分布情況。

定義PV

apiVersion: v1

kind: PersistentVolume

metadata:

  name: example-pv

spec:

  capacity:

    storage: 5Gi

  volumeMode: Filesystem

  accessModes:

  - ReadWriteOnce

  persistentVolumeReclaimPolicy: Delete

  storageClassName: local-storage

  local: # local類型

    path: /data/vol1  # 節點上的具體路徑

  nodeAffinity: # 這里就設置了節點親和

    required:

      nodeSelectorTerms:

      - matchExpressions:

        - key: kubernetes.io/hostname

          operator: In

          values:

          - node01 # 這里我們使用node01節點,該節點有/data/vol1路徑

如果你在node02上也有/data/vol1這個目錄,上面這個PV也一定不會在node02上,因為下面的nodeAffinity設置了主機名就等於node01。

另外這種本地PV通常推薦使用的是宿主機上單獨的硬盤設備,而不是和操作系統共有一塊硬盤,雖然可以這樣用。

定義存儲類

kind: StorageClass

apiVersion: storage.k8s.io/v1

metadata:

  name: local-storage

provisioner: kubernetes.io/no-provisioner

volumeBindingMode: WaitForFirstConsumer

這里的volumeBindingMode: WaitForFirstConsumer很關鍵,意思就是延遲綁定,當有符合PVC要求的PV不立即綁定。因為POD使用PVC,而綁定之后,POD被調度到其他節點,顯然其他節點很有可能沒有那個PV所以POD就掛起了,另外就算該節點有合適的PV,而POD被設置成不能運行在該節點,這時候就沒法了,延遲綁定的好處是,POD的調度要參考卷的分布。當開始調度POD的時候看看它要求的LPV在哪里,然后就調度到該節點,然后進行PVC的綁定,最后在掛載到POD中,這樣就保證了POD所在的節點就一定是LPV所在的節點。所以讓PVC延遲綁定,就是等到使用這個PVC的POD出現在調度器上之后(真正被調度之前),然后根據綜合評估再來綁定這個PVC。

定義PVC

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

  name: local-claim

spec:

  accessModes:

  - ReadWriteOnce

  resources:

    requests:

      storage: 5Gi

  storageClassName: local-storage

 

可以看到這個PVC是pending狀態,這也就是延遲綁定,因為此時還沒有POD。

定義POD

apiVersion: apps/v1

kind: Deployment

metadata:

  name: tomcat-deploy

spec:

  replicas: 1

  selector:

    matchLabels:

      appname: myapp

  template:

    metadata:

      name: myapp

      labels:

        appname: myapp

    spec:

      containers:

      - name: myapp

        image: tomcat:8.5.38-jre8

        ports:

        - name: http

          containerPort: 8080

          protocol: TCP

        volumeMounts:

          - name: tomcatedata

            mountPath : "/data"

      volumes:

        - name: tomcatedata

          persistentVolumeClaim:

            claimName: local-claim

 

  這個POD被調度到node01上,因為我們的PV就在node01上,這時候你刪除這個POD,然后在重建該POD,那么依然會被調度到node01上。

總結:本地卷也就是LPV不支持動態供給的方式,延遲綁定,就是為了綜合考慮所有因素再進行POD調度。其根本原因是動態供給是先調度POD到節點,然后動態創建PV以及綁定PVC最后運行POD;而LPV是先創建與某一節點關聯的PV,然后在調度的時候綜合考慮各種因素而且要包括PV在哪個節點,然后再進行調度,到達該節點后在進行PVC的綁定。也就說動態供給不考慮節點,LPV必須考慮節點。所以這兩種機制有沖突導致無法在動態供給策略下使用LPV。換句話說動態供給是PV跟着POD走,而LPV是POD跟着PV走。

Storageclass參考:https://www.cnblogs.com/fuyuteng/p/11017630.html

本地持久化存儲(LPV)參考:https://www.cnblogs.com/rexcheny/p/10925464.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM