Kubernetes對象中的PersistentVolume、PersistentVolumeClaim和StorageClass的概念關系


Kubernetes容器要持久化數據,離不開volume,k8s的volume和Docker原生概念中的volume有一些差別,不過本次不講這個,本次要明確的是k8s持久化數據用到的幾個對象PersistentVolume、PersistentVolumeClaim和StorageClass,首先明確這既然都是k8s對象,就可以通過API來創建的。

        k8s的volume支持的類型有很多,例如emptyDir、hostPath、nfs等,這些相對好理解,還有一種就是ersistentVolumeClaim,剛開始接觸的時候不太理解這個對象該如何使用,本次主要介紹ersistentVolumeClaim相關的概念和使用方法。

        開始先要提一下PersistentVolume(PV)對象,PersistentVolume和Volume一樣是群集中的一塊存儲區域,然而Kubernetes將PersistentVolume抽象成了一種集群資源,類似於集群中的節點(Node)對象,這意味着我們可以使用Kubernetes API來創建PersistentVolume對象。PV與Volume最大的不同是PV擁有着獨立於Pod的生命周期。

        而PersistentVolumeClaim(PVC)代表了用戶對PV資源的請求。用戶需要使用PV資源時,只需要創建一個PVC對象(包括指定使用何種存儲資源,使用多少GB,以何種模式使用PV等信息),Kubernetes會自動為我們分配我們所需的PV。如果把PersistentVolume類比成集群中的Node,那么PersistentVolumeClaim就相當於集群中的Pod,Kubernetes為Pod分配可用的Node,類似的也可以理解成為PersistentVolumeClaim分配可用的PersistentVolume。

1、靜態創建PV對象

可以直接靜態創建一個PV對象,作為一個存儲供PVC使用,創建PV主要有下面幾個參數

accessModes 訪問模式有下面三種:

  •      ReadWriteOnce(RWO):是最基本的方式,可讀可寫,但只支持被單個 Pod 掛載。
  •      ReadOnlyMany(ROX):只讀模式,可以被多個 Pod 掛載。
  •      ReadWriteMany(RWX):可讀可寫,並且可以被被多個 Pod 掛載。
persistentVolumeReclaimPolicy 回收策略,即 PVC 釋放卷的時候 PV 該如何操作,有下面三種:
  •      Retain,不清理,刪除PVC時,PV仍然存在並標記為“released”(需要刪除時需要手動清理)
  •      Recycle,刪除數據,對卷執行清理(rm -rf / thevolume / *),並使其再次可用於新索引(只有 NFS 和 HostPath 支持)
  •      Delete,刪除存儲資源,會從Kubernetes中刪除PV對象,以及外部基礎結構中的關聯存儲資產,例如AWS EBS,GCE PD,Azure磁盤或Cinder卷
#靜態創建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

    定義PV時,我們需要指定其底層存儲的類型,例如上文中創建的PV,底層使用nfs存儲,支持的類型很多,例如awsElasticBlockStore、FC、nfs、RBD、CephFS、Hostpath、StorageOS等等,可以查看官方文檔。

    可以查看當前集群下創建的PV對象,kubectl get PersistentVolume --all-namespaces

2、靜態創建PVC對象

    創建PV之后,並沒有被使用,如果想使用這個PV就需要創建PVC了,最后在pod中指定使用這個PVC而建立起pod和PV的關系。

#靜態創建PVC
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: myclaim spec: accessModes: - ReadWriteOnce
volumeMode: Filesystem resources: requests: storage: 8Gi storageClassName: slow selector: matchLabels: release: "stable"
#pod使用PVC
kind: Pod
apiVersion: v1
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: dockerfile/nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

    分析一下上面的代碼:

  • 創建PVC時指定了accessModes = ReadWriteOnce。這表明這個PVC希望使用accessModes = ReadWriteOnce的PV。
  • 創建PVC時指定了volumeMode= Filesystem。這表明這個PVC希望使用volumeMode= Filesystem的PV。
  • 創建PVC時指定了storageClassName: slow,此配置用於綁定PVC和PV,意思是這個PVC希望使用storageClassName=slow的PV。我們可以看到最上面創建PV時也包含storageClassName=slow的配置。
  • PVC還可以指定PV必須滿足的Label,如加了selector匹配matchLabels: release: "stable"。這表明此PVC希望使用Label:release: "stable"的PV。
  • 最后是resources聲明,跟pod一樣可以聲明使用特定數量的資源,storage: 8Gi表明此PVC希望使用8G的Volume資源。

      通過上面的分析,我們可以看到PVC和PV的綁定,不是簡單的通過Label來進行。而是要綜合storageClassName,accessModes,matchLabels以及storage來匹配符合條件的PV進行綁定。

3、動態創建PV對象

    上面我們通過描述文件靜態創建PV對象最終完成和pod的綁定,這種直接通過描述文件創建PV的方式稱為靜態創建,這樣的創建方式有弊端,假如我們創建PV時指定大小為50G,而PVC請求80G的PV,那么此PVC就無法找到合適的PV來綁定。因此實際生產中更多的使用PV的動態創建。

    PV的動態創建依賴於StorageClass對象。我們不需要手動創建任何PV,所有的工作都由StorageClass為我們完成,可以查看集群中的StorageClass信息 ,kubectl get StorageClass --all-namespaces

 

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://192.168.10.100:8080"
  restuser: ""
  secretNamespace: ""
  secretName: ""
reclaimPolicy: Retain
allowVolumeExpansion: true

 

 這個例子使用創建了一個基於glusterfs分布式存儲的StorageClass,只有allowVolumeExpansion=teue時,才能擴展PVC,要為PVC請求更大的卷,請編輯PVC對象並指定更大的大小,這會觸發底層PersistentVolume的卷的擴展。永遠不會創建新的,而是調整現有卷的大小。

StorageClass的定義包含四個部分:

  • provisioner:指定 Volume 插件的類型,包括內置插件(如kubernetes.io/glusterfs、kubernetes.io/aws-ebs)和外部插件(如 external-storage 提供的 ceph.com/cephfs)。
  • parameters:指定 provisioner 的選項,比如 glusterfs 支持 resturl、restuser 等參數。
  • mountOptions:指定掛載選項,當 PV 不支持指定的選項時會直接失敗。比如 NFS 支持 hard 和 nfsvers=4.1 等選項。
  • reclaimPolicy:指定回收策略,同 PV 的回收策略。

        手動創建的PV時,我們指定了storageClassName=slow的配置項,然后Pod定義中也通過指定storageClassName=slow,從而完成綁定。而通過StorageClass實現動態PV時,我們只需要指定StorageClass的metadata.name即可,這個名稱非常重要,用戶通過名稱類請求特定的存儲類,儲類的對象一旦被創建,name將不能再更改。
        回到上文中創建PVC的例子,此時PVC指定了storageClassName=slow。那么Kubernetes會在集群中尋找是否存在metadata.name=slow的StorageClass,如果存在,此StorageClass會自動為此PVC創建一個accessModes = ReadWriteOnce,並且大小為8GB的PV。

 

或者直接寫到一起

#上面的部分省略了
        volumeMounts:
        - name: data-dir
          mountPath: /var/mysql/data
      #volumes:
      #- name: data-dir
      #  hostPath:
      #    path: /opt/mysql

  volumeClaimTemplates:
  - metadata:
#annotations: volume.alpha.kubernetes.io/storage-class: xxxx creationTimestamp: null name: data-dir spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: slow volumeMode: Filesystem status: phase: Pending

 

        下面是參考官方的一種比較通用的寫法,基於helm的,直接將PVC的代碼寫到volumes的后面,根據values.yaml中的參數生成是掛載PVC的存儲還是其他存儲,可以作為以后自己寫腳本的參考

      volumes:
      - name: config
        configMap:
          name: {{ template "redis-ha.fullname" . }}-configmap
      - name: probes
        configMap:
          name: {{ template "redis-ha.fullname" . }}-probes
      {{- if .Values.sysctlImage.mountHostSys }}
      - name: host-sys
        hostPath:
          path: /sys
      {{- end }}
{{- if .Values.persistentVolume.enabled }}
  volumeClaimTemplates:
  - metadata:
      name: data
      annotations:
      {{- range $key, $value := .Values.persistentVolume.annotations }}
        {{ $key }}: {{ $value }}
      {{- end }}
    spec:
      accessModes:
      {{- range .Values.persistentVolume.accessModes }}
        - {{ . | quote }}
      {{- end }}
      resources:
        requests:
          storage: {{ .Values.persistentVolume.size | quote }}
    {{- if .Values.persistentVolume.storageClass }}
    {{- if (eq "-" .Values.persistentVolume.storageClass) }}
      storageClassName: ""
    {{- else }}
      storageClassName: "{{ .Values.persistentVolume.storageClass }}"
    {{- end }}
    {{- end }}
{{- else if .Values.hostPath.path }}
      - name: data
        hostPath:
          path: {{ tpl .Values.hostPath.path .}}
{{- else }}
      - name: data
        emptyDir: {}
{{- end }}

 

總結一下整個過程

1)集群管理員預先創建存儲類(StorageClass);

2)用戶創建使用存儲類的持久化存儲聲明(PVC:PersistentVolumeClaim);

3)存儲持久化聲明通知系統,它需要一個持久化存儲(PV: PersistentVolume);

4)系統讀取存儲類的信息;

5)系統基於存儲類的信息,在后台自動創建PVC需要的PV;

6)用戶創建一個使用PVC的Pod;

7)Pod中的應用通過PVC進行數據的持久化;

8)而PVC使用PV進行數據的最終持久化處理。

 

官方文檔 https://kubernetes.io/docs/concepts/storage/persistent-volumes/

 


免責聲明!

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



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