本文介紹了在多可用區Kubernetes叢集中挂載雲盤的實作方案,核心原理為:
通過StorageClass中的volumeBindingMode參數配置給出兩種挂載雲盤的方案:”Pod跟着雲盤走“、”雲盤跟着Pod走“;
volumeBindingMode配置參考:
卷綁定模式說明多可用區叢集
部署叢集的時候使用多可用區方案來保證叢集的高可用已得到大家的共識,這樣叢集在某個可用區出現異常時仍然可以保持叢集對外提供正常服務,阿裡雲ACK服務提供了多可用區叢集部署方案,可參考
使用文檔。
容器存儲卷為使用者資料持久化提供了實作方案,通過将外置存儲挂載到容器内部的方式為應用提供存儲空間,在容器被銷毀後存儲卷中的資料依然可以繼續被其他的應用使用。常用的存儲卷類型有塊存儲、檔案存儲、對象存儲,其對可用區的敏感性如下:
塊存儲:如阿裡雲雲盤;ECS和雲盤需要在同一個可用區内聯合使用;
檔案存儲:如阿裡雲NAS、CPFS服務;ECS和NAS可以跨可用區挂載,但要求在相同VPC内;
對象存儲:如阿裡雲OSS;ECS和OSS可以跨可用區、跨Region挂載;
可見,隻有雲盤存儲卷對可用區有強制要求,需要在叢集内部根據資料卷可用區排程Pod,PV(雲盤)的可用區和Pod被排程的可用區必須一緻才可以挂載成功。k8s實作了針對資料卷的排程器,會根據Pod使用的PV資訊把Pod排程到合适的可用區;
下面針對阿裡雲雲盤講述一下如何在多可用環境實作塊存儲資料卷的可用區排程。
雲盤多可用區使用方案
使用StatefulSet在多可用區叢集挂載雲盤
關于應用負載,Pod中挂載雲盤資料卷的場景有如下特點:
雲盤作為塊存儲隻能挂載到相同可用區的節點上,且雲盤不是共享存儲,隻能被一個Pod挂載使用;
常用的應用編排模闆中:Deployment和Daemonset都是具有相同Pod配置的多個副本模式,如果Pod挂載資料卷,則所有Pod的資料卷需要是一樣的,而雲盤隻能挂載到一個Pod的限制,是以Deployment、DaemonSet不适合挂載雲盤資料卷。
Statefulset類型應用提供了兩種配置Volume的方式,一種和Deployment一樣直接通過volumes定義persistentVolumeClaim,這種方式隻能為StatefulSet應用配置一個特定的PVC(雲盤),不能用在多副本場景中;
另一種是通過volumeClaimTemplates配置,這個方式為每個Pod配置一個具有一定規則名字的PVC,不通的PVC對應不同的雲盤卷。這樣的配置可以啟動多個副本,且每個副本都挂載了獨立的雲盤。
即:在應用使用雲盤的場景中,需要使用StatefulSet進行編排,并使用volumeClaimTemplates配置挂載雲盤。多可用區場景亦是如此。
通過volumeClaimTemplates為每個Pod配置的PVC名字規則:
PVC的名字 = {volumeClaimTemplates名字} + "-" + {StatefulSet名字} + "-" + 序号
例如如下配置的StatefulSet:
apiVersion: apps/v1beta2
kind: StatefulSet
metadata:
name: web
spec:
replicas: 3
***
volumeClaimTemplates:
- metadata:
name: disk-ssd
spec:
***
為3個Pod分别建立配置的pvc名字為:disk-ssd-web-0、disk-ssd-web-1、disk-ssd-web-2;
多可用區叢集挂載雲盤方案
沒有挂載雲盤資料卷的情況下,pod啟動過程為:
Pod建立好後進入排程流程;
檢查Pod引用的pvc,如果pvc處于unbound狀态,等待pvc變成bound;如果pvc處于bound狀态,繼續排程;
根據nodeSelector、Node狀态等配置選擇最合适的目标節點;
在目标節點啟動Pod;
考慮挂載雲盤卷排程方案1:
根據PV中的排程資訊選擇滿足條件的目标節點集合;
根據nodeSelector、Node狀态等配置選擇滿足條件的目标節點;
考慮挂載雲盤卷排程方案2:
檢查Pod引用的pvc,如果pvc處于unbound狀态;
根據目标節點的可用區資訊,動态建立雲盤(PV),這時PV的可用區和目标啟動節點相同;
上面兩種挂載雲盤資料卷方案中:
方案1:先确定了PV(雲盤)可用區資訊,并将雲盤可用區資訊作為Pod排程的一個參量,即Pod随着雲盤走;
方案2:先确定了Pod運作的目标節點,再動态建立PV(雲盤)并綁定,即雲盤跟着Pod走。
Pod跟着雲盤走:
此方案的關鍵點是:Pod排程前确定建立好PV,并确定PV的可用區資訊。在靜态存儲卷或動态存儲卷中都可以實作此方案。
部署前需要規劃好應用期望運作的可用區,并手動(靜态卷)/自動(動态卷)建立好雲盤、PV對象;如果一個應用期望運作在多個可用區,則需要在多個可用區申請雲盤并建立多個PV。每個PV對象中需要添加相應的可用區排程資訊。
排程資訊可以通過在PV的Label中添加如下配置(由VolumeZonePredicate排程器進行排程):
labels:
failure-domain.beta.kubernetes.io/zone: cn-hangzhou-b
failure-domain.beta.kubernetes.io/region: cn-hangzhou
排程資訊也可以通過在PV的nodeAffinity中添加如下配置(由VolumeBindingPredicate排程器進行排程):
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: topology.diskplugin.csi.alibabacloud.com/zone
operator: In
values:
- cn-shenzhen-a
下面示例是在一個包含三可用區節點的叢集進行:
# kubectl describe node | grep failure-domain.beta.kubernetes.io/zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-a
failure-domain.beta.kubernetes.io/zone=cn-beijing-a
failure-domain.beta.kubernetes.io/zone=cn-beijing-b
failure-domain.beta.kubernetes.io/zone=cn-beijing-b
failure-domain.beta.kubernetes.io/zone=cn-beijing-c
failure-domain.beta.kubernetes.io/zone=cn-beijing-c
靜态資料卷
靜态存儲卷指PV對象、雲盤執行個體需要管理者手動建立,并将可用區等資訊配置到資料卷中。在使用靜态存儲卷前需要先建立好雲盤、pv。
本示例在StatefulSet應用中啟動三個Pod,每個pod挂載一個雲盤資料卷,且分别運作在cn-beijing-a、cn-beijing-b、cn-beijing-c三個可用區;期望:StatefulSet名字為 web,volumeClaimTemplates名字為disk-ssd;
分别按照下面模闆建立pvc、pv:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: disk-ssd-web-0
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 25Gi
selector:
matchLabels:
alicloud-pvname: pv-disk-ssd-web-0
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-disk-ssd-web-0
labels:
alicloud-pvname: pv-disk-ssd-web-0
spec:
capacity:
storage: 25Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
csi:
driver: diskplugin.csi.alibabacloud.com
volumeHandle: d-2zeeujx1zexxkbc8ny4b
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: topology.diskplugin.csi.alibabacloud.com/zone
operator: In
values:
- cn-beijing-a
其中pvc、pv、可用區、雲盤ID對應關系:
disk-ssd-web-0 ==> pv-disk-ssd-web-0 ==> cn-beijing-a ==> d-2zeeujx1zexxkbc8ny4b
disk-ssd-web-1 ==> pv-disk-ssd-web-1 ==> cn-beijing-b ==> d-2ze4n7co1x8w8xs95sqk
disk-ssd-web-2 ==> pv-disk-ssd-web-2 ==> cn-beijing-c ==> d-2zeaed32ln6d8sbpichh
# kubectl get pvc | grep disk-ssd-web
disk-ssd-web-0 Bound pv-disk-ssd-web-0 25Gi RWO 59s
disk-ssd-web-1 Bound pv-disk-ssd-web-1 25Gi RWO 56s
disk-ssd-web-2 Bound pv-disk-ssd-web-2 25Gi RWO 54s
# kubectl get pv | grep disk-ssd-web
pv-disk-ssd-web-0 25Gi RWO Retain Bound default/disk-ssd-web-0 2m43s
pv-disk-ssd-web-1 25Gi RWO Retain Bound default/disk-ssd-web-1 2m40s
pv-disk-ssd-web-2 25Gi RWO Retain Bound default/disk-ssd-web-2 2m38s
部署下面StatefulSet模闆:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1beta2
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- name: disk-ssd
mountPath: /data
volumeClaimTemplates:
- metadata:
name: disk-ssd
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: csi-disk-topology
resources:
requests:
storage: 20Gi
執行下面指令擷取Pod資訊,上述模闆啟動的三個Pod分别運作在a、b、c三個可用區:
# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 14m
web-1 1/1 Running 0 13m
web-2 1/1 Running 0 13m
# kubectl describe pod | grep Node
Node: cn-beijing.172.16.1.101/172.16.1.101
Node: cn-beijing.172.16.2.87/172.16.2.87
Node: cn-beijing.172.16.3.197/172.16.3.197
# kubectl describe node cn-beijing.172.16.1.101 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-a
# kubectl describe node cn-beijing.172.16.2.87 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-b
# kubectl describe node cn-beijing.172.16.3.197 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-c
分别删除三個pod,驗證重新開機後的Pod依然落在相應可用區:
# kubectl delete pod --all
pod "web-0" deleted
pod "web-1" deleted
pod "web-2" deleted
# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 61s
web-1 1/1 Running 0 41s
web-2 1/1 Running 0 21s
# kubectl describe pod | grep Node
Node: cn-beijing.172.16.1.101/172.16.1.101
Node: cn-beijing.172.16.2.87/172.16.2.87
Node: cn-beijing.172.16.3.197/172.16.3.197
# kubectl describe node cn-beijing.172.16.1.101 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-a
# kubectl describe node cn-beijing.172.16.2.87 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-b
# kubectl describe node cn-beijing.172.16.3.197 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-c
動态資料卷
建立支援多可用區的StorageClass,如下配置:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: csi-disk-multizone
provisioner: diskplugin.csi.alibabacloud.com
parameters:
type: cloud_ssd
zoneId: cn-beijing-a,cn-beijing-b,cn-beijing-c
reclaimPolicy: Delete
zoneId:配置多個可用區,生成多個雲盤時會在多個可用區之間循環建立;
按照下面StatefulSet配置建立應用:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1beta2
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- name: disk-ssd
mountPath: /data
volumeClaimTemplates:
- metadata:
name: disk-ssd
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: csi-disk-topology
resources:
requests:
storage: 20Gi
檢視生成的Pod、PVC、PV資訊:
# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2m2s
web-1 1/1 Running 0 84s
web-2 1/1 Running 0 52s
# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
disk-ssd-web-0 Bound disk-9e6a6f65-f3fc-11e9-a7a7-00163e165b60 20Gi RWO csi-disk-multizone 2m6s
disk-ssd-web-1 Bound disk-b5071f37-f3fc-11e9-a7a7-00163e165b60 20Gi RWO csi-disk-multizone 88s
disk-ssd-web-2 Bound disk-c81b6163-f3fc-11e9-a7a7-00163e165b60 20Gi RWO csi-disk-multizone 56s
# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
disk-9e6a6f65-f3fc-11e9-a7a7-00163e165b60 20Gi RWO Delete Bound default/disk-ssd-web-0 csi-disk-multizone 116s
disk-b5071f37-f3fc-11e9-a7a7-00163e165b60 20Gi RWO Delete Bound default/disk-ssd-web-1 csi-disk-multizone 85s
disk-c81b6163-f3fc-11e9-a7a7-00163e165b60 20Gi RWO Delete Bound default/disk-ssd-web-2 csi-disk-multizone 39s
檢視Pod、pv所在的可用區,分别落在3個可用區:
# kubectl describe pod web-0 | grep Node
Node: cn-beijing.172.16.1.101/172.16.1.101
# kubectl describe node cn-beijing.172.16.1.101 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-a
# kubectl describe pod web-1 | grep Node
Node: cn-beijing.172.16.2.87/172.16.2.87
# kubectl describe node cn-beijing.172.16.2.87 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-b
# kubectl describe pod web-2 | grep Node
Node: cn-beijing.172.16.3.197/172.16.3.197
# kubectl describe node cn-beijing.172.16.3.197 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-c
# kubectl describe pv disk-9e6a6f65-f3fc-11e9-a7a7-00163e165b60 | grep zone
Term 0: topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-a]
# kubectl describe pv disk-b5071f37-f3fc-11e9-a7a7-00163e165b60 | grep zone
Term 0: topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-b]
# kubectl describe pv disk-c81b6163-f3fc-11e9-a7a7-00163e165b60 | grep zone
Term 0: topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-c]
雲盤跟着Pod走:
雲盤跟着Pod走是指在Pod完成排程,已經确定了運作節點所在可用區以後,再動态建立雲盤、PV的方式。是以雲盤跟着Pod走的方案隻适用于動态資料卷。
建立WaitForFirstConsumer類型的StorageClass:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: csi-disk-topology
provisioner: diskplugin.csi.alibabacloud.com
parameters:
type: cloud_ssd
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
WaitForFirstConsumer:表示使用定義了這個storageClass的pvc,在pod啟動的時候先進行pod排程,再觸發pv、雲盤的Provision操作。
建立下面StatefulSet應用:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1beta2
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- name: disk-ssd
mountPath: /data
volumeClaimTemplates:
- metadata:
name: disk-ssd
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: csi-disk-topology
resources:
requests:
storage: 20Gi
擷取Pod、PV的資訊:
# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2m5s
web-1 1/1 Running 0 100s
web-2 1/1 Running 0 74s
# kubectl describe pod web-0 | grep Node
Node: cn-beijing.172.16.3.197/172.16.3.197
# kubectl describe node cn-beijing.172.16.3.197 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-c
# kubectl describe pod web-1 | grep Node
Node: cn-beijing.172.16.1.101/172.16.1.101
# kubectl describe node cn-beijing.172.16.1.101 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-a
# kubectl describe pod web-2 | grep Node
Node: cn-beijing.172.16.2.87/172.16.2.87
# kubectl describe node cn-beijing.172.16.2.87 | grep zone
failure-domain.beta.kubernetes.io/zone=cn-beijing-b
# kubectl describe pv disk-d4b08afa-f3fe-11e9-a7a7-00163e165b60 | grep zone
Term 0: topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-c]
# kubectl describe pv disk-e32d5fcf-f3fe-11e9-a7a7-00163e165b60 | grep zone
Term 0: topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-a]
# kubectl describe pv disk-f2cec31a-f3fe-11e9-a7a7-00163e165b60 | grep zone
Term 0: topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-b]
”雲盤跟着Pod走“的方案需要澄清一下:
隻有第一次啟動pod的時候,且配置了”雲盤跟着Pod走“政策,才會出現先排程pod再觸發建立pv、雲盤的場景。當pod重新開機的時候,這時走的流程是根據pv的zone資訊進行排程,即”pod跟着雲盤走“。
總結:
本文給出了2種多可用區叢集使用雲盤資料卷方案:
Pod跟着雲盤走:需要先建立好雲盤、PV對象,根據雲盤的可用區資訊排程負載,這個場景更多适合挂載已有雲盤的場景;且這個方案在排程層面更多的依賴雲盤可用區的排程資訊,而弱化了其他排程器的排程能力。
雲盤跟着Pod走:先執行負載排程,再根據Pod所在可用區資訊建立雲盤、PV對象;這個方案充分考慮了所有排程器的排程政策,在排程完成後建立PV并挂載使用,這個過程雲盤可用區資訊不參與排程。
對于應用負載使用雲盤資料卷的場景,更推薦使用”雲盤跟着Pod走“的方案。