- 一、背景
- 二、CSI 是什麼
- 三、CSI 系統架構
- 1、CSI 如何與 k8s 元件相互通訊
- 2、CSI 由哪些元件組成
- 3、CSI 的工作原理
- 4、k8s 儲存中涉及的元件及其作用
- 4.1、Sidecar Containers
- 4.1.1、external-attacher
- 4.1.2、external-provisioner
- 4.1.3、external-resizer
- 4.1.4、external-snapshotter
- 4.1.5、livenessprobe
- 4.1.6、node-driver-registrar
- 4.1.7、cluster-driver-registrar(已棄用)
- 4.1.8、external-health-monitor-controller
- 4.1.9、external-health-monitor-agent(已棄用)
- 4.2、PV Controller
- 4.3、AD Controller
- 4.4、Volume Manager
- 4.1、Sidecar Containers
- 四、CSI 儲存流程
- 1、Provisioning Volumes
- 2、Attaching Volumes
- 3、Mounting Volumes
- 4、Unmounting Volumes
- 5、Detaching Volumes
- 6、Deleting Volumes
一、背景
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 是什麼
CSI是Container Storage Interface
(容器儲存介面)的簡寫。
CSI的目的是定義行業標準"容器儲存介面",使儲存供應商(SP,Storage Provider)能夠開發一個符合CSI標準的外掛並使其可以在多個容器編排(CO)系統中工作。CO包括Cloud Foundry, Kubernetes, Mesos,Swarm等。
kubernetes將透過CSI介面來跟第三方儲存廠商進行通訊,來操作儲存,從而提供容器儲存服務。
三、CSI 系統架構
CSI 主要包含兩個部分:
-
Controller Server:主要控制端的功能,主要實現建立、刪除、掛載、解除安裝等功能。通常以 StatefulSet/Deployment 形式部署
-
Node Server:主要實現節點上的 mount , unmount功能。通常以DaemonSet 形式部署在每一個節點
1、CSI 如何與 k8s 元件相互通訊
CSI Controller Server 和 External CSI SideCar 之間以及 CSI Node Server 和 Kubelet 之間其實都是透過建立 Unix Domain Socket 檔案使用 gRPC 協議進行互相通訊的
2、CSI 由哪些元件組成
- Node Service: 執行在每個 Kubernetes 節點上,負責在節點上掛載和解除安裝儲存卷,並處理節點級別的儲存操作。
- Controller Service: 執行在 Kubernetes 控制平面中,負責管理儲存卷的生命週期,包括建立、刪除和擴容等操作。
- Identity Service:它在 CSI 驅動器註冊時提供標識資訊,並向 Kubernetes 叢集公開驅動器的支援能力。它負責告知 Kubernetes 驅動器的存在,提供驅動器的基本資訊和功能支援。
3、CSI 的工作原理
CSI 的工作流程實際上就是圍繞著兩方面的元件:
- 由 Kubernetes 官方維護的一組標準 external 元件,他們主要負責監聽 K8s 裡的資源物件,從而向 CSI Driver 發起 gRPC 呼叫。 Kubernetes CSI Sidecar Containers。它們是與 CSI 驅動器一起部署在同一個 Pod 中,用於輔助 CSI Driver 完成一些額外的任務和功能。
- 各儲存廠商開發的元件(主要實現 Identity Service,Controller Service,Node Service)
4、k8s 儲存中涉及的元件及其作用
4.1、Sidecar Containers
Sidecar Contrainers
Kubernetes CSI Sidecar 容器
是一組標準容器,旨在簡化 Kubernetes 上 CSI 驅動程式的開發和部署。
這些容器包含監視 Kubernetes API
、針對 "CSI 卷驅動程式" 容器觸發適當操作以及根據需要更新 Kubernetes API 的通用邏輯。
這些容器旨在與第三方 CSI 驅動容器捆綁在一起,並作為 pod 一起部署。
這些 Sidecar 容器的優點:
-
減少 "樣板" 程式碼
- CSI 驅動程式開發人員不必擔心複雜的"Kubernetes 特定"程式碼。
-
關注點分離
- 與 Kubernetes API 互動的程式碼與實現 CSI 介面的程式碼是隔離的(並且位於不同的容器中)。
4.1.1、external-attacher
它監視 Kubernetes API 伺服器
上的 VolumeAttachment
物件並Controller[Publish|Unpublish]Volume
針對 CSI 端點觸發操作。
4.1.2、external-provisioner
CSI external-provisioner
是一個 sidecar
容器,用於監視 Kubernetes API 伺服器中的 PersistentVolumeClaim
物件。
它針對指定的 CSI 端點呼叫 CreateVolume
以配置新卷。
如果 PVC 引用 Kubernetes StorageClass
,並且儲存類的配置者欄位中的名稱與 GetPluginInfo
呼叫中指定的 CSI 端點返回的名稱匹配,則透過建立新的 Kubernetes PersistentVolumeClaim
物件來觸發卷配置。
成功配置新卷後,sidecar 容器會建立一個 Kubernetes PersistentVolume
物件來表示該卷。
使用刪除回收策略刪除繫結到與此驅動程式對應的 PersistentVolume
的 PersistentVolumeClaim
物件,會導致 sidecar 容器針對指定的 CSI 端點觸發 DeleteVolume
操作以刪除該卷。一旦卷被成功刪除,sidecar 容器也會刪除代表該卷的 PersistentVolume
物件。
4.1.3、external-resizer
CSI external-resizer
是一個 sidecar 容器,它監視 Kubernetes API 伺服器
上的 PersistentVolumeClaim
物件編輯,並在使用者請求 PersistentVolumeClaim
物件上的更多儲存時觸發針對 CSI 端點的 ControllerExpandVolume
操作。
4.1.4、external-snapshotter
CSI external-snapshotter sidecar
監視 Kubernetes API 伺服器中的 VolumeSnapshotContent
CRD 物件。CSI external-snapshotter sidecar
還負責呼叫 CSI RPCs CreateSnapshot
、DeleteSnapshot
和 ListSnapshots
。
可以使用 --enable-volume-group-snapshots
選項啟用卷組快照支援。啟用後,CSI 外部快照 sidecar 會監視 API 伺服器上的 VolumeGroupSnapshotContent
CRD 物件,並負責呼叫 CSI RPCs CreateVolumeGroupSnapshot
、DeleteVolumeGroupSnapshot
和 GetVolumeGroupSnapshot
。
4.1.5、livenessprobe
CSI livenessprobe
是一個 sidecar 容器,用於監控 CSI 驅動程式的執行狀況,並透過 Liveness Probe
機制向 Kubernetes 報告。這使得 Kubernetes 能夠自動檢測驅動程式的問題並重新啟動 pod 以嘗試修復問題。
所有 CSI 驅動程式都應使用活性探針來提高部署在 Kubernetes 上的驅動程式的可用性。
4.1.6、node-driver-registrar
CSI node-driver-registrar
是一個 sidecar 容器,它從 CSI 端點獲取驅動程式資訊(使用 NodeGetInfo
),並使用 kubelet plugin registration mechanism (kubelet 外掛序號產生器制)將其註冊到該節點上的 kubelet。
Kubelet
直接針對 CSI driver
發出 CSI NodeGetInfo
、NodeStageVolume
和 NodePublishVolume
呼叫。它使用 kubelet
外掛序號產生器制來發現 unix 域套接字以與 CSI 驅動程式通訊。因此,所有 CSI 驅動程式都應該使用這個 sidecar 容器向 kubelet
註冊自己。
4.1.7、cluster-driver-registrar(已棄用)
自 k8s 1.13以來,此 Sidecar Container 未更新。從 k8s 1.16開始,此 Sidecar Container 已正式棄用
CSI cluster-driver-registrar
是一個 sidecar 容器,它透過建立 CSIDriver
物件向 Kubernetes 叢集註冊 CSI 驅動程式,該物件使驅動程式能夠自定義 Kubernetes 與其互動的方式。
使用以下 Kubernetes 功能之一的 CSI 驅動程式應使用此 sidecar 容器:
-
Skip Attach
- 對於不支援
ControllerPublishVolume
的驅動程式,這指示 Kubernetes 跳過附加操作,並且無需部署外部附加器 sidecar。
- 對於不支援
-
Pod Info on Mount
- 這會導致 Kubernetes 將 Pod 名稱和名稱空間等後設資料傳遞給
NodePublishVolume
呼叫。
- 這會導致 Kubernetes 將 Pod 名稱和名稱空間等後設資料傳遞給
如果不使用這些功能之一,則不需要此 sidecar 容器(以及 CSIDriver
物件的建立)。
4.1.8、external-health-monitor-controller
CSI external-health-monitor-controller
是一個 sidecar 容器,與 CSI controller driver
一起部署,類似於 CSI external-provisioner sidecar
的部署方式。它呼叫 CSI 控制器 RPC ListVolumes
或 ControllerGetVolume
來檢查 CSI 卷的執行狀況,如果卷的狀況異常,則在 PersistentVolumeClaim
上報告事件。
CSI 外部執行狀況監視器控制器還監視節點故障事件。可以透過將 enable-node-watcher
標誌設定為true來啟用該元件。目前這隻會對local PVs產生影響。當檢測到節點故障事件時,將在 PVC 上報告一個事件,以指示使用此 PVC 的 pod 位於故障節點上。
支援 VOLUME_CONDITION 和 LIST_VOLUMES
或 VOLUME_CONDITION
和 GET_VOLUME
控制器功能的 CSI 驅動程式應使用此 sidecar 容器。
4.1.9、external-health-monitor-agent(已棄用)
此 sidecar 已被棄用,並替換為 Kubernetes 中的
CSIVolumeHealth
功能。
CSI external-health-monitor-agent
是一個 sidecar 容器,與 CSI 節點驅動程式一起部署,類似於 CSI node-driver-registrar sidecar
的部署方式。它呼叫 CSI 節點 RPC NodeGetVolumeStats
檢查 CSI 卷的健康狀況,如果卷狀況異常,則報告 Pod 上的事件。
支援 VOLUME_CONDITION
和 NODE_GET_VOLUME_STATS
節點功能的 CSI 驅動程式應使用此 sidecar 容器。
4.2、PV Controller
是 kube-controller-manager
內部元件,是 k8s 控制面的一部分,負責 PV/PVC 繫結及週期管理,根據需求進行資料卷的 Provision/Delete 操作
-
in-tree:建立/刪除底層儲存、建立/刪除pv物件的操作,由
PV controller
呼叫volume plugin(in-tree)
來完成。 -
out-tree CSI:建立/刪除底層儲存、建立/刪除pv物件的操作由
external-provisioner
與csi plugin
共同來完成。
4.3、AD Controller
AD Cotroller
全稱 Attachment/Detachment 控制器
,也是 kube-controller-manager
內部一個元件。主要負責建立、刪除VolumeAttachment
物件,並呼叫 volume plugin
來做儲存裝置的 Attach/Detach
操作(將資料卷掛載到特定node節點上/從特定node節點上解除掛載),以及更新 node.Status.VolumesAttached
等。
不同的 volume plugin
的 Attach/Detach
操作邏輯有所不同,對於 csi plugin(out-tree volume plugin)
來說,AD controller
的 Attach/Detach
操作只是修改 VolumeAttachment
物件的狀態,而不會真正的將資料卷掛載到節點/從節點上解除掛載,真正的節點儲存掛載/解除掛載操作由kubelet中 volume manager
呼叫 csi plugin
來完成。
4.4、Volume Manager
是 kubelet
內部一個元件,它主要是用來做節點 Volume 的 Attach/Detach/Mount/Unmount
操作。
對於 csi
來說,volume manager
的 Attach/Detach
操作只建立/刪除 VolumeAttachment
物件,而不會真正的將資料卷掛載到節點/從節點上解除掛載;
CSI-Attacher
元件也不會做掛載/解除掛載操作,只是更新 VolumeAttachment
物件狀態,真正的節點儲存掛載/解除掛載操作由kubelet
中 volume manager
呼叫呼叫 csi plugin
來完成。
四、CSI 儲存流程
K8s 中的 Pod 在掛載儲存卷時需經歷三個的階段:Provision/Delete(創盤/刪盤)、Attach/Detach(掛接/摘除)和 Mount/Unmount(掛載/解除安裝),下面以圖文的方式講解 K8s 在這三個階段使用 CSI 的流程。
1、Provisioning Volumes
1、叢集管理員建立 StorageClass
資源,該 StorageClass
中包含 CSI 外掛名稱(provisioner: rancher.io/local-path )
2、使用者建立 PersistentVolumeClaim 資源,PVC 指定儲存大小及 StorageClass。
cat << EOF > pvc-test.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-test
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
storageClassName: local-path
EOF
3、卷控制器(PersistentVolumeController)觀察到叢集中新建立的 PVC 沒有與之匹配的 PV,且其使用的儲存型別為 out-of-tree,於是為 PVC 打 annotation:volume.beta.kubernetes.io/storage-provisioner=[out-of-tree CSI 外掛名稱](本例中為provisioner: rancher.io/local-path)。
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
-
AD 控制器(AttachDetachController)觀察到使用 CSI 型別 PV 的 Pod 被排程到某一節點,此時 AD 控制器會呼叫內部 in-tree CSI 外掛(csiAttacher)的 Attach 函式。
-
內部 in-tree CSI 外掛(csiAttacher)會建立一個 VolumeAttachment 物件到叢集中。
-
External Attacher 觀察到該 VolumeAttachment 物件,並呼叫外部 CSI 外掛的 ControllerPublish 函式以將卷掛接到對應節點上。外部 CSI 外掛掛載成功後,External Attacher 會更新相關 VolumeAttachment 物件的 .Status.Attached 為 true。
-
AD 控制器內部 in-tree CSI 外掛(csiAttacher)觀察到 VolumeAttachment 物件的 .Status.Attached 設定為 true,於是更新 AD 控制器內部狀態(ActualStateOfWorld),該狀態會顯示在 Node 資源的 .Status.VolumesAttached 上。
3、Mounting Volumes
-
Volume Manager(Kubelet 元件)觀察到有新的使用 CSI 型別 PV 的 Pod 排程到本節點上,於是呼叫內部 in-tree CSI 外掛(csiAttacher)的 WaitForAttach 函式。
-
內部 in-tree CSI 外掛(csiAttacher)等待叢集中 VolumeAttachment 物件狀態 .Status.Attached 變為 true。
-
in-tree CSI 外掛(csiAttacher)呼叫 MountDevice 函式,該函式內部透過 unix domain socket 呼叫外部 CSI 外掛的 NodeStageVolume 函式;之後外掛(csiAttacher)呼叫內部 in-tree CSI 外掛(csiMountMgr)的 SetUp 函式,該函式內部會透過 unix domain socket 呼叫外部 CSI 外掛的 NodePublishVolume 函式。
4、Unmounting Volumes
-
使用者刪除相關 Pod。
-
Volume Manager(Kubelet 元件)觀察到包含 CSI 儲存卷的 Pod 被刪除,於是呼叫內部 in-tree CSI 外掛(csiMountMgr)的 TearDown 函式,該函式內部會透過 unix domain socket 呼叫外部 CSI 外掛的 NodeUnpublishVolume 函式。
-
Volume Manager(Kubelet 元件)呼叫內部 in-tree CSI 外掛(csiAttacher)的 UnmountDevice 函式,該函式內部會透過 unix domain socket 呼叫外部 CSI 外掛的 NodeUnpublishVolume 函式。
5、Detaching Volumes
-
AD 控制器觀察到包含 CSI 儲存卷的 Pod 被刪除,此時該控制器會呼叫內部 in-tree CSI 外掛(csiAttacher)的 Detach 函式。
-
csiAttacher 會刪除叢集中相關 VolumeAttachment 物件(但由於存在 finalizer,va 物件不會立即刪除)。
-
External Attacher 觀察到叢集中 VolumeAttachment 物件的 DeletionTimestamp 非空,於是呼叫外部 CSI 外掛的 ControllerUnpublish 函式以將卷從對應節點上摘除。外部 CSI 外掛摘除成功後,External Attacher 會移除相關 VolumeAttachment 物件的 finalizer 欄位,此時 VolumeAttachment 物件被徹底刪除。
-
AD 控制器中內部 in-tree CSI 外掛(csiAttacher)觀察到 VolumeAttachment 物件已刪除,於是更新 AD 控制器中的內部狀態;同時 AD 控制器更新 Node 資源,此時 Node 資源的 .Status.VolumesAttached 上已沒有相關掛接資訊。
6、Deleting Volumes
-
使用者刪除相關 PVC。
-
External Provisioner 元件觀察到 PVC 刪除事件,根據 PVC 的回收策略(Reclaim)執行不同操作:
-
Delete:呼叫外部 CSI 外掛的 DeleteVolume 函式以刪除卷;一旦捲成功刪除,Provisioner 會刪除叢集中對應 PV 物件。
-
Retain:Provisioner 不執行卷刪除操作。
-