轉載自:https://blog.csdn.net/watermelonbig/article/details/84108424
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: {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
查看下創建出來的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:
......
1
2
3
4
5
6
7
8
9
10
11
12
13
14
可以進入到容器中查看下實際的卷掛載結果:
# kubectl exec -it test-pod -c test-emptydir /bin/sh
1
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
查看下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
......
1
2
3
4
5
6
7
8
9
10
11
12
13
14
我們登錄到容器中,進入掛載的/test-data目錄中,創建個測試文件。
# kubectl exec -it test-pod2 -c test-hostpath /bin/sh
/ # echo 'testtesttest' > /test-data/test.log
/ # exit
1
2
3
我們在運行該pod的node節點上,可以看到如下的文件和內容。
[root@kube-node2 test-data]# cat /test-data/test.log
testtesttest
1
2
現在,我們把該pod刪除掉,再看看node節點上的hostPath使用的目錄與數據會有什麼變化。
[root@kube-node1 ~]# kubectl delete pod test-pod2
pod "test-pod2" deleted
1
2
到運行原pod的node節點上查看如下。
[root@kube-node2 test-data]# ls -l
total 4
-rw-r--r-- 1 root root 13 Nov 14 00:25 test.log
[root@kube-node2 test-data]# cat /test-data/test.log
testtesttest
1
2
3
4
5
在使用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
1
2
3
4
5
6
7
8
9
10
該場景僅能用於單節點的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
1
2
3
4
5
6
外部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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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"
1
注:如果是已經部署好的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
1
2
3
Option 2: GKE(Google Kubernetes Engine)集羣
GKE集羣將自動格式化並掛載所請求的Local SSDs。在GKE documentation中有更詳細的說明。
然後,跳至步驟4。
Option 3: 使用裸機環境搭建的集羣
根據應用程序的使用要求對每個節點上的本地數據磁盤進行分區和格式化。
定義一個StorageClass,並在一個發現目錄下掛載所有要使用的存儲文件系統。 發現目錄是在configmap中指定,見下文。
如上所述,使用KUBE_FEATURE_GATES配置Kubernetes API Server, controller-manager, scheduler, 和所有節點上的 kubelets。
如果沒有使用默認的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
1
2
3
4
5
6
(2)創建單機k8s本地測試集羣
$ ALLOW_PRIVILEGED=true LOG_LEVEL=5 FEATURE_GATES=$KUBE_FEATURE_GATES hack/local-up-cluster.sh
1
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
1
2
3
4
5
6
7
8
9
$ kubectl create -f provisioner/deployment/kubernetes/example/default_example_storageclass.yaml
1
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
1
這裏是將模板經過渲染後得到最終使用的各項資源定義文件。
如果是使用自定義的配置文件的話:
helm template ./helm/provisioner --values custom-values.yaml > ./provisioner/deployment/kubernetes/provisioner_generated.yaml
1
(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
1
2
3
4
5
6
7
(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
1
2
3
4
5
6
7
8
9
10
11
因爲是有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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
此時就可以直接通過引用名爲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
1
2
3
4
5
6
7
8
9
10
11
請在使用時替換爲您實際的存儲容量需求和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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
我們可以看到存儲卷延遲綁定的效果,在綁定到容器前,該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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
創建並查看:
# 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
1
2
3
4
5
6
7
查看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
......
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
進入容器中查看掛載的數據卷:
[root@kube-node1 ~]# 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
1
2
3
4
5
6
7
8
9
再回過頭來看下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
1
2
3
一個關於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"
1
其次是,定義一個"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
1
2
3
4
5
6
7
8
9
10
11
12
請特別注意,在上面的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/