PV、PVC、StorageClass講解

PV、PVC、StorageClass講解

爲了方便開發人員更加容易的使用存儲纔出現的概念。一般咱們在一個POD中定義使用存儲是這樣的方式,咱們以hostpath類型來講:html

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - image: nginx
    name: mynginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: html
  volumes:
  - name: html # 名稱
    hostPath: # 存儲類型
      path: /data # 物理節點上的真實路徑
      type: Directory # 若是該路徑不存在講如何處理,Directory是要求目錄必須存在

其實經過上面能夠看出來,不管你使用什麼類型的存儲你都須要手動定義,指明存儲類型以及相關配置。這裏的hostpath類型仍是比較簡單的,若是是其餘類型的好比分佈式存儲,那麼這對開發人員來講將會是一種挑戰,由於畢竟真正的存儲是由存儲管理員來設置的他們會更加了解,那麼有沒有一種方式讓咱們使用存儲更加容易,對上層使用人員屏蔽底層細節呢?答案是確定的,這就是PV、PVC的概念。不過須要注意的是咱們在集羣中一般不使用hostPath、emptyDir這種類型,除非你只是測試使用。node

什麼是PV

PV全稱叫作Persistent Volume,持久化存儲卷。它是用來描述或者說用來定義一個存儲卷的,這個一般都是有運維或者數據存儲工程師來定義。好比下面咱們定義一個NFS類型的PV:nginx

apiVersion: v1
kind: PersistentVolume
metadata:  # PV創建不要加名稱空間,由於PV屬於集羣級別的
  name: nfs-pv001  # PV名稱
  labels: # 這些labels能夠不定義
    name: nfs-pv001
    storetype: nfs
spec:  # 這裏的spec和volumes裏面的同樣
  storageClassName: normal
  accessModes:  # 設置訪問模型
    - ReadWriteMany
    - ReadWriteOnce
    - ReadOnlyMany
  capacity: # 設置存儲空間大小
    storage: 500Mi
  persistentVolumeReclaimPolicy: Retain # 回收策略
  nfs:
    path: /work/volumes/v1
    server: stroagesrv01.contoso.com

accessModes:支持三種類型git

  • ReadWriteMany 多路讀寫,卷能被集羣多個節點掛載並讀寫github

  • ReadWriteOnce 單路讀寫,卷只能被單一集羣節點掛載讀寫算法

  • ReadOnlyMany 多路只讀,卷能被多個集羣節點掛載且只能讀docker

這裏的訪問模型總共有三種,可是不一樣的存儲類型支持的訪問模型不一樣,具體支持什麼須要查詢官網。好比咱們這裏使用nfs,它支持所有三種。可是ISCI就不支持ReadWriteMany;HostPath就不支持ReadOnlyMany和ReadWriteMany。api

persistentVolumeReclaimPolicy:也有三種策略,這個策略是當與之關聯的PVC被刪除之後,這個PV中的數據如何被處理tomcat

  • Retain 當刪除與之綁定的PVC時候,這個PV被標記爲released(PVC與PV解綁但尚未執行回收策略)且以前的數據依然保存在該PV上,可是該PV不可用,須要手動來處理這些數據並刪除該PV。服務器

  • Delete 當刪除與之綁定的PVC時候

  • Recycle 這個在1.14版本中以及被廢棄,取而代之的是推薦使用動態存儲供給策略,它的功能是當刪除與該PV關聯的PVC時,自動刪除該PV中的全部數據

注意:PV必須先與POD建立,並且只能是網絡存儲不能屬於任何Node,雖然它支持HostPath類型但因爲你不知道POD會被調度到哪一個Node上,因此你要定義HostPath類型的PV就要保證全部節點都要有HostPath中指定的路徑。

PVC

PVC是用來描述但願使用什麼樣的或者說是知足什麼條件的存儲,它的全稱是Persistent Volume Claim,也就是持久化存儲聲明。開發人員使用這個來描述該容器須要一個什麼存儲。好比下面使用NFS的PVC:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc001
  namespace: default
  labels: # 這些labels能夠不定義
    name: nfs-pvc001
    storetype: nfs
    capacity: 500Mi
spec:
  storageClassName: normal
  accessModes:  # PVC也須要定義訪問模式,不過它的模式必定是和現有PV相同或者是它的子集,不然匹配不到PV
  - ReadWriteMany
  resources: # 定義資源要求PV知足這個PVC的要求才會被匹配到
    requests:
      storage: 500Mi  # 定義要求有多大空間

這個PVC就會和上面的PV進行綁定,爲何呢?它有一些原則:

  1. PV和PVC中的spec關鍵字段要匹配,好比存儲(storage)大小。

  2. PV和PVC中的storageClassName字段必須一致,這個後面再說。

上面的labels中的標籤只是增長一些描述,對於PVC和PV的綁定沒有關係

應用了上面的PV和PVC,能夠看到自動綁定了。

在POD中如何使用PVC呢

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: nfs-pvc001

這裏經過volumes來聲明使用哪一個PVC,能夠看到和本身定義持久化卷相似,可是這裏更加簡單了,直接使用PVC的名字便可。在容器中使用/data目錄就會把數據寫入到NFS服務器上的目錄中。

當咱們刪除那個PVC的時候,該PV變成Released狀態,因爲咱們的策略是Retain,因此若是想讓這個PV變爲可用咱們就須要手動清理數據並刪除這個PV。這裏你可能會以爲矛盾,你讓這個PV變爲可用,爲何還要刪除這個PV呢?其實所謂可用就是刪除這個PV而後創建一個同名的。

能夠看出來PVC就至關因而容器和PV之間的一個接口,使用人員只須要和PVC打交道便可。另外你可能也會想到若是當前環境中沒有合適的PV和個人PVC綁定,那麼我建立的POD不就失敗了麼?的確是這樣的,不過若是發現這個問題,那麼就趕快建立一個合適的PV,那麼這時候持久化存儲循環控制器會不斷的檢查PVC和PV,當發現有合適的能夠綁定以後它會自動給你綁定上而後被掛起的POD就會自動啓動,而不須要你重建POD。

什麼是持久化存儲

咱們知道所謂容器掛載卷就是將宿主機的目錄掛載到容器中的某個目錄。而持久化則意味着這個目錄裏面的內容不會由於容器被刪除而清除,也不會和當前宿主機有什麼直接關係,而是一個外部的。這樣當POD重建之後或者在其餘主機節點上啓動後依然能夠訪問這些內容。不過以前說過hostPath和emptyDir則推薦使用,由於前者和當前宿主機有必然聯繫然後者就是一個隨POD刪除而被刪除的臨時目錄。

宿主機是如何掛載遠程目錄的

掛載過程會有不一樣,這取決於遠程存儲的類型,它是塊設備存儲仍是文件設備存儲。可是無論怎麼樣POD有這樣一個目錄/var/lib/kubelet/pods/<Pod 的 ID>/volumes/kubernetes.io~<Volume 類型 >/<Volume 名字 >這個目錄是POD被調度到該節點以後,由kubelet爲POD建立的。由於它必定會被建立,由於系統中的默認secret就會被掛載到這裏。以後就要根據存儲設備類型的不一樣作不一樣處理。

文件存儲設備

以nfs這種文件設備存儲來講。咱們依然啓動以前的容器繼續使用以前的PVC。

因爲這個POD運行在node01節點,咱們登錄node01節點,查看這個目錄

/var/lib/kubelet/pods/<Pod 的 ID>/volumes/kubernetes.io~<Volume 類型 >/<Volume 名字 >當你建立POD的時候它因爲它被調度到node01節點,因此會建立這個目錄,並且根據YAML中的定義就也會在這個目錄中建立你在volumesMount中定義的目錄,以下圖:

經過命令查看在本地宿主機的掛載狀況

因爲建立了必要的目錄,那麼kubelet就直接使用mount命令把nfs目錄掛載到這個目錄上volumes/kubernetes.io~<type>/<Volume 名字>,注意這時候僅僅是把這個遠程存儲掛載到宿主機目錄上,要想讓容器使用還須要作調用相關接口來把這個宿主機上的目錄掛載到容器上。因此當準備好以後啓動容器的時候就是利用CRI裏的mounts參數把這個宿主機的目錄掛載到容器中指定的目錄上,就至關於執行docker run -v

不過須要注意的是因爲nfs文件存儲不是一個塊設備,因此宿主機系統須要扮演的就是nfs客戶端角色,kubelet就是調用這個客戶端工具來完成掛載的。

塊存儲設備

塊存儲設備你能夠理解爲一個磁盤。這個的處理要稍微複雜一點,就好像你爲Linux服務器添加一塊磁盤同樣,你得先安裝而後分區格式化以後掛載到某個目錄使用。

/var/lib/kubelet/pods/<Pod 的 ID>/volumes/kubernetes.io~<Volume 類型 >/<Volume 名字 >這個目錄依然會建立。當POD被調度到該節點上會作以下操做

  1. 首先要安裝一個塊設備存儲到宿主機(不是物理安裝,而是經過API來安裝),如何安裝取決於不一樣塊存儲設備的API,不少雲廠商有這種塊存儲設備好比Google的GCE。

  2. 格式化磁盤,

  3. 把格式化好的磁盤設備掛載到宿主機上的目錄

  4. 啓動容器掛載宿主機上的目錄到容器中

相對於文件設備存儲來講塊設備要稍微複雜一點,不過上面這些過程都是自動的有kubelet來完成。

小結

負責把PVC綁定到PV的是一個持久化存儲卷控制循環,這個控制器也是kube-manager-controller的一部分運行在master上。而真正把目錄掛載到容器上的操做是在POD所在主機上發生的,因此經過kubelet來完成。並且建立PV以及PVC的綁定是在POD被調度到某一節點以後進行的,完成這些操做,POD就能夠運行了。下面梳理一下掛載一個PV的過程:

  1. 用戶提交一個包含PVC的POD

  2. 調度器把根據各類調度算法把該POD分配到某個節點,好比node01

  3. Node01上的kubelet等待Volume Manager準備存儲設備

  4. PV控制器調用存儲插件建立PV並與PVC進行綁定

  5. Attach/Detach Controller或Volume Manager經過存儲插件實現設備的attach。(這一步是針對塊設備存儲)

  6. Volume Manager等待存儲設備變爲可用後,掛載該設備到/var/lib/kubelet/pods/<Pod 的 ID>/volumes/kubernetes.io~<Volume 類型 >/<Volume 名字 >目錄上

  7. Kubelet被告知卷已經準備好,開始啓動POD,經過映射方式掛載到容器中

StorageClass

PV是運維人員來建立的,開發操做PVC,但是大規模集羣中可能會有不少PV,若是這些PV都須要運維手動來處理這也是一件很繁瑣的事情,因此就有了動態供給概念,也就是Dynamic Provisioning。而咱們上面的建立的PV都是靜態供給方式,也就是Static Provisioning。而動態供給的關鍵就是StorageClass,它的做用就是建立PV模板。

建立StorageClass裏面須要定義PV屬性好比存儲類型、大小等;另外建立這種PV須要用到存儲插件。最終效果是,用戶提交PVC,裏面指定存儲類型,若是符合咱們定義的StorageClass,則會爲其自動建立PV並進行綁定。

咱們這裏演示一下NFS的動態PV建立

kubernetes自己支持的動態PV建立不包括nfs,因此須要使用額外插件實現。nfs-client

我這裏就按照網站的例子來建立,裏面的內容毫無修改,固然你須要本身準備NFS服務器。因爲用於提供動態建立PV的程序是運行在POD中,因此你須要保證你的Kubernetes節點到NFS的網絡通暢,我這裏就在個人Kubernetes集羣的某個節點上創建的NFS服務。下面是PVC文件

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mytomcat-pvc
spec:
  storageClassName:  managed-nfs-storage
  accessModes:
    - ReadWriteMany
  resources: 
    requests:
      storage: 500Mi

當你應用這個PVC的時候,因爲例子中的storageClassName也是managed-nfs-storage(固然這個名字你能夠修改)就會去自動建立PV。

下圖是在Node02這個節點上看到的

基於這種形式,咱們只須要根據咱們有的存儲系統來定義StorageClass,經過名稱來標識不一樣種類的存儲,好比SSD、block-device這種名稱,而不須要定義具體大小。那麼使用人員就能夠根據須要經過StorageClass的名字來使用,從而實現動態建立PV的過程。

這裏有個要求就是你的存儲系統須要提供某種接口來讓controller能夠調用並傳遞進去PVC的參數去建立PV,不少雲存儲都支持。但是也有不支持的,好比NFS就不支持因此咱們須要一個單獨的插件來完成這個工做。也就是例子中使用的quay.io/external_storage/nfs-client-provisioner鏡像,可是建立PV也須要相關權限,也就是例子中rabc.yaml部分。在定義StorageClass中有一個叫作provisioner: fuseim.pri/ifs這個就是插件的名稱,這個名稱其實也就是官方例子中deployment中設置的名字,這個名字你能夠修改。

固然咱們說過有些自己就支持,好比下面的kubernetes官網中的一個AWS的例子:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  iopsPerGB: "10"
  fsType: ext4

kubernetes.io/aws-ebs就是kubernetes內置的存儲插件名稱,若是你使用aws就用這個名稱就好。由於kubernetes就會去調用AWS的API來建立存儲而後在建立PV。

這裏你可能會有個疑問,爲何開篇的例子裏面也用了storageClassName: normal,但是咱們並無定義任何StorageClass。其實雖然咱們使用了,可是系統上並無一個叫作normal的存儲類,這時候仍是靜態綁定,只是綁定的時候它會考慮你的PV和PVC中的存儲類名稱是否一致。固然若是是靜態綁定你能夠不寫storageClassName,由於若是開起一個的叫作DefaultStorageClassplugin插件就會默認有這樣一個存儲類,它會自動添加到你的任何沒有明確聲明storageClassName的PV和PVC中。

本地持久化存儲

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

爲何須要這種類型的存儲呢?有時候你的應用對磁盤IO有很高的要求,網絡存儲性能確定不如本地的高,尤爲是本地使用了SSD這種磁盤。

但這裏有個問題,一般咱們先建立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走。

kubernetes支持的存儲類型

Kubernetes支持的動態PV建立類型

相關文章
相關標籤/搜索