隨着自研上雲的深刻,愈來愈多的有狀態服務對於在 TKE 集羣中使用雲上存儲能力的需求也愈來愈強烈。html
目前 騰訊雲容器服務 TKE(Tencent Kubernetes Engine)已支持在 TKE 集羣中的應用使用多種存儲服務,包括 雲硬盤 CBS、文件存儲 CFS 以及 對象存儲 COS。TKE 經過兩種存儲插件(In-Tree 和 CSI)來支持上述能力,用戶能夠經過雲控制檯很方便地選擇存儲類型並建立對應的 PV/PVC。但仍然會有一些問題困擾着你們,好比:TKE 集羣中是否支持擴容 CBS 雲盤;若是集羣跨可用區,如何避免集羣中頻繁出現掛載(attach)失敗;TKE 中是否支持快照功能;個人應用應該選擇哪一種類型存儲;In-Tree 和 CSI 都支持 CBS,兩者有和區別,是否能把以前使用 In-Tree 插件建立的雲盤轉變爲 CSI 插件管理等。node
對於 TKE 存儲的相關問題,這裏會詳細介紹。接下來,咱們先概覽下 Kubernetes 持久化存儲的流程nginx
這裏對 Kubernetes 持久化存儲的流程作個概覽,不深刻各個組件。git
建立一個使用了持久化存儲的 pod 的流程包含如下步驟:github
kubernetes-sigs/sig-storage-lib-external-provisioner
實現了一個 external provisioner,則與 out-of-tree 插件相同;若是是 in-tree 插件,且插件直接實現了相應的創刪接口,則 PV Controller 直接調用 in-tree 插件的實現完成建立 PV。ControllerPublishVolume
)來 attach。/var/lib/kubelet/plugins
下),第二步是將 bind mount 剛纔的 global mount path到/var/lib/kubelet/pods/${pod_UUID}/volumes
下。(Provision -> Attach -> Mount; Unmount -> Detach -> Delete)web
隨着 Kubernetes 社區發展,TKE 前後支持了 In-Tree 和 CSI 兩種存儲插件。兩者在功能上的主要區別在於 In-Tree 存儲插件僅支持在 TKE 集羣使用 CBS,而 CSI 支持使用 CBS、CFS、COS。api
類型 | 支持CBS | 支持CFS | 支持COS | 參考 |
---|---|---|---|---|
In-Tree | √ | × | × | |
CSI | √ | √ | √ | https://github.com/TencentClo... |
cloud.tencent.com/qcloud-cbs
,因此也可稱爲 QcloudCbs,在 TKE 集羣中有個默認的名爲cbs
的 StorageClass。NAME PROVISIONER AGE cbs (default) cloud.tencent.com/qcloud-cbs 48m
In-Tree 插件只實現了使用 CBS 的能力,其主要特性有:服務器
騰訊雲存儲 | 靜態數據卷 | 動態數據卷 | 拓撲感知 | 調度器感知節點 maxAttachLimit |
---|---|---|---|---|
騰訊雲硬盤(CBS) | 支持兩種使用方式: <li>直接經過 volume 使用</li> <li>經過PV/PVC使用(推薦)</li> | 支持 | 支持。pod 調度後,在同一個可用區建立 volume。避免 CBS 跨可用區沒法使用。 | 支持。雲服務器(cvm)能夠掛載的雲硬盤(cbs)是有上限的。調度器調度 pod 時過濾掉超過最大可掛載 CBS 數量的節點。 |
下面簡單瞭解下 In-Tree 插件 QcloudCbs 的架構圖,瞭解各相關組件分別完成何種工做。架構
上圖是包含 TKE In-Tree 存儲插件的 Kubernetes 存儲架構圖。圖中綠色部分,皆屬於 In-Tree 插件 QcloudCbs 的實現範疇。
由上述的 Kubernetes持久化存儲流程 可知要動態使用一個 cbs pv,主要有三個過程:provision、attach、mount,而這三個過程是由不一樣組件負責的:app
kubernetes-sigs/sig-storage-lib-external-provisioner
實現的一個 external provisioner,來 provision 和 delete volume。PV Controller 在這種模式下雖然不去 provision/delete volume,可是仍是會參與處理(好比 PV 和 PVC 的綁定)。MaxQcloudCbsVolumeCount
,該策略主要實現調度器感知節點 maxAttachLimit特性。而 Scheduler 原生的一個 predicate 策略:NoVolumeZoneConflictPred
,是用來把 pod 調度到已有 PV 所在 zone 的節點,這能夠避免雲盤跨可用區掛載的問題;對於新建 PV 的話,避免雲盤跨可用區掛載問題則由拓撲感知特性完成。CSI 是 Kubernetes 社區擴展卷的標準和推薦方式。TKE 的 CSI 插件包含 CBS、CFS、COS 三個 driver,本節重點介紹 CBS CSI driver,並與 QcloudCbs 進行對比。3個 driver 的靜態 pv 和動態 pv 的支持狀況以下表所示:
騰訊雲存儲 | 靜態數據卷 | 動態數據卷 |
---|---|---|
雲硬盤(CBS) | 支持 | 支持 |
文件存儲(CFS) | 支持 | 支持 |
對象存儲(COS) | 支持 | 不支持 |
存儲插件 | 靜態數據卷 | 動態數據卷 | 拓撲感知 | 調度器感知節點maxAttachLimit | 卷在線擴容 | 卷快照&恢復 |
---|---|---|---|---|---|---|
CBS CSI | √ | √ | √ | √ | √ | √ |
QcloudCbs(In-Tree) | √ | √ | √ | √ | × | × |
CSI 原理參考上圖。要實現一個 CSI driver,通常須要實現如下 3 個 gRPC services(CSI Controller Service 可選):
CSI Controller Services
(可選):controller 負責創刪卷、attach/detach、擴容、快照等。涉及的方法以下:
CSI Node Services
:負責向節點註冊 driver,mount/unmount。涉及的方法以下:
在咱們實現以外,kuberntes Team 還提供了多個外部組件,用於溝通 k8s 原生組件(apiserver、controller manager、kubelet)與本身實現的 CSI driver。
PersistentVolumeClaim
(PVC)對象,調用 driver 的CreateVolume/DeleteVolume
VolumeAttachment
對象,調用 driver 的Controller[Publish|Unpublish]Volume
PersistentVolumeClaim
對象,調用 driver 的ControllerExpandVolume
VolumeSnapshot
和VolumeSnapshotContent
CRD 對象,external-snapshotter watch VolumeSnapshotContent
對象。調用 driver 的CreateSnapshot/DeleteSnapshot/ListSnapshots
NodeGetInfo
獲取 driver 信息,而後使用 kubelet 插件註冊機制註冊 driver。CBS CSI 使用社區推薦部署方式,包含兩個 workload:
NodePlugin
,由 CBS CSI Driver 和 node-driver-registrar 兩個容器組成。負責向節點註冊 driver,並提供 mount 的能力。Controller
。由 driver 和多個 sidecar(external-provisioner、external-attacher、external-resizer、external-snapshotter、snapshot-controller)一塊兒構成,提供創刪卷、attach/detach、擴容、快照等能力
/var/lib/kubelet/plugins
的子目錄下;以後 bind mount 階段(NodePublishVolume)的 target path 是/var/lib/kubelet/pods
。因此咱們爲這兩個目錄都設置了掛載傳播(模式爲Bidirectional
)provisioner:
cbs csi 的安裝請參見 cbs csi 文檔,咱們也已經在騰訊雲控制檯支持擴展組件安裝。
本節最佳實踐均以 cbs csi 插件爲例,相應版本要求也是針對 cbs csi 插件。
cbs 雲盤不支持跨可用區掛載到節點,因此在跨可用區的集羣中推薦經過拓撲感知特性來避免跨可用區掛載的問題。
使用方式很簡單,在 storageclass 中設置 volumeBindingMode
爲WaitForFirstConsumer
,而後使用該 storageClass 便可。intree 和 csi 插件均支持。
kind: StorageClass metadata: name: cbs-topo parameters: type: cbs provisioner: com.tencent.cloud.csi.cbs reclaimPolicy: Delete volumeBindingMode: WaitForFirstConsumer
拓撲感知調度須要多個 k8s 組件配合完成,包括 scheduler、pv controller、external-provisioner。流程爲:
WaitForFirstConsumer
,即不會立刻處理該pvc的建立事件,等待 scheduler 處理;volume.kubernetes.io/selected-node: 10.0.0.72
volume.kubernetes.io/selected-node
),根據 nodeName 獲取 Node 對象,傳入到 provisioner 中。failure-domain.beta.kubernetes.io/zone
),以後在對應 zone 建立 pv,從而達到和 pod 相同可用區的效果,避免雲盤和 node 在不一樣可用區而沒法掛載。TKE 支持在線擴容 PV,對應的雲盤及文件系統,即不須要重啓 pod 便可完成擴容。但,爲了確保文件系統的穩定性,仍是推薦先讓雲盤文件系統處於未 mount 狀況下。爲此,咱們將提供兩種擴容方式:
不重啓 pod 的狀況下在線擴容
重啓 pod 的狀況下在線擴容
在 storageclass 中設置allowVolumeExpansion
爲true
:
allowVolumeExpansion: true apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: cbs-csi-expand parameters: diskType: CLOUD_PREMIUM provisioner: com.tencent.cloud.csi.cbs reclaimPolicy: Delete volumeBindingMode: Immediate
一、確認擴容前 pv 和文件系統狀態,大小均爲 20G
$ kubectl exec ivantestweb-0 df /usr/share/nginx/html Filesystem 1K-blocks Used Available Use% Mounted on /dev/vdd 20511312 45036 20449892 1% /usr/share/nginx/html $ kubectl get pv pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c 20Gi RWO Delete Bound default/www1-ivantestweb-0 cbs-csi 20h
二、執行如下命令修改 PVC 對象中的容量,擴容至 30G
$ kubectl patch pvc www1-ivantestweb-0 -p '{"spec":{"resources":{"requests":{"storage":"30Gi"}}}}'
執行後稍等片刻,能夠發現 pv 和文件系統已經擴容至 30G:
$ kubectl exec ivantestweb-0 df /usr/share/nginx/html Filesystem 1K-blocks Used Available Use% Mounted on /dev/vdd 30832548 44992 30771172 1% /usr/share/nginx/html $ kubectl get pv pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c 30Gi RWO Delete Bound default/www1-ivantestweb-0 cbs-csi 20h
一、確認擴容前 pv 和文件系統狀態,大小均爲 30G
$ kubectl exec ivantestweb-0 df /usr/share/nginx/html Filesystem 1K-blocks Used Available Use% Mounted on /dev/vdd 30832548 44992 30771172 1% /usr/share/nginx/html $ kubectl get pv pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c 30Gi RWO Delete Bound default/www1-ivantestweb-0 cbs-csi 20h
二、使用下面命令給 PV 對象打標籤,打一個非法 zone,旨在下一步重啓 pod 後 pod 沒法調度到某個節點上
$ kubectl label pv pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c failure-domain.beta.kubernetes.io/zone=nozone
三、重啓 pod。重啓後因爲 pod 對應的 pv 的標籤代表的是非法 zone,pod 會處於 Pending 狀態
$ kubectl delete pod ivantestweb-0 $ kubectl get pod ivantestweb-0 NAME READY STATUS RESTARTS AGE ivantestweb-0 0/1 Pending 0 25s $ kubectl describe pod ivantestweb-0 Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 40s (x3 over 2m3s) default-scheduler 0/1 nodes are available: 1 node(s) had no available volume zone.
四、修改 PVC 對象中的容量,擴容至 40G
kubectl patch pvc www1-ivantestweb-0 -p '{"spec":{"resources":{"requests":{"storage":"40Gi"}}}}'
五、去掉 PV 對象以前打的標籤,這樣 pod 就能調度成功了。
$ kubectl label pv pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c failure-domain.beta.kubernetes.io/zone-persistentvolume/pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c labeled
稍等片刻,pod running,對應的 pv 和文件系統也擴容成功,從 30G 擴容到 40G 了
$ kubectl get pod ivantestweb-0 NAME READY STATUS RESTARTS AGE ivantestweb-0 1/1 Running 0 17m $ kubectl get pv pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c 40Gi RWO Delete Bound default/www1-ivantestweb-0 cbs-csi 20h $ kubectl get pvc www1-ivantestweb-0 NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE www1-ivantestweb-0 Bound pvc-e193201e-6f6d-48cf-b96d-ccc09225cf9c 40Gi RWO cbs-csi 20h $ kubectl exec ivantestweb-0 df /usr/share/nginx/html Filesystem 1K-blocks Used Available Use% Mounted on /dev/vdd 41153760 49032 41088344 1% /usr/share/nginx/html
一、使用下面 yaml,建立VolumeSnapshotClass
對象
apiVersion: snapshot.storage.k8s.io/v1beta1 kind: VolumeSnapshotClass metadata: name: cbs-snapclass driver: com.tencent.cloud.csi.cbs deletionPolicy: Delete
建立後顯示:
$ kubectl get volumesnapshotclass NAME DRIVER DELETIONPOLICY AGE cbs-snapclass com.tencent.cloud.csi.cbs Delete 17m
二、使用下面 yaml,建立
apiVersion: snapshot.storage.k8s.io/v1beta1 kind: VolumeSnapshot metadata: name: new-snapshot-demo spec: volumeSnapshotClassName: cbs-snapclass source: persistentVolumeClaimName: csi-pvc
建立後稍等片刻,volumesnapshot 和 volumesnapshotcontent 對象都建立成功,READYTOUSE
爲 true:
$ kubectl get volumesnapshot NAME READYTOUSE SOURCEPVC SOURCESNAPSHOTCONTENT RESTORESIZE SNAPSHOTCLASS SNAPSHOTCONTENT CREATIONTIME AGE new-snapshot-demo true www1-ivantestweb-0 10Gi cbs-snapclass snapcontent-ea11a797-d438-4410-ae21-41d9147fe610 22m 22m $ kubectl get volumesnapshotcontent NAME READYTOUSE RESTORESIZE DELETIONPOLICY DRIVER VOLUMESNAPSHOTCLASS VOLUMESNAPSHOT AGE snapcontent-ea11a797-d438-4410-ae21-41d9147fe610 true 10737418240 Delete com.tencent.cloud.csi.cbs cbs-snapclass new-snapshot-demo 22m
具體快照 id 在 volumesnapshotcontent 對象中,status.snapshotHandle
(snap-e406fc9m),能夠根據這個快照 id 在騰訊雲控制檯確認快照是否存在
$ kubectl get volumesnapshotcontent snapcontent-ea11a797-d438-4410-ae21-41d9147fe610 -oyaml apiVersion: snapshot.storage.k8s.io/v1beta1 kind: VolumeSnapshotContent metadata: creationTimestamp: "2020-11-04T08:58:39Z" finalizers: - snapshot.storage.kubernetes.io/volumesnapshotcontent-bound-protection name: snapcontent-ea11a797-d438-4410-ae21-41d9147fe610 resourceVersion: "471437790" selfLink: /apis/snapshot.storage.k8s.io/v1beta1/volumesnapshotcontents/snapcontent-ea11a797-d438-4410-ae21-41d9147fe610 uid: 70d0390b-79b8-4276-aa79-a32e3bdef3d6 spec: deletionPolicy: Delete driver: com.tencent.cloud.csi.cbs source: volumeHandle: disk-7z32tin5 volumeSnapshotClassName: cbs-snapclass volumeSnapshotRef: apiVersion: snapshot.storage.k8s.io/v1beta1 kind: VolumeSnapshot name: new-snapshot-demo namespace: default resourceVersion: "471418661" uid: ea11a797-d438-4410-ae21-41d9147fe610 status: creationTime: 1604480319000000000 readyToUse: true restoreSize: 10737418240 snapshotHandle: snap-e406fc9m
一、咱們在 3.2.1 中建立的VolumeSnapshot
的對象名爲new-snapshot-demo
,使用下面 yaml 來從快照恢復一個卷
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: restore-test spec: storageClassName: cbs-csi dataSource: name: new-snapshot-demo kind: VolumeSnapshot apiGroup: snapshot.storage.k8s.io accessModes: - ReadWriteOnce resources: requests: storage: 10Gi
發現 restore 的 pvc 已經建立出來,diskid 也在 pv 中(disk-gahz1kw1)
$ kubectl get pvc restore-test NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE restore-test Bound pvc-80b98084-29a3-4a38-a96c-2f284042cf4f 10Gi RWO cbs-csi 97s $ kubectl get pv pvc-80b98084-29a3-4a38-a96c-2f284042cf4f -oyaml apiVersion: v1 kind: PersistentVolume metadata: annotations: pv.kubernetes.io/provisioned-by: com.tencent.cloud.csi.cbs creationTimestamp: "2020-11-04T12:08:25Z" finalizers: - kubernetes.io/pv-protection name: pvc-80b98084-29a3-4a38-a96c-2f284042cf4f resourceVersion: "474676883" selfLink: /api/v1/persistentvolumes/pvc-80b98084-29a3-4a38-a96c-2f284042cf4f uid: 5321df93-5f21-4895-bafc-71538d50293a spec: accessModes: - ReadWriteOnce capacity: storage: 10Gi claimRef: apiVersion: v1 kind: PersistentVolumeClaim name: restore-test namespace: default resourceVersion: "474675088" uid: 80b98084-29a3-4a38-a96c-2f284042cf4f csi: driver: com.tencent.cloud.csi.cbs fsType: ext4 volumeAttributes: diskType: CLOUD_PREMIUM storage.kubernetes.io/csiProvisionerIdentity: 1604478835151-8081-com.tencent.cloud.csi.cbs volumeHandle: disk-gahz1kw1 nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: topology.com.tencent.cloud.csi.cbs/zone operator: In values: - ap-beijing-2 persistentVolumeReclaimPolicy: Delete storageClassName: cbs-csi volumeMode: Filesystem status: phase: Bound
【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公衆號,及時獲取更多幹貨!!