1. 程式人生 > >emptyDir、hostPath以及local volume都是Kubernetes的本地儲存卷,那麼有何不同?

emptyDir、hostPath以及local volume都是Kubernetes的本地儲存卷,那麼有何不同?

Kubernetes支援幾十種類型的後端儲存卷,其中有幾種儲存卷總是給人一種分不清楚它們之間有什麼區別的感覺,尤其是local與hostPath這兩種儲存卷型別,看上去都像是node本地儲存方案嘛。當然,還另有一種volume型別是emptyDir,也有相近之處。

在Docker容器時代,我們就對Volume很熟悉了,一般來說我們是通過建立Volume資料卷,然後掛載到指定容器的指定路徑下,以實現容器資料的持久化儲存或者是多容器間的資料共享,當然這裡說的都是單機版的容器解決方案。

進入到容器叢集時代後,我們看到Kubernetes按時間順序先後提供了emptyDir、hostPath和local的本地磁碟儲存卷解決方案。emptyDir、hostPath都是Kubernetes很早就實現和支援了的技術,local volume方式則是從k8s v1.7才剛剛釋出的alpha版本,目前在k8s v1.10中釋出了local volume的beta版本,部分功能在早期版本中並不支援。

在展開之前,我們先討論一個問題,就是既然都已經實現容器雲平臺了,我們為什麼還要關注這幾款本地儲存卷的貨呢?粗略歸納了下,有以下幾個原因:

  • 特殊使用場景需求,如需要個臨時儲存空間,執行cAdvisor需要能訪問到node節點/sys/fs/cgroup的資料,做本機單節點的k8s環境功能測試等等。
  • 容器叢集只是做小規模部署,滿足開發測試、整合測試需求。
  • 作為分散式儲存服務的一種補充手段,比如我在一臺node主機上插了塊SSD,準備給某個容器吃小灶。
  • 目前主流的兩個容器叢集儲存解決方案是ceph和glusterfs,二者都是典型的網路分散式儲存,所有的資料讀、寫都是對磁碟IO和網路IO的考驗,所以部署儲存叢集時至少要使用萬兆的光纖網絡卡和光纖交換機。如果你都沒有這些硬貨的話,強上分散式儲存方案的結果就是收穫一個以"慢動作"見長的容器叢集啦。
  • 分散式儲存叢集服務的規劃、部署和長期的監控、擴容與執行維護是專業性很強的工作,需要有專職的技術人員做長期的技術建設投入。

我們並不是說分散式儲存服務不好,很多公司在雲平臺建設的實踐中,往往是需要結合使用幾種通用的與專用的儲存解決方案,才能最終滿足大部分的使用需求。
所以,如果這裡有一款場景適合你的話,不妨瞭解一下這幾款本地儲存卷的功能特點、使用技巧與異同。

emptyDir

emptyDir型別的Volume在Pod分配到Node上時被建立,Kubernetes會在Node上自動分配一個目錄,因此無需指定宿主機Node上對應的目錄檔案。 這個目錄的初始內容為空,當Pod從Node上移除時,emptyDir中的資料會被永久刪除。
注:容器的crashing事件並不會導致emptyDir中的資料被刪除。

最佳實踐

根據官方給出的最佳使用實踐的建議,emptyDir可以在以下幾種場景下使用:

  • 臨時空間,例如基於磁碟的合併排序
  • 設定檢查點以從崩潰事件中恢復未執行完畢的長計算
  • 儲存內容管理器容器從Web伺服器容器提供資料時所獲取的檔案

預設情況下,emptyDir可以使用任何型別的由node節點提供的後端儲存。如果你有特殊的場景,需要使用tmpfs作為emptyDir的可用儲存資源也是可以的,只需要在建立emptyDir卷時增加一個emptyDir.medium欄位的定義,並賦值為"Memory"即可。
注:在使用tmpfs檔案系統作為emptyDir的儲存後端時,如果遇到node節點重啟,則emptyDir中的資料也會全部丟失。同時,你編寫的任何檔案也都將計入Container的記憶體使用限制。

emptyDir volume 實驗

我們在測試k8s環境中建立一個emptyDir volume的使用示例。

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - image: busybox
    name: test-emptydir
    command: [ "sleep", "3600" ]
    volumeMounts:
    - mountPath: /data
      name: data-volume
  volumes:
  - name: data-volume
    emptyDir: {}

檢視下創建出來的pod,這裡只截取了與volume有關的部分,其他無關內容直接省略:

# kubectl describe pod test-pod
Name:         test-pod
Namespace:    default
Node:         kube-node2/172.16.10.102
......
    Environment:    <none>
    Mounts:
      /data from data-volume (rw)
......
Volumes:
  data-volume:
    Type:    EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
......

可以進入到容器中檢視下實際的卷掛載結果:

# kubectl exec -it test-pod -c test-emptydir /bin/sh

hostPath

hostPath型別則是對映node檔案系統中的檔案或者目錄到pod裡。在使用hostPath型別的儲存卷時,也可以設定type欄位,支援的型別有檔案、目錄、File、Socket、CharDevice和BlockDevice。

來自官網對hostPath的使用場景和注意事項的介紹

使用場景:

  • 當執行的容器需要訪問Docker內部結構時,如使用hostPath對映/var/lib/docker到容器;
  • 當在容器中執行cAdvisor時,可以使用hostPath對映/dev/cgroups到容器中;

注意事項:

  • 配置相同的pod(如通過podTemplate建立),可能在不同的Node上表現不同,因為不同節點上對映的檔案內容不同
  • 當Kubernetes增加了資源敏感的排程程式,hostPath使用的資源不會被計算在內
  • 宿主機下建立的目錄只有root有寫許可權。你需要讓你的程式執行在privileged container上,或者修改宿主機上的檔案許可權。

hostPath volume 實驗

下面我們在測試k8s環境中建立一個hostPath volume使用示例。

apiVersion: v1
kind: Pod
metadata:
  name: test-pod2
spec:
  containers:
  - image: busybox
    name: test-hostpath
    command: [ "sleep", "3600" ]
    volumeMounts:
    - mountPath: /test-data
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory

檢視下pod建立結果,觀察volumes部分:

# kubectl describe pod test-pod2
Name:         test-pod2
Namespace:    default
Node:         kube-node2/172.16.10.102
......
    Mounts:
      /test-data from test-volume (rw)
......
Volumes:
  test-volume:
    Type:          HostPath (bare host directory volume)
    Path:          /data
    HostPathType:  Directory
......

我們登入到容器中,進入掛載的/test-data目錄中,建立個測試檔案。

# kubectl exec  -it test-pod2 -c test-hostpath /bin/sh
/ # echo 'testtesttest' > /test-data/test.log
/ # exit

我們在執行該pod的node節點上,可以看到如下的檔案和內容。

[[email protected] test-data]# cat /test-data/test.log
testtesttest

現在,我們把該pod刪除掉,再看看node節點上的hostPath使用的目錄與資料會有什麼變化。

[[email protected] ~]# kubectl delete pod test-pod2
pod "test-pod2" deleted

到執行原pod的node節點上檢視如下。

[[email protected] test-data]# ls -l
total 4
-rw-r--r-- 1 root root 13 Nov 14 00:25 test.log
[[email protected] test-data]# cat /test-data/test.log
testtesttest
  • 在使用hostPath volume卷時,即便pod已經被刪除了,volume卷中的資料還在!

單節點的k8s本地測試環境與hostPath volume

有時我們需要搭建一個單節點的k8s測試環境,就利用到hostPath作為後端的儲存卷,模擬真實環境提供PV、StorageClass和PVC的管理功能支援。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  namespace: kube-system
  name: standard
  annotations:
    storageclass.beta.kubernetes.io/is-default-class: "true"
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
provisioner: kubernetes.io/host-path
  • 該場景僅能用於單節點的k8s測試環境中

emptyDir和hostPath在功能上的異同分析

  • 二者都是node節點的本地儲存卷方式;
  • emptyDir可以選擇把資料存到tmpfs型別的本地檔案系統中去,hostPath並不支援這一點;
  • hostPath除了支援掛載目錄外,還支援File、Socket、CharDevice和BlockDevice,既支援把已有的檔案和目錄掛載到容器中,也提供了“如果檔案或目錄不存在,就建立一個”的功能;
  • emptyDir是臨時儲存空間,完全不提供持久化支援;
  • hostPath的卷資料是持久化在node節點的檔案系統中的,即便pod已經被刪除了,volume卷中的資料還會留存在node節點上;

local volume的概念

這是一個很新的儲存型別,建議在k8s v1.10+以上的版本中使用。該local volume型別目前還只是beta版。
Local volume 允許使用者通過標準PVC介面以簡單且可移植的方式訪問node節點的本地儲存。 PV的定義中需要包含描述節點親和性的資訊,k8s系統則使用該資訊將容器排程到正確的node節點。

配置要求

  • 使用local-volume外掛時,要求使用到了儲存裝置名或路徑都相對固定,不會隨著系統重啟或增加、減少磁碟而發生變化。
  • 靜態provisioner配置程式僅支援發現和管理掛載點(對於Filesystem模式儲存卷)或符號連結(對於塊裝置模式儲存卷)。 對於基於本地目錄的儲存卷,必須將它們通過bind-mounted的方式繫結到發現目錄中。

StorageClass與延遲繫結

官方推薦在使用local volumes時,建立一個StorageClass並把volumeBindingMode欄位設定為“WaitForFirstConsumer”。雖然local volumes還不支援動態的provisioning管理功能,但我們仍然可以建立一個StorageClass並使用延遲卷繫結的功能,將volume binding延遲至pod scheduling階段執行。
這樣可以確保PersistentVolumeClaim繫結策略將Pod可能具有的任何其他node節點約束也進行評估,例如節點資源要求、節點選擇器、Pod親和性和Pod反親和性。

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

外部static provisioner

配置local volume後,可以使用一個外部的靜態配置器來幫助簡化本地儲存的管理。 Provisioner 配置程式將通過為每個卷建立和清理PersistentVolumes來管理髮現目錄下的卷。
Local storage provisioner要求管理員在每個節點上預配置好local volumes,並指明該local volume是屬於以下哪種型別:

  • Filesystem volumeMode (default) PVs - 需要掛載到發現目錄下面。
  • Block volumeMode PVs - 需要在發現目錄下建立一個指向節點上的塊裝置的符號連結。

一個local volume,可以是掛載到node本地的磁碟、磁碟分割槽或目錄。

Local volumes雖然可以支援建立靜態PersistentVolume,但到目前為止仍不支援動態的PV資源管理。
這意味著,你需要自己手動去處理部分PV管理的工作,但考慮到至少省去了在建立pod時手寫PV YAML配置檔案的工作,這個功能還是很值得的。

建立基於Local volumes的PV的示例

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 100Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /mnt/disks/ssd1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - example-node
  • nodeAffinity欄位是必須配置的,k8s依賴於這個標籤為你定義的Pods在正確的nodes節點上找到需要使用的local volumes。
  • 使用volumeMode欄位時,需要啟用BlockVolume 這一Alpha feature特性。
  • volumeMode欄位的預設值是Filesystem,但也支援配置為Block,這樣就會把node節點的local volume作為容器的一個裸塊裝置掛載使用。

資料安全風險

local volume仍受node節點可用性方面的限制,因此並不適用於所有應用程式。 如果node節點變得不健康,則local volume也將變得不可訪問,使用這個local volume的Pod也將無法執行。 使用local voluems的應用程式必須能夠容忍這種降低的可用性以及潛在的資料丟失,是否會真得導致這個後果將取決於node節點底層磁碟儲存與資料保護的具體實現了。

hostPath與local volume在功能上的異同分析

  • 二者都基於node節點本地儲存資源實現了容器內資料的持久化功能,都為某些特殊場景下提供了更為適用的儲存解決方案;
  • 前者時間很久了,所以功能穩定,而後者因為年輕,所以功能的可靠性與穩定性還需要經歷時間和案例的歷練,尤其是對Block裝置的支援還只是alpha版本;
  • 二者都為k8s儲存管理提供了PV、PVC和StorageClass的方法實現;
  • local volume實現的StorageClass不具備完整功能,目前只支援卷的延遲繫結;
  • hostPath是單節點的本地儲存卷方案,不提供任何基於node節點親和性的pod排程管理支援;
  • local volume適用於小規模的、多節點的k8s開發或測試環境,尤其是在不具備一套安全、可靠且效能有保證的儲存叢集服務時;

local volume的安裝配置方法

local-volume專案及地址:https://github.com/kubernetes-incubator/external-storage/tree/master/local-volume

Step 1:配置k8s叢集使用本地磁碟儲存

如果使用block塊裝置,則需要啟用Alpha的功能特性:k8s v1.10+

$ export KUBE_FEATURE_GATES="BlockVolume=true"

注:如果是已經部署好的k8s v1.10+叢集,需要為幾個主要元件均開啟對該特性的支援後,才能使用block塊裝置功能。如果k8s是低於1.10版本,則還需要啟用其它的幾個功能特性,因為在低版本中這些功能特性還都是alpha版本的。

根據大家搭建k8s的方法的不同,下面提供了四種情況下的配置說明。

Option 1: GCE(Google Compute Engine)叢集

使用kube-up.sh啟動的GCE叢集將自動格式化並掛載所請求的Local SSDs,因此您可以使用預先生成的部署規範部署配置器並跳至步驟4,除非您要自定義配置器規範或儲存類。

$ NODE_LOCAL_SSDS_EXT=<n>,<scsi|nvme>,fs cluster/kube-up.sh
$ kubectl create -f provisioner/deployment/kubernetes/gce/class-local-ssds.yaml
$ kubectl create -f provisioner/deployment/kubernetes/gce/provisioner_generated_gce_ssd_volumes.yaml

Option 2: GKE(Google Kubernetes Engine)叢集

GKE叢集將自動格式化並掛載所請求的Local SSDs。在GKE documentation中有更詳細的說明。
然後,跳至步驟4。

Option 3: 使用裸機環境搭建的叢集

  1. 根據應用程式的使用要求對每個節點上的本地資料磁碟進行分割槽和格式化。
  2. 定義一個StorageClass,並在一個發現目錄下掛載所有要使用的儲存檔案系統。 發現目錄是在configmap中指定,見下文。
  3. 如上所述,使用KUBE_FEATURE_GATES配置Kubernetes API Server, controller-manager, scheduler, 和所有節點上的 kubelets。
  4. 如果沒有使用預設的Kubernetes排程程式策略,則必須啟用以下特性:
    (1) Pre-1.9: NoVolumeBindConflict
    (2) 1.9+: VolumeBindingChecker

說明:在我們使用測試環境中,是一套3節點的k8s測試環境,為了模擬測試local volume功能,直接結合使用了下面option4中提供的ram disks測試方法,建立了3個tmpfs格式的檔案系統掛載資源。

Option 4: 使用一個本機單節點的測試叢集

(1)建立/mnt/disks目錄,並在該目錄下掛載幾個子目錄。下面是使用三個ram disks做一個真實儲存卷的模擬測試。

$ mkdir /mnt/fast-disks
$ for vol in vol1 vol2 vol3;
do
    mkdir -p /mnt/fast-disks/$vol
    mount -t tmpfs $vol /mnt/fast-disks/$vol
done

(2)建立單機k8s本地測試叢集

$ ALLOW_PRIVILEGED=true LOG_LEVEL=5 FEATURE_GATES=$KUBE_FEATURE_GATES hack/local-up-cluster.sh

Step 2: 建立一個StorageClass (1.9+)

要延遲卷繫結直到pod排程並處理單個pod中的多個本地PV,必須建立StorageClass並將volumeBindingMode設定為WaitForFirstConsumer。

# Only create this for K8s 1.9+
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-disks
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
# Supported policies: Delete, Retain
reclaimPolicy: Delete
$ kubectl create -f provisioner/deployment/kubernetes/example/default_example_storageclass.yaml
  • default_example_storageclass.yaml檔案請到local volume專案檔案中查詢需要使用的yaml檔案

Step 3: 建立local persistent volumes

Option 1: local volume static provisioner 方式

配置一個外部的靜態配置器。
(1)生成Provisioner的ServiceAccount,Roles,DaemonSet和ConfigMap規範,並對其進行自定義配置。
此步驟使用helm模板生成需要的配置規格。 有關設定說明,請參閱helm README
使用預設值生成Provisioner的配置規格,請執行:

helm template ./helm/provisioner > ./provisioner/deployment/kubernetes/provisioner_generated.yaml
  • 這裡是將模板經過渲染後得到最終使用的各項資源定義檔案。

如果是使用自定義的配置檔案的話:

helm template ./helm/provisioner --values custom-values.yaml > ./provisioner/deployment/kubernetes/provisioner_generated.yaml

(2)部署Provisioner
如果使用者對Provisioner的yaml檔案的內容感到滿意,就可以使用kubectl建立Provisioner的DaemonSet和ConfigMap了。

# kubectl create -f ./provisioner/deployment/kubernetes/provisioner_generated.yaml
configmap "local-provisioner-config" created
daemonset.extensions "local-volume-provisioner" created
serviceaccount "local-storage-admin" created
clusterrolebinding.rbac.authorization.k8s.io "local-storage-provisioner-pv-binding" created
clusterrole.rbac.authorization.k8s.io "local-storage-provisioner-node-clusterrole" created
clusterrolebinding.rbac.authorization.k8s.io "local-storage-provisioner-node-binding" created

(3)檢查已自動發現的local volumes
一旦啟動,外部static provisioner將發現並自動創建出 local-volume PVs。
我們檢視下上面測試中創建出的PVs有哪些:

# kubectl get pv
NAME                CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
local-pv-436f0527   495Mi      RWO            Delete           Available             fast-disks               2m
local-pv-77a4ffb0   495Mi      RWO            Delete           Available             fast-disks               2m
local-pv-97f7ec5c   495Mi      RWO            Delete           Available             fast-disks               2m
local-pv-9f0ddba3   495Mi      RWO            Delete           Available             fast-disks               2m
local-pv-a0dfdc91   495Mi      RWO            Delete           Available             fast-disks               2m
local-pv-a52333e3   495Mi      RWO            Delete           Available             fast-disks               2m
local-pv-bed86926   495Mi      RWO            Delete           Available             fast-disks               2m
local-pv-d037a0d1   495Mi      RWO            Delete           Available             fast-disks               2m
local-pv-d26c3252   495Mi      RWO            Delete           Available             fast-disks               2m
  • 因為是有3個node節點,每個上面的/mnt/fast-disks自動發現目錄下掛載了3個檔案系統,所以這裡查詢的結果是生成了9個PVs

檢視某一個PV的詳細描述資訊:

# kubectl describe pv local-pv-436f0527
Name:              local-pv-436f0527
Labels:            <none>
Annotations:       pv.kubernetes.io/provisioned-by=local-volume-provisioner-kube-node2-c3733876-b56f-11e8-990b-080027395360
Finalizers:        [kubernetes.io/pv-protection]
StorageClass:      fast-disks
Status:            Available
Claim:             
Reclaim Policy:    Delete
Access Modes:      RWO
Capacity:          495Mi
Node Affinity:     
  Required Terms:  
    Term 0:        kubernetes.io/hostname in [kube-node2]
Message:           
Source:
    Type:  LocalVolume (a persistent volume backed by local storage on a node)
    Path:  /mnt/fast-disks/vol2
Events:    <none>
  • 此時就可以直接通過引用名為fast-disks的storageClassName名稱來宣告使用上述PV並將其繫結到PVC。

Option 2: 手動建立 local persistent volume

參照前文介紹local volume概念的章節中已經講解過的PersistentVolume使用示例。

Step 4: 建立 local persistent volume claim

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: example-local-claim
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 50Mi
  storageClassName: fast-disks
  • 請在使用時替換為您實際的儲存容量需求和storageClassName值。
# kubectl create -f local-pvc.yaml
persistentvolumeclaim "example-local-claim" created

# kubectl get pvc
NAME                  STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
example-local-claim   Pending  

 # kubectl describe pvc example-local-claim
Name:          example-local-claim
Namespace:     default
StorageClass:  fast-disks
Status:        Pending
Volume:        
Labels:        <none>
Annotations:   <none>
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      
Access Modes:  
Events:
  Type    Reason                Age               From                         Message
  ----    ------                ----              ----                         -------
  Normal  WaitForFirstConsumer  6s (x6 over 59s)  persistentvolume-controller  waiting for first consumer to be created before binding
  • 我們可以看到儲存卷延遲繫結的效果,在繫結到容器前,該PVC的狀態會是pending

Step 5:建立一個測試Pod並引用上面建立的PVC

apiVersion: v1
kind: Pod
metadata:
  name: local-pvc-pod
spec:
  containers:
  - image: busybox
    name: test-local-pvc
    command: [ "sleep", "3600" ]
    volumeMounts:
    - mountPath: /data
      name: data-volume
  volumes:
  - name: data-volume
    persistentVolumeClaim:
      claimName: example-local-claim

建立並檢視:

# kubectl create -f example-local-pvc-pod.yaml
pod "local-pvc-pod" created

# kubectl get pods -o wide
NAME                             READY     STATUS    RESTARTS   AGE       IP            NODE
client1                          1/1       Running   67         64d       172.30.80.2   kube-node3
local-pvc-pod                    1/1       Running   0          2m        172.30.48.6   kube-node1

檢視pod中容器掛載PVC的配置詳情,這裡只截取了部分資訊:

# kubectl describe pod local-pvc-pod
Name:         local-pvc-pod
Namespace:    default
Node:         kube-node1/172.16.10.101
Start Time:   Thu, 15 Nov 2018 16:39:30 +0800
Labels:       <none>
Annotations:  <none>
Status:       Running
IP:           172.30.48.6
Containers:
  test-local-pvc:
......
    Mounts:
      /data from data-volume (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-qkhcf (ro)
Conditions:
  Type           Status
  Initialized    True
  Ready          True
  PodScheduled   True
Volumes:
  data-volume:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  example-local-claim
    ReadOnly:   false
......

進入容器中檢視掛載的資料卷:

[[email protected] ~]# kubectl exec -it local-pvc-pod -c test-local-pvc /bin/sh
/ # ls
bin   data  dev   etc   home  proc  root  sys   tmp   usr   var
/ # df -h
Filesystem                Size      Used Available Use% Mounted on
overlay                  41.0G      8.1G     32.8G  20% /
tmpfs                    64.0M         0     64.0M   0% /dev
tmpfs                   495.8M         0    495.8M   0% /sys/fs/cgroup
vol3                    495.8M         0    495.8M   0% /data

再回過頭來看下PVC的狀態,已經變成了Bound:

# kubectl get pvc
NAME                        STATUS    VOLUME              CAPACITY   ACCESS MODES   STORAGECLASS   AGE
example-local-claim         Bound     local-pv-a0dfdc91   495Mi      RWO            fast-disks     1h

一個關於local volume功能侷限性問題的討論

在上面的實驗過程中不知道大家有沒有發現一處問題,就是我們在定義PVC時是指定的申請50Mi的空間,而實際掛載到測試容器上的儲存空間是495.8M,剛好是我們在某個node節點上掛載的一個檔案系統的全部空間。
為什麼會這樣呢?這就是我們所使用的這個local persistent volume外部靜態配置器的功能侷限性所在了。它不支援動態的PV空間申請管理。

也就是說,雖然通過這個靜態PV配置器,我們省去了手寫PV YAML檔案的痛苦,但仍然需要手工處理這項工作:

  • 手工維護在ConfigMap中指定的自動發現目錄下掛載的檔案系統資源,或者是block裝置的符號連結;
  • 我們需要對能夠使用的本地儲存資源提前做一個全域性的規劃,然後劃分為各種尺寸的卷後掛載到自動發現目錄下,當然了只要是還有空閒儲存資源,現有現掛載也是可以的。

那如果以前給某容器分配的一個儲存空間不夠用了怎麼辦?
給大家的一個建議是使用Linux下的LVM(邏輯分割槽管理)來管理每個node節點上的本地磁碟儲存空間。

  • 建立一個大的VG分組,把一個node節點上可以使用的儲存空間都放進去;
  • 按未來一段時間內的容器儲存空間使用預期,提前批量創建出一部分邏輯卷LVs,都掛載到自動發現目錄下去;
  • 不要把VG中的儲存資源全部用盡,預留少部分用於未來給個別容器擴容儲存空間的資源;
  • 使用lvextend為特定容器使用的儲存捲進行擴容;

如果容器需要使用block塊裝置怎麼配置

有幾點會與上面的配置方法上不同。
首先,是要在k8s主要的元件上均開啟用於支援block塊裝置的特性。

KUBE_FEATURE_GATES="BlockVolume=true"

其次是,定義一個"Block"型別的volumeMode PVC,為容器申請一個"Block"型別的PV。

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: example-block-local-claim
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 50Mi
  volumeMode: Block
  storageClassName: fast-disks
  • 請特別注意,在上面的yaml檔案中使用的volumeMode欄位。

Local volumes的最佳實踐

  • 為了更好的IO隔離效果,建議將一整塊磁碟作為一個儲存卷使用;
  • 為了得到儲存空間的隔離,建議為每個儲存卷使用一個獨立的磁碟分割槽;
  • 在仍然存在指定了某個node節點的親和性關係的舊PV時,要避免重新建立具有相同節點名稱的node節點。 否則,系統可能會認為新節點包含舊的PV。
  • 對於具有檔案系統的儲存卷,建議在fstab條目和該卷的mount安裝點的目錄名中使用它們的UUID(例如ls -l /dev/disk/by-uuid的輸出)。 這種做法可確保不會安裝錯誤的本地卷,即使其裝置路徑發生了更改(例如,如果/dev/sda1在新增新磁碟時變為/dev/sdb1)。 此外,這種做法將確保如果建立了具有相同名稱的另一個節點時,該節點上的任何卷仍然都會是唯一的,而不會被誤認為是具有相同名稱的另一個節點上的卷。
  • 對於沒有檔案系統的原始塊儲存卷,請使用其唯一ID作為符號連結的名稱。 根據您的環境,/dev/disk/by-id/中的卷ID可能包含唯一的硬體序列號。 否則,應自行生成一個唯一ID。 符號連結名稱的唯一性將確保如果建立了另一個具有相同名稱的節點,則該節點上的任何卷都仍然是唯一的,而不會被誤認為是具有相同名稱的另一個節點上的卷。

停用local volume的方法

當您想要停用本地卷時,這是一個可行的工作流程。

  • 關閉使用這些卷的Pods;
  • 從node節點上移除local volumes(比如unmounting, 拔出磁碟等等);
  • 手動刪除相應的PVCs物件;
  • Provisioner將嘗試清理卷,但會由於卷不再存在而失敗;
  • 手動刪除相應的PVs物件。
    注:以上工作也是拜我們所使用的外部靜態配置器所賜。

參考資料:
https://github.com/kubernetes-incubator/external-storage/tree/master/local-volume
https://kubernetes.io/docs/concepts/storage/volumes/