K8s容器存儲接口(CSI)介紹


Container Storage Interface是由來自Kubernetes、Mesos、Docker等社區member聯合制定的一個行業標准接口規范,旨在將任意存儲系統暴露給容器化應用程序。
CSI規范定義了存儲提供商實現CSI兼容的Volume Plugin的最小操作集和部署建議。CSI規范的主要焦點是聲明Volume Plugin必須實現的接口。
 

一、CSI插件需實現的接口

CSI Plugin開發者要實現三類gRPC服務接口:
實現了此接口的CSI插件,不但可以在k8s中使用,在所有參與了CSI標准制訂的Container Orchestration system中都是通用的。
 

二、CSI sidecar容器

為了協助存儲提供商開發CSI兼容插件,Kubernetes官方提供了一些CSI sidecar容器(對應了從Kubernetes項目里面剝離出來的存儲管理功能)
  • external-provisioner(csi-provisioner)

在啟動時通過--provisioner指定自身provisioner名稱,與StorageClass中的provisioner字段對應。
(1)watch PVC對象,判斷PVC是否需要動態創建存儲卷,標准如下:
①PVC的annotation[volume.beta.kubernetes.io/storage-provisioner](由PV Controller創建)是否與自己的provisioner名稱相等。
②PVC對應StorageClass的VolumeBindingMode若為Immediate,則表示需要立即提供動態存儲卷。
此時調用CSI Plugin的CreateVolume接口,同時創建名為${Provisioner指定的PV前綴}-${PVC uuid}的PV
(2)watch PV對象,判斷其是否需要刪除,標准如下:
①判斷其.status.phase是否為Release。
②判斷其.spec.PersistentVolumeReclaimPolicy是否為Delete。
③判斷其annotation[pv.kubernetes.io/provisioned-by]是否與自己的provisioner名稱相等。
若需要,則調用CSI Plugin的DeleteVolume接口,同時刪除PV對象
  • external-attacher(csi-attacher)

(1)watch VolumeAttachment對象,獲得PV的所有信息,如volume ID、node ID等。根據VolumeAttachment的DeletionTimestamp和.status.attached來判斷是調用CSI Plugin的ControllerPublish做attach,還是調用CntrollerUnpublish接口做detach
(2)在attacher時為相關PV打上Finalizer;當PV處於刪除狀態(DeletionTimestamp非空)時,刪除Finalizer
  • external-snapshotter

(1)watch VolumeSnapshot對象,根據其狀態調用CSI Plugin的CreateSnapshot接口
等存儲快照生成后,它會將存儲快照生成的相關信息放到VolumeSnapshotContent對象中,並和用戶提交的VolumeSnapshot做bound
(2)當VolumeSnapsho處於刪除狀態(DeletionTimestamp非空)時,調用CSI Plugin的DeleteSnapshot接口
  • external-resizer

watch PVC對象,判斷用戶在PVC中是否增加了需求的存儲空間。如果PVC狀態是Bound且.status.Capacity與.spec.Resources.Requests不等,則進行卷擴展:
①更新PVC的.status.Conditions,表明此時處於Resizing狀態
②調用CSI Plugin的ControllerExpandVolume接口,若返回值中NodeExpansionRequired=true(還需要Kubelet中的Volume Manager繼續調用CSI Plugin的NodeExpandVolume接口進行擴容),則更新PVC的status.Conditions 為 FileSystemResizePending;否則,更新 PVC的.Status.Conditions為空,且更新PVC的status.Capacity。
③更新PV的spec.Capacity
  • node-driver-registrar

調用CSI Plugin的接口獲取插件信息,通過Kubelet的 插件注冊機制將CSI Plugin注冊到kubelet 
  • livenessprobe

 調用CSI Plugin的Probe接口,同時在/healthz暴露HTTP健康檢查探針 

 

三、CSI插件與CSI sidecar容器的組合

一般會在一個CSI Plugin容器中同時實現所有接口,使其同時作為CSI Controller Server和CSI Node Server
它會和Kubernetes官方提供的CSI sidecar容器組成Pod,每種組合都會完成某種功能:
CSI Controller Server和External CSI SideCar、CSI Node Server和Kubelet通過Unix Socket來通信:
 
因此,CSI分為兩部分:
  • 由k8s社區驅動實現的通用的部分,如圖中的csi-provisioner和csi-attacher
  • 由雲存儲廠商實現的csi-controller-server和csi-node-server(一般集成到一個一個CSI Plugin容器中),對接雲存儲廠商的API實現真正的create/delete/mount/unmount存儲的相關操作
 

四、通過CRD管理CSI

對CSI的管理是通過CRD的形式實現的,所以引入了自定義對象類型VolumeAttachment、CSINode、CSIDriver 

(1)VolumeAttachmen

VolumeAttachment描述一個Volume的attach/detach信息。可以通過VolumeAttachment對一個Volume在某個節點上的attach狀態進行跟蹤。
 
VolumeAttachmentSpec數據結構:
  • Attacher
根據Driver name指定由哪個CSI Plugin來進行attach/detach
  • NodeName
指定了attach/detach是發生在哪個節點上的
  • Source
指定了對哪一個PV進行attach/detach。
 
VolumeAttachmentStatus數據結構:
  • Attached
指示了是否已經attach
    如果VolumeAttachment的DeletionTimestamp為空且Attached=false,則external-attacher需要調用CSI Plugin的ControllerPublish做attach
    如果VolumeAttachment的DeletionTimestamp不為空且Attached=true,則external-attacher需要調用CSI Plugin的ControllerUnPublish做detach
 

(2)CSIDriver

CSIDriver描述了部署的CSI Plugin、定義了Kubernetes調用CSI Plugin的行為,需要管理員根據插件類型進行創建
CSIDriverSpec數據結構:
  • AttachRequired
定義一個Plugin是否支持Attach功能。
不需要Attach操作時,該標簽就是False,說明無需部署external-attacher,也不需要VolumeAttachment對象。
例子:調用CreateVolume接口時,CreateVolumeRequest中VolumeCapability決定了是文件系統直接掛載還是塊設備掛載。塊存儲需要Attach操作,但文件存儲不需要Attach操作,例如NFS沒有掛載、格式化的概念,只需要mount遠端文件系統到本地即可
 
 
  • PodInfoOnMount
定義了調用CSI Plugin的NodePublishVolume接口時是否帶上Pod信息(PodName、PodUID、PodNamespace)。
默認為false,即CSI Plugin無法獲知Pod信息
  • VolumeLifecycleModes
該CSI Plugin支持哪些VolumeLifecycleMode
包括Persistent和Ephemeral兩種
  • StorageCapacity
若為true,表示希望調度器調度時考慮節點的存儲容量
  • FSGroupPolicy
Volume掛載前是否允許更改屬主、權限的定義
  • TokenRequests
CSI Plugin可能需要Pod的Service Account Token
  • RequiresRepublish
若為true,表示CSI Plugin希望不斷被調用NodepublishVolume接口,以更新掛載到Pod中的Volume的相關變化。
不過只能更新Volume的contents,running狀態Pod的掛載點是不能改變的

(3)CSINode

CSINode是集群中的節點信息,由Node上的node-driver-registrar在啟動時創建。
因此,可以只定義集群中一部分節點擁有CSINode。
 
CSINodeSpec由一組CSIDriver信息組成。每一個新的CSI Plugin注冊后,都會在其中添加自身信息
每個CSI Plugin可添加的CSIDriver信息包括:
  • Name
  • NodeID
  • Allocatable
節點最多可以attach多少個Volume
  • TopologyKeys
該CSINode支持哪些TopologyKeys。進行支持拓撲感知的provisioning時,將會根據這些TopologyKeys從Node獲取values,會傳遞給CSI Plugin
如果CSI Plugin不支持拓撲,可以置為null。
 

五、總結

插件注冊

(1)kubelet在啟動的時候有一個約定,例如在/var/lib/kubelet/plugins_registry目錄每新加一個文件,就相當於新加了一個Plugin。
(2)node-driver-registrar首先會調用CSI-Plugin的GetPluginInfo接口,獲取CSI Plugin監聽的地址以及其Driver name。
(3)node-driver-registrar會監聽自身的GetInfo和NotifyRegistrationStatus接口。
(4)在/var/lib/kuberlet/plugins_registry這個目錄下啟動一個Socket,生成一個Socket文件 ,例如:"com.nfs.csi.tds-reg.sock”
(5)kubelet Watche到Socket后,通過該Socket調用node-driver-registrar的GetInfo接口,獲取其剛剛獲得的CSI-Plugin的信息
(6)獲取成功之后,Kubelet會:
①根據CSI Plugin監聽地址調用其NodeGetInfo接口,獲取[nodeID]、[Drive name]、[AccessibleTopology]、maxAttachLimit(節點最大可掛載卷數量)
    為Node新建(或更新已有)annotation[csi.volume.kubernetes.io/nodeid]=xxx
    基於[AccessibleTopology]給Node添加label
    根據maxAttachLimit更改Node的.status.Allocatable:attachable-volumes-csi-[Drive name]=[maxAttachLimit]。
②根據CSI Plugin監聽地址調用其NodeGetVolumeStats接口,並反映在其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剩余量
③創建一個CSINode對象(或更新已有CSINode對象)
④調用node-driver-registrar的NotifyRegistrationStatus接口,告訴它CSI-Plugin已經注冊成功了。

Provisioning Volumes

(1)PV Controller觀察到集群中新創建的PVC沒有與之匹配的PV,且其對應SC指定的Provisioner不是Internal Provisioner,於是為PVC打annotation[volume.beta.kubernetes.io/storage-provisioner]=xxx
(2)對應external-provisioner判斷需要創建存儲卷,於是調用CSI Plugin的CreateVolume接口
(3)external-provisioner在請求參數設置AccessibilityRequirements
    如果SC的VolumeBindingMode為Immediate模式:如果SC指定了AllowedTopologies則,只考慮AllowedTopologies,否則需要將所有包含TopologyKeys鍵的節點Value添進來。
    如果SC的VolumeBindingMode為WaitForFirstConsumer模式:獲取Pod調度到的節點的CSINode對象的TopologyKeys,根據這些TopologyKeys從Node的Label獲取values值,與SC的AllowedTopologies比對,不符合則報錯
(4)CSI Plugin返回成功后,表示存儲卷已經在Cloud Storage Vendor真正創建完成
存儲卷既有可能是新的,也有可能是根據VolumeSnapshotContent中的Snapshot的ID創建的:
(5)external-provisioner會創建一個PV,PV Controller會將PV與PVC進行綁定。
 

Attaching Volumes

(1)AD  Controller觀察到使用VolumeSource為CSI的PV的Pod被調度到某一節點,會調用AD  Controller內部in-tree CSI插件(csiAttacher)的Attach函數,由其創建一個VolumeAttachment
(2)同時,external-attacher也會同步一些PV的信息在VolumeAttachment里。
(3)external-attacher觀察到該VolumeAttachment對象,如果其.spec.Attacher中的Driver name指定的是自己同一Pod內的CSI Plugin,則調用CSI Plugin的ControllerPublish接口
(4)CSI Plugin會調用雲存儲廠商的API,把遠端的Volume attach到目標節點中上(如/dev/vdb)
(5)external-attacher更新該VolumeAttachment的.status.Attached為true。
(4)AD  Controller內部in-tree CSI插件(csiAttacher)觀察到該VolumeAttachment的.status.Attached為 true,於是更新AD  Controller內部狀態(ActualStateOfWorld),該狀態會顯示在Node資源的.status.VolumesAttached上。

Mounting Volumes

(1)kubelet組件Volume Manager觀察到有新的使用PersistentVolumeSource為CSI的PV的Pod調度到本節點上,於是調用內部in-tree CSI插件(csiAttacher)的WaitForAttach函數。
(2)內部in-tree CSI插件(csiAttacher)等待attach完成后,調用MountDevice函數,由其調用CSI plugin的NodeStageVolume接口;
(3)插件csiAttacher調用內部 in-tree CSI 插件(csiMountMgr)的SetUp函數,由其調用CSI plugin的NodePublishVolume接口。

Unmounting Volumes

用戶刪除相關Pod后
(1)kubelet組件Volume Manager觀察到有新的使用PersistentVolumeSource為CSI的PV的Pod被刪除,於是調用內部 in-tree CSI 插件(csiMountMgr)的TearDown函數,由其調用CSI plugin的NodeUnpublishVolume接口。
(2)kubelet組件Volume Manager調用內部in-tree CSI 插件(csiAttacher)的 UnmountDevice函數,由其調用CSI plugin的NodeUnpublishVolume接口。

Detaching Volumes

(1)AD Controller觀察到有PersistentVolumeSource為CSI的PV被刪除,就會調用內部 in-tree CSI 插件(csiAttacher)的Detach函數。
(2)csiAttacher會刪除相關VolumeAttachment對象(但由於存在finalizer,不會立即刪除成功)。
(3)external-attacher觀察到該VolumeAttachment對象的DeletionTimestamp非空,於是調用CSI Plugin的 ControllerUnpublish接口以從對應節點上detach Volume。
(4)detach成功后,external-attacher會移除該VolumeAttachment對象的finalizer字段,使其被徹底刪除。
(4)AD Controller內部in-tree CSI插件(csiAttacher)觀察到該VolumeAttachment對象已刪除,於是更新AD Controller中的內部狀態;同時從Node的.status.VolumesAttached中清除此attach信息。

Deleting Volumes

external-provisioner watch到用戶刪除了PVC,根據 PVC的回收策略(Reclaim)執行不同操作:
    Delete:調用CSI Plugin的DeleteVolume接口,由其去遠端存儲刪除存儲卷;一旦成功刪除,external-provisioner會刪除對應PV對象。
    Retain:external-provisioner不執行存儲卷刪除操作。
 

參考資料:

[1] https://kubernetes.io/docs/home/

[2] https://edu.aliyun.com/roadmap/cloudnative

[3] 鄭東旭《Kubernetes源碼剖析》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM