1. 程式人生 > 其它 >【K8s概念】理解容器儲存介面 CSI

【K8s概念】理解容器儲存介面 CSI

參考:https://www.kubernetes.org.cn/9127.html

背景

K8s 原生支援一些儲存型別的 PV,如 iSCSI、NFS、CephFS 等等,這些 in-tree 型別的儲存程式碼放在 Kubernetes 程式碼倉庫中。這裡帶來的問題是 K8s 程式碼與三方儲存廠商的程式碼強耦合:

  • 更改 in-tree 型別的儲存程式碼,使用者必須更新 K8s 元件,成本較高
  • in-tree 儲存程式碼中的 bug 會引發 K8s 元件不穩定
  • K8s 社群需要負責維護及測試 in-tree 型別的儲存功能
  • in-tree 儲存外掛享有與 K8s 核心元件同等的特權,存在安全隱患
  • 三方儲存開發者必須遵循 K8s 社群的規則開發 in-tree 型別儲存程式碼

CSI 容器儲存介面標準的出現解決了上述問題,將三方儲存程式碼與 K8s 程式碼解耦,使得三方儲存廠商研發人員只需實現 CSI 介面(無需關注容器平臺是 K8s 還是 Swarm 等)。

CSI 核心流程介紹

K8s 中的 Pod 在掛載儲存卷時需經歷三個的階段:

  • Provision/Delete(創盤/刪盤)
  • Attach/Detach(掛接/摘除)
  • Mount/Unmount(掛載/解除安裝),

下面以圖文的方式講解 K8s 在這三個階段使用 CSI 的流程。

1.Provisioning Volumes

1.叢集管理員建立 StorageClass 資源,該 StorageClass 中包含 CSI 外掛名稱。

2.使用者建立 PersistentVolumeClaim 資源,PVC 指定儲存大小及 StorageClass。

3.卷控制器(PersistentVolumeController)觀察到叢集中新建立的 PVC 沒有與之匹配的 PV,且其使用的儲存型別為 out-of-tree,於是為 PVC 打 annotation:volume.beta.kubernetes.io/storage-provisioner=[out-of-tree CSI 外掛名稱]

4.External Provisioner 元件觀察到 PVC 的 annotation 中包含 “volume.beta.kubernetes.io/storage-provisioner” 且其 value 是自己,於是開始創盤流程。
    獲取相關 StorageClass 資源並從中獲取引數,用於後面 CSI 函式呼叫。
    通過 unix domain socket 呼叫外部 CSI 外掛的CreateVolume 函式。

5.外部 CSI 外掛返回成功後表示盤建立完成,此時External Provisioner 元件會在叢集建立一個 PersistentVolume 資源。

6.卷控制器會將 PV 與 PVC 進行繫結。

2.Attaching Volumes(Node)

1.AD 控制器(AttachDetachController)觀察到使用 CSI 型別 PV 的 Pod 被排程到某一節點,此時AD 控制器會呼叫內部 in-tree CSI 外掛(csiAttacher)的 Attach 函式。

2.內部 in-tree CSI 外掛(csiAttacher)會建立一個 VolumeAttachment 物件到叢集中。

3.External Attacher 觀察到該 VolumeAttachment 物件,並呼叫外部 CSI外掛的ControllerPublish 函式以將卷掛接到對應節點上。外部 CSI 外掛掛載成功後,External Attacher會更新相關 VolumeAttachment 物件的 .Status.Attached 為 true。

4.AD 控制器內部 in-tree CSI 外掛(csiAttacher)觀察到 VolumeAttachment 物件的 .Status.Attached 設定為 true,於是更新AD 控制器內部狀態(ActualStateOfWorld),該狀態會顯示在 Node 資源的 .Status.VolumesAttached 上。

3.Mounting Volumes(Kubelet)

1.Volume Manager(Kubelet 元件)觀察到有新的使用 CSI 型別 PV 的 Pod 排程到本節點上,於是呼叫內部 in-tree CSI 外掛(csiAttacher)的 WaitForAttach 函式。

2.內部 in-tree CSI 外掛(csiAttacher)等待叢集中 VolumeAttachment 物件狀態 .Status.Attached 變為 true。

3.in-tree CSI 外掛(csiAttacher)呼叫 MountDevice 函式,該函式內部通過 unix domain socket 呼叫外部 CSI 外掛的NodeStageVolume 函式;之後外掛(csiAttacher)呼叫內部 in-tree CSI 外掛(csiMountMgr)的 SetUp 函式,該函式內部會通過 unix domain socket 呼叫外部 CSI 外掛的NodePublishVolume 函式。

4.Unmounting Volumes(Kubelet)

1.使用者刪除相關 Pod。

2.Volume Manager(Kubelet 元件)觀察到包含 CSI 儲存卷的 Pod 被刪除,於是呼叫內部 in-tree CSI 外掛(csiMountMgr)的 TearDown 函式,該函式內部會通過 unix domain socket 呼叫外部 CSI 外掛的 NodeUnpublishVolume 函式。

3.Volume Manager(Kubelet 元件)呼叫內部 in-tree CSI 外掛(csiAttacher)的 UnmountDevice 函式,該函式內部會通過 unix domain socket 呼叫外部 CSI 外掛的 NodeUnpublishVolume 函式。

5.Detaching Volumes(Node)

1.AD 控制器觀察到包含 CSI 儲存卷的 Pod 被刪除,此時該控制器會呼叫內部 in-tree CSI 外掛(csiAttacher)的 Detach 函式。

2.csiAttacher會刪除叢集中相關 VolumeAttachment 物件(但由於存在 finalizer,va 物件不會立即刪除)。

3.External Attacher觀察到叢集中 VolumeAttachment 物件的 DeletionTimestamp 非空,於是呼叫外部 CSI 外掛的ControllerUnpublish 函式以將卷從對應節點上摘除。外部 CSI 外掛摘除成功後,External Attacher會移除相關 VolumeAttachment 物件的 finalizer 欄位,此時 VolumeAttachment 物件被徹底刪除。

4.AD 控制器中內部 in-tree CSI 外掛(csiAttacher)觀察到 VolumeAttachment 物件已刪除,於是更新AD 控制器中的內部狀態;同時AD 控制器更新 Node 資源,此時 Node 資源的 .Status.VolumesAttached 上已沒有相關掛接資訊。

6.Deleting Volumes

1.使用者刪除相關 PVC。

2.External Provisioner 元件觀察到 PVC 刪除事件,根據 PVC 的回收策略(Reclaim)執行不同操作:
    Delete:呼叫外部 CSI 外掛的DeleteVolume 函式以刪除卷;一旦捲成功刪除,Provisioner會刪除叢集中對應 PV 物件。
    Retain:Provisioner不執行卷刪除操作。

CSI Sidecar 元件介紹

為使 K8s 適配 CSI 標準,社群將與 K8s 相關的儲存流程邏輯放在了 CSI Sidecar 元件中。

1. Node Driver Registrar

1)功能

Node-Driver-Registrar 元件會將外部 CSI 外掛註冊到Kubelet,從而使Kubelet通過特定的 Unix Domain Socket 來呼叫外部 CSI 外掛函式(Kubelet 會呼叫外部 CSI 外掛的 NodeGetInfo、NodeStageVolume、NodePublishVolume、NodeGetVolumeStats 等函式)。

2)原理

Node-Driver-Registrar 元件通過Kubelet 外部外掛註冊機制實現註冊,註冊成功後:

    Kubelet為本節點 Node 資源打 annotation:Kubelet呼叫外部 CSI 外掛的NodeGetInfo 函式,其返回值 [nodeID]、[driverName] 將作為值用於 “csi.volume.kubernetes.io/nodeid” 鍵。
    Kubelet更新 Node Label:將NodeGetInfo 函式返回的 [AccessibleTopology] 值用於節點的 Label。
    Kubelet更新 Node Status:將NodeGetInfo 函式返回的 maxAttachLimit(節點最大可掛載卷數量)更新到 Node 資源的 Status.Allocatable:attachable-volumes-csi-[driverName]=[maxAttachLimit]。
    Kubelet更新 CSINode 資源(沒有則建立):將 [driverName]、[nodeID]、[maxAttachLimit]、[AccessibleTopology] 更新到 Spec 中(拓撲僅保留 Key 值)。

2. External Provisioner

1)功能

建立/刪除實際的儲存卷,以及代表儲存卷的 PV 資源。

2)原理

External-Provisioner在啟動時需指定引數 — provisioner,該引數指定 Provisioner 名稱,與 StorageClass 中的 provisioner 欄位對應。

External-Provisioner啟動後會 watch 叢集中的 PVC 和 PV 資源。

對於叢集中的 PVC 資源:

    判斷 PVC 是否需要動態建立儲存卷,標準如下:
        PVC 的 annotation 中是否包含 “volume.beta.kubernetes.io/storage-provisioner” 鍵(由卷控制器建立),並且其值是否與 Provisioner 名稱相等。
        PVC 對應 StorageClass 的 VolumeBindingMode 欄位若為 WaitForFirstConsumer,則 PVC 的 annotation 中必須包含 “volume.kubernetes.io/selected-node” 鍵(詳見排程器如何處理 WaitForFirstConsumer),且其值不為空;若為 Immediate 則表示需要 Provisioner 立即提供動態儲存卷。
    通過特定的 Unix Domain Socket 呼叫外部 CSI 外掛的 CreateVolume 函式。
    建立 PV 資源,PV 名稱為 [Provisioner 指定的 PV 字首] – [PVC uuid]。

對於叢集中的 PV 資源:

    判斷 PV 是否需要刪除,標準如下:
        判斷其 .Status.Phase 是否為 Release。
        判斷其 .Spec.PersistentVolumeReclaimPolicy 是否為 Delete。
        判斷其是否包含 annotation(pv.kubernetes.io/provisioned-by),且其值是否為自己。
    通過特定的 Unix Domain Socket 呼叫外部 CSI 外掛的 DeleteVolume 介面。
    刪除叢集中的 PV 資源。

3. External Attacher

1)功能

掛接/摘除儲存卷。

2)原理

External-Attacher 內部會時刻 watch 叢集中的 VolumeAttachment 資源和 PersistentVolume 資源。

對於 VolumeAttachment 資源:

    從 VolumeAttachment 資源中獲得 PV 的所有資訊,如 volume ID、node ID、掛載 Secret 等。
    判斷 VolumeAttachment 的 DeletionTimestamp 欄位是否為空來判斷其為卷掛接或卷摘除:若為卷掛接則通過特定的 Unix Domain Socket 呼叫外部 CSI 外掛的ControllerPublishVolume 介面;若為卷摘除則通過特定的 Unix Domain Socket 呼叫外部 CSI 外掛的ControllerUnpublishVolume 介面。

對於 PersistentVolume 資源:

    在掛接時為相關 PV 打上 Finalizer:external-attacher/[driver 名稱]。
    當 PV 處於刪除狀態時(DeletionTimestamp 非空),刪除 Finalizer:external-attacher/[driver 名稱]。

4. External Resizer

1)功能

擴容儲存卷。

2)原理

External-Resizer內部會 watch 叢集中的 PersistentVolumeClaim 資源。

對於 PersistentVolumeClaim 資源:

    判斷 PersistentVolumeClaim 資源是否需要擴容:PVC 狀態需要是 Bound 且 .Status.Capacity 與 .Spec.Resources.Requests 不等。
    更新 PVC 的 .Status.Conditions,表明此時處於 Resizing 狀態。
    通過特定的 Unix Domain Socket 呼叫外部 CSI 外掛的 ControllerExpandVolume 介面。
    更新 PV 的 .Spec.Capacity。
    若 CSI 支援檔案系統線上擴容,ControllerExpandVolume 介面返回值中 NodeExpansionRequired 欄位為 true,External-Resizer更新 PVC 的 .Status.Conditions 為 FileSystemResizePending 狀態;若不支援則擴容成功,External-Resizer更新 PVC 的 .Status.Conditions 為空,且更新 PVC 的 .Status.Capacity。

Volume Manager(Kubelet 元件)觀察到儲存卷需線上擴容,於是通過特定的 Unix Domain Socket 呼叫外部 CSI 外掛的NodeExpandVolume 介面實現檔案系統擴容。

5. livenessprobe

1)功能

檢查 CSI 外掛是否正常。

2)原理

通過對外暴露一個 / healthz HTTP 埠以服務 kubelet 的探針探測器,內部是通過特定的 Unix Domain Socket 呼叫外部 CSI 外掛的 Probe 介面。

CSI 介面介紹

三方儲存廠商需實現 CSI 外掛的三大介面:

  • IdentityServer
  • ControllerServer
  • NodeServer。

1. IdentityServer

IdentityServer 主要用於認證 CSI 外掛的身份資訊。

2. ControllerServer

ControllerServer 主要負責儲存卷及快照的建立/刪除以及掛接/摘除操作。

3. NodeServer

NodeServer 主要負責儲存卷掛載/解除安裝操作。

K8s CSI API 物件

K8s 為支援 CSI 標準,包含如下 API 物件:

  • CSINode
  • CSIDriver
  • VolumeAttachment

1. CSINode

apiVersion: storage.k8s.io/v1beta1
kind: CSINode
metadata:
  name: node-10.212.101.210
spec:
  drivers:
  - name: yodaplugin.csi.alibabacloud.com
    nodeID: node-10.212.101.210
    topologyKeys:
    - kubernetes.io/hostname
  - name: pangu.csi.alibabacloud.com
    nodeID: a5441fd9013042ee8104a674e4a9666a
    topologyKeys:
    - topology.pangu.csi.alibabacloud.com/zone

作用:

    判斷外部 CSI 外掛是否註冊成功。在 Node Driver Registrar 元件向 Kubelet 註冊完畢後,Kubelet 會建立該資源,故不需要顯式建立 CSINode 資源。
    將 Kubernetes 中 Node 資源名稱與三方儲存系統中節點名稱(nodeID)一一對應。此處Kubelet會呼叫外部 CSI 外掛NodeServer 的 GetNodeInfo 函式獲取 nodeID。
    顯示卷拓撲資訊。CSINode 中 topologyKeys 用來表示儲存節點的拓撲資訊,卷拓撲資訊會使得Scheduler在 Pod 排程時選擇合適的儲存節點。

2. CSIDriver

apiVersion: storage.k8s.io/v1beta1
kind: CSIDriver
metadata:
  name: pangu.csi.alibabacloud.com
spec:
    # 外掛是否支援卷掛接(VolumeAttach)
  attachRequired: true
  # Mount階段是否CSI外掛需要Pod資訊
  podInfoOnMount: true
  # 指定CSI支援的卷模式
  volumeLifecycleModes:
  - Persistent

作用:

    簡化外部 CSI 外掛的發現。由叢集管理員建立,通過 kubectl get csidriver 即可得知環境上有哪些 CSI 外掛。
    自定 義Kubernetes 行為,如一些外部 CSI 外掛不需要執行卷掛接(VolumeAttach)操作,則可以設定 .spec.attachRequired 為 false。

3. VolumeAttachment

apiVersion: storage.k8s.io/v1
kind: VolumeAttachment
metadata:
  annotations:
    csi.alpha.kubernetes.io/node-id: 21481ae252a2457f9abcb86a3d02ba05
  finalizers:
  - external-attacher/pangu-csi-alibabacloud-com
  name: csi-0996e5e9459e1ccc1b3a7aba07df4ef7301c8e283d99eabc1b69626b119ce750
spec:
  attacher: pangu.csi.alibabacloud.com
  nodeName: node-10.212.101.241
  source:
    persistentVolumeName: pangu-39aa24e7-8877-11eb-b02f-021234350de1
status:
  attached: true

作用:VolumeAttachment 記錄了儲存卷的掛接/摘除資訊以及節點資訊。

支援特性

1. 拓撲支援

2. 儲存卷擴容

3. 單節點卷數量限制

4. 儲存卷監控

儲存商需實現 CSI 外掛的 NodeGetVolumeStats 介面,Kubelet 會呼叫該函式,並反映在其 metrics上:

    kubelet_volume_stats_capacity_bytes:儲存卷容量
    kubelet_volume_stats_used_bytes:儲存卷已使用容量
    kubelet_volume_stats_available_bytes:儲存卷可使用容量
    kubelet_volume_stats_inodes:儲存卷 inode 總量
    kubelet_volume_stats_inodes_used:儲存卷 inode 使用量
    kubelet_volume_stats_inodes_free:儲存卷 inode 剩餘量

5. Secret

CSI 儲存卷支援傳入 Secret 來處理不同流程中所需要的私密資料,目前 StorageClass 支援如下 Parameter:

    csi.storage.k8s.io/provisioner-secret-name
    csi.storage.k8s.io/provisioner-secret-namespace
    csi.storage.k8s.io/controller-publish-secret-name
    csi.storage.k8s.io/controller-publish-secret-namespace
    csi.storage.k8s.io/node-stage-secret-name
    csi.storage.k8s.io/node-stage-secret-namespace
    csi.storage.k8s.io/node-publish-secret-name
    csi.storage.k8s.io/node-publish-secret-namespace
    csi.storage.k8s.io/controller-expand-secret-name
    csi.storage.k8s.io/controller-expand-secret-namespace

Secret 會包含在對應 CSI 介面的引數中,如對於 CreateVolume 介面而言則包含在 CreateVolumeRequest.Secrets 中。

6. 塊裝置

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx-example
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  volumeClaimTemplates:
  - metadata:
      name: html
    spec:
      accessModes:
        - ReadWriteOnce
      volumeMode: Block
      storageClassName: csi-pangu
      resources:
        requests:
          storage: 40Gi
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeDevices:
        - devicePath: "/dev/vdb"
          name: html

三方儲存廠商需實現 NodePublishVolume 介面。Kubernetes 提供了針對塊裝置的工具包(”k8s.io/kubernetes/pkg/util/mount”),在 NodePublishVolume 階段可呼叫該工具的 EnsureBlock 和 MountBlock 函式。

7. 卷快照/卷克隆能力

作者:Varden 出處:http://www.cnblogs.com/varden/ 本文內容如有雷同,請聯絡作者! 本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。