1. 程式人生 > 實用技巧 >Kubernetes儲存管理詳解

Kubernetes儲存管理詳解

官方文件地址

https://kubernetes.io/docs/concepts/storage/volumes/

一、Kubernetes儲存分類

1、本地儲存

1.1、本地儲存分類

本地儲存有3種

  • hostPath
  • local
  • emptyDir

1.2、為什麼需要本地儲存

  1. 特殊使用場景需求,如需要個臨時儲存空間,執行cAdvisor需要能訪問到node節點/sys/fs/cgroup的資料,做本機單節點的k8s環境功能測試等等。

  2. 容器叢集只是做小規模部署,滿足開發測試、整合測試需求。

  3. 作為分散式儲存服務的一種補充手段,比如我在一臺node主機上插了塊SSD,準備給某個容器吃小灶。

  4. 目前主流的兩個容器叢集儲存解決方案是ceph和glusterfs,二者都是典型的網路分散式儲存,所有的資料讀、寫都是對磁碟IO和網路IO的考驗,所以部署儲存叢集時至少要使用萬兆的光纖網絡卡和光纖交換機。如果你都沒有這些硬貨的話,強上分散式儲存方案的結果就是收穫一個以”慢動作”見長的容器叢集啦。

  5. 分散式儲存叢集服務的規劃、部署和長期的監控、擴容與執行維護是專業性很強的工作,需要有專職的技術人員做長期的技術建設投入。

2、叢集儲存

叢集儲存有3種,就是前面我們介紹過的投射資料卷,不再細說

  • secret
  • configMap
  • downwardAPI

3、遠端儲存

遠端儲存相當多,下面只列出常見的幾種,更多種類詳見官方文件

  • persistentClaim
  • nfs
  • gitRepo
  • flexVolume
  • rbd
  • cephfs

二、本地儲存詳解

1、hostPath

官方定義如下

A hostPath volume mounts a file or directory from the host node’s filesystem into your Pod. This is not something that most Pods will need, but it offers a powerful escape hatch for some applications.

這種方式依賴與 node,如果 pod 被重建建立到了其他的 node,這時如果沒有在新 node 上準備好 hostpath,就會出問題

這種會把宿主機上的指定卷載入到容器之中,當然,如果 Pod 發生跨主機的重建,其內容就難保證了。

這種卷一般和DaemonSet搭配使用,用來操作主機檔案,例如進行日誌採集的 FLK 中的 FluentD 就採用這種方式,載入主機的容器日誌目錄,達到收集本主機所有日誌的目的。

hatch 英[hætʃ] 美[hætʃ]

v. 孵出; 出殼; 孵化; 破殼; 使(小鳥、小魚、小蟲等)孵出;

capacity 英[kəˈpæsəti] 美[kəˈpæsəti]

n. 容量; 容積; 容納能力; 領悟(或理解、辦事)能力; 職位; 職責;

例項

[root@master hostPath]# cat hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
 name: test-pd
spec:
 containers:
  	- image: daocloud.io/library/nginx:1.7.9
 	  name: test-container
	  volumeMounts:
  	  - mountPath: /test-pd
   	    name: test-volume

 volumes:
 - name: test-volume
   hostPath:
	 # directory location on host
     path: /data01
     # this field is optional
     type: Directory

2、local

一個很新的儲存型別,建議在k8s v1.10+以上的版本中使用。該local volume型別目前還只是beta版,這裡不做測試,同樣也不建議在生產環境中使用

hostPath和local對比

  1. 二者都基於node節點本地儲存資源實現了容器內資料的持久化功能,都為某些特殊場景下提供了更為適用的儲存解決方案;

  2. 前者時間很久了,所以功能穩定,而後者因為年輕,所以功能的可靠性與穩定性還需要經歷時間和案例的歷練,尤其是對Block裝置的支援還只是alpha版本;

  3. 二者都為k8s儲存管理提供了PV、PVC和StorageClass的方法實現;

  4. local volume實現的StorageClass不具備完整功能,目前只支援卷的延遲繫結;

  5. hostPath是單節點的本地儲存卷方案,不提供任何基於node節點親和性的pod排程管理支援;

  6. local volume適用於小規模的、多節點的k8s開發或測試環境,尤其是在不具備一套安全、可靠且效能有保證的儲存叢集服務時;

3、emptyDir

EmptyDir是一個空目錄,他的生命週期和所屬的 Pod 是完全一致的

EmptyDir的用處是,可以在同一 Pod 內的不同容器之間共享工作過程中產生的檔案。

預設,EmptyDir 是使用主機磁碟進行儲存的,也可以設定emptyDir.medium 欄位的值為Memory,來提高執行速度,但是這種設定,對該卷的佔用會消耗容器的記憶體份額。

例項

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

三、認識 pv 和 pvc

1、pv和pvc概述

PersistentVolume(持久卷)和PersistentVolumeClaim(持久卷申請)是k8s提供的兩種API資源,用於抽象儲存細節。也提供了基礎設施和應用之間的分界,管理員關注於如何通過pv提供儲存功能而無需關注使用者如何使用,同樣的使用者只需要掛載pvc到容器中而不需要關注儲存卷採用何種技術實現。換種說法就是管理員建立一系列的 PV 提供儲存,然後為應用提供 PVC,應用程式僅需要載入一個 PVC,就可以進行訪問

1.5版本之後又提供了 PV 的動態供應。可以不經 PV 步驟直接建立 PVC

pvc和pv的關係與pod和node關係類似,前者消耗後者的資源。pvc可以向pv申請指定大小的儲存資源並設定訪問模式,這就可以通過Provision -> Claim 的方式,來對儲存資源進行控制。

2、基本概念

1、pv

管理儲存和管理計算有著明顯的不同。PersistentVolume給使用者和管理員提供了一套API,抽象出儲存是如何提供和消耗的細節。

PersistentVolume(持久卷,簡稱PV)是叢集內,由管理員提供的網路儲存的一部分。就像叢集中的節點一樣,PV也是叢集中的一種資源。它的生命週期卻是和使用它的Pod相互獨立的。PV這個API物件,捕獲了諸如NFS、ISCSI、或其他雲端儲存系統的實現細節。

2、pvc

PersistentVolumeClaim(持久卷宣告,簡稱PVC)是使用者的一種儲存請求。它和Pod類似,Pod消耗Node資源,而PVC消耗PV資源。Pod能夠請求特定的資源(如CPU和記憶體)。PVC能夠請求指定的大小和訪問的模式(可以被對映為一次讀寫或者多次只讀)。

PVC允許使用者消耗抽象的儲存資源,使用者也經常需要各種屬性(如效能)的PV。叢集管理員需要提供各種各樣、不同大小、不同訪問模式的PV,而不用向用戶暴露這些volume如何實現的細節。因為這種需求,就催生出一種StorageClass資源。

3、StorageClass

StorageClass提供了一種方式,使得管理員能夠描述他提供的儲存的等級。叢集管理員可以將不同的等級對映到不同的服務等級、不同的後端策略。 後面會詳細解釋

4、volume和claim的生命週期

PV是叢集中的資源,PVC是對這些資源的請求,同時也是這些資源的“提取證”。PV和PVC的互動遵循以下生命週期:

  1. 供給

​ 有兩種PV提供的方式:靜態和動態。

靜態

​ 叢集管理員建立多個PV,它們攜帶著真實儲存的詳細資訊,這些儲存對於叢集使用者是可用的。它們存在於Kubernetes API中,並可用於儲存使用。

**動態**

​ 當管理員建立的靜態PV都不匹配使用者的PVC時,叢集可能會嘗試專門地供給volume給PVC。這種供給基於StorageClass:PVC必須請求這樣一個等級,而管理員必須已經建立和配置過這樣一個等級,以備發生這種動態供給的情況。請求等級配置為“”的PVC,有效地禁用了它自身的動態供給功能。

  1. 繫結

使用者建立一個PVC(或者之前就已經就為動態供給建立了),指定要求儲存的大小和訪問模式。master中有一個控制迴路用於監控新的PVC,查詢匹配的PV(如果有),並把PVC和PV繫結在一起。如果一個PV曾經動態供給到了一個新的PVC,那麼這個迴路會一直繫結這個PV和PVC。另外,使用者總是至少能得到它們所要求的儲存,但是volume可能超過它們的請求。一旦綁定了,PVC繫結就是專屬的,無論它們的繫結模式是什麼。

如果沒找到匹配的PV,那麼PVC會無限期得處於unbound未繫結狀態,一旦PV可用了,PVC就會又變成繫結狀態。比如,如果一個供給了很多50G的PV叢集,不會匹配要求100G的PVC。直到100G的PV新增到該叢集時,PVC才會被繫結。

  1. 使用

Pod使用PVC就像使用volume一樣。叢集檢查PVC,查詢繫結的PV,並對映PV給Pod。對於支援多種訪問模式的PV,使用者可以指定想用的模式。一旦使用者擁有了一個PVC,並且PVC被繫結,那麼只要使用者還需要,PV就一直屬於這個使用者。使用者排程Pod,通過在Pod的volume塊中包含PVC來訪問PV。

  1. 釋放

當用戶使用PV完畢後,他們可以通過API來刪除PVC物件。當PVC被刪除後,對應的PV就被認為是已經是“released”了,但還不能再給另外一個PVC使用。前一個PVC的屬於還存在於該PV中,必須根據策略來處理掉。

  1. 回收

PV的回收策略告訴叢集,在PV被釋放之後叢集應該如何處理該PV。當前,PV可以被Retained(保留)、 Recycled(再利用)或者Deleted(刪除)。保留允許手動地再次宣告資源。對於支援刪除操作的PV卷,刪除操作會從Kubernetes中移除PV物件,還有對應的外部儲存(如AWS EBS,GCE PD,Azure Disk,或者Cinder volume)。動態供給的卷總是會被刪除。

  1. Recycled(再利用)

如果PV卷支援再利用,再利用會在PV捲上執行一個基礎的擦除操作(rm -rf /thevolume/*),使得它可以再次被其他PVC宣告利用。

管理員可以通過Kubernetes controller manager的命令列工具,來配置自定義的再利用Pod模板。自定義的再利用Pod模板必須包含PV卷的詳細內容,如下示例:

apiVersion: v1
kind: Pod
metadata:
  name: pv-recycler-
  namespace: default
spec:
  restartPolicy: Never
  volumes:
  - name: vol
    hostPath:
      path: /any/path/it/will/be/replaced
  containers:
  - name: pv-recycler
    image: "gcr.io/google_containers/busybox"
    command: ["/bin/sh", "-c", "test -e /scrub && rm -rf /scrub/..?* /scrub/.[!.]* /scrub/*  && test -z \"$(ls -A /scrub)\" || exit 1"]
    volumeMounts:
    - name: vol
      mountPath: /scrub

如上,在volumes部分的指定路徑,應該被替換為PV卷需要再利用的路徑。

5、pv型別

pv型別使用外掛的形式來實現。Kubernetes現在支援以下外掛:

  • GCEPersistentDisk
  • AWSElasticBlockStore
  • AzureFile
  • AzureDisk
  • FC (Fibre Channel)
  • Flocker
  • NFS
  • iSCSI
  • RBD (Ceph Block Device)
  • CephFS
  • Cinder (OpenStack block storage)
  • Glusterfs
  • VsphereVolume
  • Quobyte Volumes
  • HostPath
  • VMware Photon
  • Portworx Volumes
  • ScaleIO Volumes

四、pv 配置詳解

以如下Yaml檔案為例

apiVersion: v1
kind: PersistentVolume
metadata:
    name: pv0003
spec:
    capacity:
      storage: 5Gi
    accessModes:
      - ReadWriteOnce
    persistentVolumeReclaimPolicy: Recycle
    storageClassName: slow
    nfs:
      path: /tmp
      server: 172.17.0.2

1、Capacity(容量)

一般來說,PV會指定儲存的容量,使用PV的capacity屬性來設定。當前,儲存大小是唯一能被設定或請求的資源。未來可能包含IOPS,吞吐率等屬性。

2、訪問模式

PV可以使用儲存資源提供者支援的任何方法來對映到host中。如下所示,提供者有著不同的功能,每個PV的訪問模式被設定為卷支援的指定模式。比如,NFS可以支援多個讀/寫的客戶端,但可以在伺服器上指定一個只讀的NFS PV。每個PV有它自己的訪問模式。

訪問模式包括:

  • ReadWriteOnce — 該volume只能被單個節點以讀寫的方式對映
  • ReadOnlyMany — 該volume可以被多個節點以只讀方式對映
  • ReadWriteMany — 該volume只能被多個節點以讀寫的方式對映

在CLI中,訪問模式可以簡寫為:

  • RWO – ReadWriteOnce
  • ROX – ReadOnlyMany
  • RWX – ReadWriteMany

注意:即使volume支援很多種訪問模式,但它同時只能使用一種方式來對映。比如,GCEPersistentDisk可以被單個節點對映為ReadWriteOnce,或者多個節點對映為ReadOnlyMany,但不能同時使用這兩種方式來對映。

3、Class

一個PV可以有一種class,通過設定storageClassName屬性來選擇指定的StorageClass。有指定class的PV只能繫結給請求該class的PVC。沒有設定storageClassName屬性的PV只能繫結給未請求class的PVC。

過去,使用volume.beta.kubernetes.io/storage-class註解,而不是storageClassName屬性。該註解現在依然可以工作,但在Kubernetes的未來版本中已經被完全棄用了。

4、回收策略

警告:Recycle回收政策已棄用。官方推薦的方法是使用動態配置。

回收策略有:

  • Retain: 手動回收

  • Recycle: 需要擦出後才能再使用

  • Delete: 相關聯的儲存資產,如AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷都會被刪除

當前,只有NFS和HostPath支援回收利用,AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷支援刪除操作。

5、階段

一個volume卷處於以下幾個階段之一:

  • Available:空閒的資源,未繫結給PVC

  • Bound: 繫結給了某個PVC

  • Released: PVC已經刪除了,但是PV還沒有被叢集回收

  • Failed: PV在自動回收中失敗了

CLI可以顯示PV繫結的PVC名稱。

6、對映選項

當PV被對映到一個node上時,Kubernetes管理員可以指定額外的對映選項。可以通過使用標註volume.beta.kubernetes.io/mount-options來指定PV的對映選項。

比如:

apiVersion: "v1"
kind: "PersistentVolume"
metadata:
  name: gce-disk-1
  annotations:
    volume.beta.kubernetes.io/mount-options: "discard"
spec:
  capacity:
    storage: "10Gi"
  accessModes:
    - "ReadWriteOnce"
  gcePersistentDisk:
    fsType: "ext4"
    pdName: "gce-disk-1

對映選項是當對映PV到磁碟時,一個可以被遞增地新增和使用的字串。

注意,並非所有的PV型別都支援對映選項。在Kubernetes v1.6中,以下的PV型別支援對映選項。

  • GCEPersistentDisk

  • AWSElasticBlockStore

  • AzureFile

  • AzureDisk

  • NFS

  • iSCSI

  • RBD (Ceph Block Device)

  • CephFS

  • Cinder (OpenStack block storage)

  • Glusterfs

  • VsphereVolume

  • Quobyte Volumes

  • VMware Photon

  • PersistentVolumeClaims(PVC)

五、pvc 配置詳解

以下面pvc的yaml檔案為例

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}

1、訪問模式

當請求指定訪問模式的儲存時,PVC使用的規則和PV相同。

2、資源

PVC就像pod一樣,可以請求指定數量的資源。請求資源時,PV和PVC都使用相同的資源樣式。

3、選擇器(Selector)

PVC可以指定標籤選擇器進行更深度的過濾PV,只有匹配了選擇器標籤的PV才能繫結給PVC。選擇器包含兩個欄位:

  • matchLabels(匹配標籤) – PV必須有一個包含該值的標籤

  • matchExpressions(匹配表示式) – 一個請求列表,包含指定的鍵、值的列表、關聯鍵和值的操作符。合法的操作符包含In,NotIn,Exists和DoesNotExist。

所有來自matchLabels和matchExpressions的請求,都是邏輯與關係的,它們必須全部滿足才能匹配上。

4、Class

PVC可以使用屬性storageClassName來指定StorageClass的名稱,從而請求指定的等級。只有滿足請求等級的PV,即那些包含了和PVC相同storageClassName的PV,才能與PVC繫結。

PVC並非必須要請求一個等級。設定storageClassName為“ ”的PVC被理解為請求一個無等級的PV,因此它只能被繫結到無等級的PV(未設定對應的標註,或者設定為“”)。未設定storageClassName的PVC不太相同,DefaultStorageClass的許可權外掛開啟與否,叢集也會區別處理PVC。

如果許可權外掛被開啟,管理員可能會指定一個預設的StorageClass。所有沒有指定StorageClassName的PVC只能被繫結到預設等級的PV。要指定預設的StorageClass,需要在StorageClass物件中將標註storageclass.kubernetes.io/is-default-class設定為“true”。如果管理員沒有指定這個預設值,叢集對PVC建立請求的迴應就和許可權外掛被關閉時一樣。如果指定了多個預設等級,那麼許可權外掛禁止PVC建立請求。

如果許可權外掛被關閉,那麼就沒有預設StorageClass的概念。所有沒有設定StorageClassName的PVC都只能繫結到沒有等級的PV。因此,沒有設定StorageClassName的PVC就如同設定StorageClassName為“”的PVC一樣被對待。

根據安裝方法的不同,預設的StorageClass可能會在安裝過程中被外掛管理預設的部署在Kubernetes叢集中。

當PVC指定selector來請求StorageClass時,所有請求都是與操作的。只有滿足了指定等級和標籤的PV才可能繫結給PVC。當前,一個非空selector的PVC不能使用PV動態供給。

過去,使用volume.beta.kubernetes.io/storage-class註解,而不是storageClassName屬性。該註解現在依然可以工作,但在Kubernetes的未來版本中已經被完全棄用了。

StorageClass定義示例

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2

StorageClass物件的命名

StorageClass物件的命名是非常重要的,它是使用者請求指定等級的方式。當建立StorageClass物件時,管理員設定等級的名稱和其他引數,但物件不會在建立後馬上就被更新。

每個StorageClass都包含欄位provisioner和parameters,在所屬的PV需要動態供給時使用這些欄位。

Provisioner

StorageClass都有儲存供應商provisioner,用來決定哪種volume外掛提供給PV使用。必須制定該欄位。

除了可以指定此處列出的“內部”供應商(其名稱字首為“kubernetes.io”並與Kubernetes一起分發)。還可以執行和指定外部供應商,它們是遵循Kubernetes定義的規範的獨立程式。外部提供者的作者對程式碼的生命週期,供應商的分發方式,執行狀況以及使用的卷外掛(包括Flex)等都有充分的自主權。庫kubernetes-incubator/external-storage存放了一個庫,用於編寫外部儲存供應商,而這些提供者實現了大量的規範,並且是各種社群維護的。

引數

StorageClass有一些引數用於描述歸屬於該StorageClass的volume。不同的儲存提供商需要不同的引數。比如,引數type對應的值io1,還有引數iopsPerGB,都是EBS專用的引數。當引數省略時,就會使用它的預設值。

比如:Ceph RBD

  apiVersion: storage.k8s.io/v1
  kind: StorageClass
  metadata:
    name: fast
  provisioner: kubernetes.io/rbd
  parameters:
    monitors: 10.16.153.105:6789
    adminId: kube
    adminSecretName: ceph-secret
    adminSecretNamespace: kube-system
    pool: kube
    userId: kube
    userSecretName: ceph-secret-user

monitors:Ceph的monitor,逗號分隔。該引數是必須的。

adminId: Ceph的客戶端ID,可在pool中建立映象。預設的是“admin”。

adminSecretNamespace:adminSecret的名稱空間,預設值是“default”。

adminSecretName:adminId的Secret Name。該引數是必須的,提供的祕鑰必須有型別“kubernetes.io/rbd”。

pool:Ceph的RBD pool,預設值是“rbd”。

userId:Ceph的客戶ID,用於對映RBD映象的,預設值和adminId引數相同。

userSecretName:Ceph Secret的名稱,userId用該引數來對映RBD映象。它必須和PVC在相同的名稱空間。該引數也是必須的。提供的祕鑰必須有型別“kubernetes.io/rbd”。比如,按照下面的方式來建立:

$ kubectl create secret generic ceph-secret --type="kubernetes.io/rbd" --from-literal=key='QVFEQ1pMdFhPUnQrSmhBQUFYaERWNHJsZ3BsMmNjcDR6RFZST0E9PQ==' --namespace=kube-system

配置注意事項

如果你在寫配置模板和示例,用於在需要持久化儲存的叢集中使用,那麼,建議使用以下的一些模式:

在你的捆綁配置(如Deployment、ConfigMap)中包含PVC物件。

在配置中不要包含PersistentVolume物件,因為例項化配置的使用者可能沒有建立PersistentVolumes的許可權

當用戶提供例項化模板時,給使用者提供儲存類名稱的選項。

如果使用者提供了一個StorageClass名稱,並且Kubernetes版本是1.4及以上,那麼將該值設定在PVC的volume.beta.kubernetes.io/storage-class標註上。這會使得PVC匹配到正確的StorageClass。

如果使用者沒有提供StorageClass名稱,或者叢集版本是1.3,那麼就需要在PVC配置中設定volume.alpha.kubernetes.io/storage-class: default標註。

— 這會使得在一些預設配置健全的叢集中,PV可以動態的提供給使用者。

— 儘管在名稱中包含了alpha單詞,但是該標註對應的程式碼有著beta級別的支援。

— 不要使用volume.beta.kubernetes.io/storage-class,無論設定什麼值,甚至是空字串。因為它會阻止DefaultStorageClass許可控制器。

在你的工具中,要監視那些一段時間後還沒有獲得繫結的PVC,並且展示給使用者。因為這可能表明叢集沒有支援動態儲存(此時我們應該建立匹配的PV),或者叢集沒有儲存系統(此時使用者不能部署需要PVC的情況)。

5、使用PVC

Pod通過使用PVC(使用方式和volume一樣)來訪問儲存。PVC必須和使用它的pod在同一個名稱空間,叢集發現pod名稱空間的PVC,根據PVC得到其後端的PV,然後PV被對映到host中,再提供給pod。

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

6、名稱空間注意事項

PV繫結是獨有的,因為PVC是名稱空間物件,對映PVC時只能在同一個名稱空間中使用多種模式(ROX,RWX)。

六、儲存配置及使用例項

NFS

不支援動態建立持久卷,只能手工建立

先手工建立PV,再通過PV手工建立PVC,PVC就是真正可用的持久卷

PVC和PV繫結規則

​ PVC會根據自己需求空間的大小自動選擇合適的PV,比如需要一個5G的PVC,PV分別為2G,7G和10G,那麼PVC會自動選擇7G的,但是剩餘的空間不是浪費了麼?原因如下:

​ 一個被繫結的PV只能用於一個PVC,他們是一對一繫結的,如果PVC大小隻需要5G,但是所選的PV有7G,那麼剩餘的2G是沒辦法使用的,如果不想這樣浪費空間只能使用動態建立的方式

ceph、glusterfs、雲硬碟

支援動態建立持久卷

動態建立持久卷的時候也是需要手動建立PV,但是PVC是不用手工建立的,PVC會自動從PV上拿取空間

1、過程摘要

  1. 叢集管理員建立由物理儲存支援的PersistentVolume。管理員不會將卷與任何Pod關聯。

  2. 叢集使用者建立PersistentVolumeClaim,它會自動繫結到合適的PersistentVolume。

  3. 使用者建立一個使用PersistentVolumeClaim作為儲存的Pod。

2、具體步驟

  1. 在你的節點上建立index.html檔案

  2. 建立PersistentVolume

  3. 建立PersistentVolumeClaim

  4. 建立一個Pod

3、實現過程

  1. 在你的節點上建立index.html檔案
# mkdir /test/pv/data
# echo 'Hello from Kubernetes storage' > /test/pv/data/index.html
  1. 建立PersistentVolume

本練習將建立hostPath PersistentVolume。Kubernetes支援hostPath在單節點叢集上進行開發和測試。hostPath PersistentVolume使用節點上的檔案或目錄來模擬網路附加儲存。

在生產群集中,不會使用hostPath。相反,叢集管理員可以配置網路資源,如Google Compute Engine永久磁碟,NFS共享或Amazon Elastic Block Store卷。群集管理員還可以使用StorageClasses 來設定動態配置。

# vim pv-volume.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/test/pv/data"

PersistentVolume 的StorageClass名稱 manual,該名稱將用於將PersistentVolumeClaim請求繫結到此PersistentVolume。

# kubectl  apply -f pv-volume.yaml
  1. 建立PersistentVolumeClaim
# vim pv-claim.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 200Mi

建立PersistentVolumeClaim之後,Kubernetes控制平面將尋找滿足索賠要求的PersistentVolume。如果控制平面找到具有相同StorageClass的合適持久卷,則將宣告繫結到該卷。

再次檢視PersistentVolume:

kubectl get pv task-pv-volume

輸出顯示狀態為Bound

NAME             CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM                   STORAGECLASS   REASON    AGE
task-pv-volume   10Gi       RWO           Retain          Bound     default/task-pv-claim   manual                   2m

檢視PersistentVolumeClaim:

kubectl get pvc task-pv-claim

輸出顯示PersistentVolumeClaim被繫結到PersistentVolume task-pv-volume

NAME            STATUS    VOLUME           CAPACITY   ACCESSMODES   STORAGECLASS   AGE
task-pv-claim   Bound     task-pv-volume   10Gi       RWO           manual         30s
  1. 建立pod

下一步是建立一個Pod,它使用PersistentVolumeClaim作為卷。

以下是Pod的配置檔案:

#vim pv-pod.yaml
kind: Pod
apiVersion: v1
metadata:
  name: task-pv-pod
spec:
  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

注意,Pod的配置檔案指定了一個PersistentVolumeClaim,但是它沒有指定一個PersistentVolume。從Pod的角度來看,這個宣告是一個卷。

建立pod:

# kubectl apply -f pv-pod.yaml

確認pod中的容器是否正在執行:

# kubectl get pod task-pv-pod

驗證nginx是否提供了hostPath卷中的index.html檔案:

此pod執行在了node1上,所以可以直接在node1上訪問此pod的ip地址
[root@node1 /]# curl  10.244.1.77
Hello from Kubernetes storage
  1. 訪問控制

使用組ID (GID)配置的儲存只允許使用相同GID的pod進行寫入。不匹配或丟失GID會導致許可權拒絕錯誤。為了減少與使用者協調的需要,管理員可以使用GID向PersistentVolume添加註解。然後,GID會自動新增到使用PersistentVolume的所有Pod中。

按如下使用pv.beta.kubernetes.io/gid 註解:

kind: PersistentVolume
apiVersion: v1
metadata:
  name: pv1
  annotations:
    pv.beta.kubernetes.io/gid: "1234"

當Pod使用具有GID註解的持久卷時,帶註解的GID將以與Pod的安全上下文中指定的GID相同的方式應用於Pod中的所有容器。每個GID,無論是源自PersistentVolume註解還是Pod的規約,都應用於每個容器中執行的第一個程序。

七、NFS持久卷應用例項

注意:實際測試,pv容量未受限制

1、NFS安裝

我這裡是用NODE1提供NFS服務,生產環境要獨立

# yum -y install nfs-utils rpcbind

這裡是做多個NFS目錄用於掛載,因為一個PVC取消一個PV的繫結之後,原來的PV還是不能被其他PVC使用的

# mkdir /data/{nfs1,nfs2,nfs3,nfs4,nfs5,nfs6,nfs7,nfs8,nfs9,nfs10} -pv && chmod 777 /data/nfs*

# vim /etc/exports   //wing拿node2(192.168.1.208)做的nfs服務,這裡就寫了一條帶IP的,其他偷懶用的*
/data/nfs1 192.168.1.208/24(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash) 
/data/nfs2 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash) 
/data/nfs3 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash) 
/data/nfs4 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash) 
/data/nfs5 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)
/data/nfs6 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)
/data/nfs7 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)
/data/nfs8 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)
/data/nfs9 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)
/data/nfs10 *(rw,async,insecure,anonuid=1000,anongid=1000,no_root_squash)

• rw:read-write,可讀寫

• async:檔案暫存於記憶體,而不是直接寫入硬碟;

• anonuid:匿名使用者的UID值

• anongid:匿名使用者的GID值。注:其中anonuid=1000,anongid=1000,為此目錄使用者web的ID號,達到連線

NFS使用者許可權一致。

• insecure: 需要設定,否則掛載無許可權

• no_root_squash:NFS客戶端連線服務端時如果使用的是root的話,那麼對服務端分享的目錄來說,也

擁有root許可權。顯然開啟這項是不安全的。

# exportfs -rv
# systemctl enable rpcbind nfs-server
# systemctl start nfs-server rpcbind
# rpcinfo -p

2、持久卷的運用

可以使用下面的explain子命令去檢視學習pv的使用方法,這裡不是要大家操作的,這些命令直接回車會看到幫助

# kubectl explain PersistentVolume
# kubectl explain PersistentVolume.spec
# kubectl explain PersistentVolume.spec.accessModes

使用YAML檔案建立PV和PVC(如果你用的wing的yaml檔案,要注意修改裡面的內容)

# cat volume/nfs-pv.yml   //內容在子目錄
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
  labels:
    name: pv001
spec:
  nfs:
    path: /data/nfs1
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002
  labels:
    name: pv002
spec:
  nfs:
    path: /data/nfs2
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003
  labels:
    name: pv003
spec:
  nfs:
    path: /data/nfs3
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv004
  labels:
    name: pv004
spec:
  nfs:
    path: /data/nfs4
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv005
  labels:
    name: pv005
spec:
  nfs:
    path: /data/nfs5
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv006
  labels:
    name: pv006
spec:
  nfs:
    path: /data/nfs6
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv007
  labels:
    name: pv007
spec:
  nfs:
    path: /data/nfs7
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv008
  labels:
    name: pv008
spec:
  nfs:
    path: /data/nfs8
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv009
  labels:
    name: pv009
spec:
  nfs:
    path: /data/nfs9
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv010
  labels:
    name: pv010
spec:
  nfs:
    path: /data/nfs10
    server: 192.168.1.208
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
    
[root@master volume]# kubectl apply -f nfs-pv.yml --record
persistentvolume/pv001 created
persistentvolume/pv002 created
persistentvolume/pv003 created
persistentvolume/pv004 created
persistentvolume/pv005 created
persistentvolume/pv006 created
persistentvolume/pv007 created
persistentvolume/pv008 created
persistentvolume/pv009 created
persistentvolume/pv010 created

[root@master volume]# kubectl  get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv001   1Gi        RWO,RWX        Retain           Available                                   16s
pv002   1Gi        RWO,RWX        Retain           Available                                   16s
pv003   1Gi        RWO,RWX        Retain           Available                                   16s
pv004   1Gi        RWO,RWX        Retain           Available                                   16s
pv005   1Gi        RWO,RWX        Retain           Available                                   16s
pv006   1Gi        RWO,RWX        Retain           Available                                   16s
pv007   2Gi        RWO,RWX        Retain           Available                                   16s
pv008   1Gi        RWO,RWX        Retain           Available                                   16s
pv009   1Gi        RWO,RWX        Retain           Available                                   16s
pv010   1Gi        RWO,RWX        Retain           Available                                   16s


# cat volume/nfs-pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
  namespace: default
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 2Gi

[root@master volume]# kubectl apply -f nfs-pvc.yml --record
persistentvolumeclaim/mypvc created
[root@master volume]# kubectl  get pvc
NAME    STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc   Bound    pv007    2Gi        RWO,RWX                       5s

3、用nginx應用測試PVC

# cat volume/nginx-pvc.yml // 執行pod的節點必須安裝 nfs-utils
apiVersion: v1
kind: Pod
metadata:
  name: nginx-vol-pvc
  namespace: default
spec:
  containers:
  - name: mywww
    image: daocloud.io/library/nginx
    volumeMounts:
    - name: www
      mountPath: /usr/share/nginx/html
  volumes:
  - name: www
    persistentVolumeClaim:
      claimName: mypvc

# kubectl apply -f volume/nginx-pvc.yml
pod/nginx-vol-pvc created

# kubectl exec -it nginx-vol-pvc -- sh
# echo "hello,world! I am wing" > /usr/share/nginx/html/index.html 
# exit

訪問測試:
# curl http://PodIP:80
[root@master volume]# kubectl  get pod -o wide
NAME                        READY   STATUS      RESTARTS   AGE     IP              NODE    NOMINATED NODE   READINESS GATES
nginx-vol-pvc               1/1     Running     0          91s     10.244.1.217    node1   <none>           <none>

# curl 10.244.1.217
hello,world! I am wing

也可以直接去修改NFS:
[root@node2 data]# ls
nfs1  nfs10  nfs2  nfs3  nfs4  nfs5  nfs6  nfs7  nfs8  nfs9
[root@node2 data]# cd nfs7
[root@node2 nfs7]# ls
index.html
[root@node2 nfs7]# echo also can write con to here > index.html 
[root@node2 nfs7]# curl 10.244.1.217
also can write con to here

4、刪除PVC和PV

雖然可以刪掉pvc把狀態變成released(釋放狀態),但是記錄狀態裡面還是有的,其他的PVC還是不能呼叫刪掉的PV,這是NFS的缺點,沒辦法解決的

# kubectl delete -f nginx-pvc.yml 
# kubectl delete -f nfs-pv.yml