新聞中心
現(xiàn)在,我將繼續(xù)和大家聊一聊關(guān)于 K8s 存儲的一個重要組成部分:Container Storage Interface (CSI)。在接下來的內(nèi)容中,我們將會了解到 CSI 的工作原理、核心概念以及如何將其集成到你的容器化環(huán)境中。

創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比祁連網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式祁連網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋祁連地區(qū)。費用合理售后完善,10余年實體公司更值得信賴。
為什么需要 CSI ?它解決了什么問題?
在學(xué)習(xí) CSI 之前,了解其產(chǎn)生的背景以及它能夠解決的問題我覺得是很有必要的。
為什么需要 CSI
雖然 Kubernetes 平臺它本身支持了非常多的存儲插件,但是畢竟也是有限的,永遠無法滿足用戶日益增長的需求,比方說有客戶要求我們的 Paas 平臺必須接國產(chǎn)的存儲怎么辦?
面臨的問題,如何做集成?
Kubernetes 本身提供了一個強大的 Volume 插件系統(tǒng),最直接的方式就是向這個 Volume Plugin 增加新的插件。
但是,想必大家也知道 Kubernetes 太復(fù)雜了,它有一定的學(xué)習(xí)曲線,這樣做一來成本比較高,再者直接集成第三方代碼,可能會對 Kubernetes 平臺系統(tǒng)的可靠性和安全性產(chǎn)生隱患。
另外,這種方法它也不方便測試和維護,比方說第三方存儲服務(wù)如果有變更,我們就需要提交變更代碼到 Kubernetes,等待 Code Review。換句話說,我們必須要等到 Kubernetes 發(fā)布才能將存儲服務(wù)的變動上線,這就意味著存儲的集成與 Kubernetes 的發(fā)布周期捆綁在一起了。
所以直接和 Kubernetes 做集成是非常不方便的。
讓我們重新回過來看下,上面反復(fù)提到過的 In-Tree 和 Out-Of-Tree 這兩個概念,我相信從字面意思上大家都已經(jīng)理解了,再結(jié)合下面這張表格,大家心里是否都已經(jīng)有了答案?
圖片
解決了什么痛點?
CSI 將三方存儲代碼與 K8s 代碼解耦,不同的存儲插件只要實現(xiàn)這些統(tǒng)一的接口,就能對接 K8s,用戶無需接觸核心的 K8s 代碼。
最重要的是,CSI 規(guī)范現(xiàn)在是業(yè)界容器編排統(tǒng)一的存儲接入標(biāo)準(zhǔn)。
圖片
Container Orchestrators(CO)
那么,什么是 CSI?
以下是 ChatGPT 給出的回答:
CSI(Container Storage Interface) 是一個規(guī)范化的接口,用于容器編排引擎與存儲供應(yīng)商之間的通信。它允許存儲供應(yīng)商編寫標(biāo)準(zhǔn)的插件,以便容器編排引擎可以與其集成,從而實現(xiàn)更加靈活和可擴展的存儲解決方案。
CSI 驅(qū)動器由三個主要組件組成,每個組件都扮演著特定的角色:
-
Node Service: 運行在每個 Kubernetes 節(jié)點上,負責(zé)在節(jié)點上掛載和卸載存儲卷,并處理節(jié)點級別的存儲操作。
-
Controller Service: 運行在 Kubernetes 控制平面中,負責(zé)管理存儲卷的生命周期,包括創(chuàng)建、刪除和擴容等操作。
-
Identity Service: 它是 CSI 驅(qū)動器的第三個組件,它在 CSI 驅(qū)動器注冊時提供標(biāo)識信息,并向 Kubernetes 集群公開驅(qū)動器的支持能力。它負責(zé)告知 Kubernetes 驅(qū)動器的存在,提供驅(qū)動器的基本信息和功能支持。
CSI 的設(shè)計思想是將存儲管理和容器編排系統(tǒng)解耦,使得新的存儲系統(tǒng)可以通過實現(xiàn)一組標(biāo)準(zhǔn)化的接口來與 Kubernetes 進行集成,而無需修改 Kubernetes 的核心代碼。
CSI 驅(qū)動器的出現(xiàn)為 Kubernetes 用戶帶來了更多的存儲選擇,同時也為存儲供應(yīng)商和開發(fā)者提供了更方便的接入點,使得集群的存儲管理更加靈活和可擴展。
圖片
Storage in Cloud Native Environment
CSI 適配工作是由容器編排系統(tǒng)(CO)和存儲提供商(SP)共同完成的,CO 通過 gRPC 與 CSI 插件進行通信。相信大家也都觀察到了,CSI 在這里充當(dāng)了連接的紐帶,上層連接容器編排系統(tǒng),下層操作三方存儲服務(wù)。
CSI 的工作原理,它是如何工作的?
Typical CSI driver architecture
下面是 CSI 的一個典型架構(gòu),雖然 CSI 對于存儲提供商來說只需實現(xiàn)三個模塊即可,但是整個編排流程可以說是相當(dāng)復(fù)雜的。
圖片
Kubernetes cluster with CSI
CSI 的整個運轉(zhuǎn)流程會涉及到兩方面的組件:
? 由 Kubernetes 官方維護的一組標(biāo)準(zhǔn) external 組件,他們主要負責(zé)監(jiān)聽 K8s 里的資源對象,從而向 CSI Driver 發(fā)起 gRPC 調(diào)用,詳見:Kubernetes CSI Sidecar Containers[1]。它們是與 CSI 驅(qū)動器一起部署在同一個 Pod 中,用于輔助 CSI Driver 完成一些額外的任務(wù)和功能。? 各存儲廠商開發(fā)的組件(需要實現(xiàn) Identity Service,Controller Service,Node Service)
我們來看下左邊的 CSI Driver Controller 部分,它是通過多個 Sidecar 組件配合第三方實現(xiàn)的插件(Controller Service)來完成的。
正如上面提到的,它負責(zé)管理存儲卷的生命周期,包括創(chuàng)建、刪除和擴容等操作。換句話說,我們的存儲廠商能夠提供什么樣的能力,部署 Controller 的時候,就需要提供與之對應(yīng)的 Sidecar 容器一起部署。
好比說我的 CSI Driver 只提供了 Dynamic provisioning 的能力,其他能力的接口我都沒實現(xiàn),在控制器部署的時候,我只需要將 external-provisioner 和我的 Controller Service 部署在一個 Pod 即可,組件的選擇完全取決于三方的實現(xiàn)。
Kubernetes CSI Sidecar Containers
#1. external-provisioner
external-provisioner 是一個 Sidecar 容器,用于在 Kubernetes 中動態(tài)地創(chuàng)建和刪除外部存儲卷。
當(dāng)一個新的 PVC (PersistentVolumeClaim) 被創(chuàng)建時,external-provisioner 會向外部存儲系統(tǒng)發(fā)起請求,以創(chuàng)建相應(yīng)的存儲卷,并將其與 PVC 關(guān)聯(lián),從而滿足 Pod 對持久化存儲的需求。
external-provisioner 實際上會執(zhí)行檢查,以驗證 PVC 中是否存在注解 volume.kubernetes.io/storage-provisioner,并且該注解的值是否與 CSI 驅(qū)動程序的名稱相匹配。整個流程貫穿了 PV Controller 這個組件。
圖片
provision
這里涉及到的兩個操作分別對應(yīng)著 Controller Service 中的 CreateVolume 和 DeleteVolume 兩個接口的實現(xiàn),它們的調(diào)用者正是 External Provisoner。
這一流程的核心是,external-provisioner 充當(dāng)了中間人,通過 Kubernetes 的 PVC 和 StorageClass 機制,將 Pod 的持久存儲需求傳遞給外部存儲系統(tǒng)。這使得存儲卷的創(chuàng)建和管理能夠無縫集成到 Kubernetes 集群中,為應(yīng)用提供了持久性的存儲解決方案。
思考
假設(shè)每個 PVC 背后對應(yīng)的 Volume 都需要獨立加密,并且加密密鑰也各不相同,PVC 的 Spec 中已經(jīng)沒有額外的參數(shù)來提供這些信息了,那么我們?nèi)绾螌⑦@些加密密鑰傳遞給 CSI 接口呢?
這里有必要提一下 CSI Operation Secrets 這個概念,它允許針對每種不同的 CSI 操作定制不同的 Secret,并且通過 StorageClass 與之配合使用。
讓我們來看下面這個 StorageClass 的定義作為例子:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: xfs2-sc-4-per-volume
provisioner: xfs2.csi.basebit.ai
parameters:
csi.storage.k8s.io/provisioner-secret-name: ${pvc.name}
csi.storage.k8s.io/provisioner-secret-namespace: ${pvc.namespace}需要關(guān)注的是 parameters 字段中的 csi.storage.k8s.io/provisioner-secret-name 字段的值,它使用了變量 ${pvc.name}。怎么理解呢?
具體而言,當(dāng)我們使用 ${pvc.name} 作為 csi.storage.k8s.io/provisioner-secret-name 參數(shù)的值時,每次創(chuàng)建 PVC 后,都可以為它創(chuàng)建一個對應(yīng)的 Provisioner Secret,并將該 PVC 的名稱作為 Provisioner Secret 的名稱。
這樣,每個 PVC 都會擁有一個唯一的 Provisioner Secret,用于身份驗證和認(rèn)證。Secret 的具體定義取決于每個 CSI 驅(qū)動器的實現(xiàn)。在針對創(chuàng)建存儲卷的場景中,CreateVolumeRequest 可以獲取到 Secret 的詳細信息。
這種參數(shù)化技術(shù)是 Kubernetes 中允許的一種靈活方式,可用于在運行時動態(tài)生成配置文件
provision、delete、expand、attach 和 detach 等操作通常也需要 CSI 驅(qū)動程序在存儲后端使用憑證,后面就不再多做贅述了。
如需了解更多高級用法,請參考文檔:StorageClass Secrets[2]
#2. external-attacher
external-attacher 是一個 Sidecar 容器,其作用是在 Kubernetes 節(jié)點上動態(tài)地進行外部存儲卷的 掛載(Attach) 和 卸載(Detach) 操作。
它是通過監(jiān)聽 Kubernetes API Server 中的 VolumeAttachment 對象,來觸發(fā) Controller Service 中的 ControllerPublishVolume 和 ControllerUnpublishVolume 兩個接口調(diào)用。
然而,并不是所有情況下都需要使用 attach/detach 操作,尤其是在一些分布式文件系統(tǒng)的 CSI 驅(qū)動程序中,這樣的操作可能并不適用。因此,可以說 attach/detach 是一項可選的特性。
#3. external-resizer
external-resizer 是一個 Sidecar 容器,用于調(diào)整外部存儲卷的大小。
當(dāng) PVC 的存儲需求發(fā)生變化時,external-resizer 可以根據(jù)需求調(diào)整外部存儲卷的大小,確保存儲資源得到最優(yōu)的利用。
它會調(diào)用 Controller Service 中的 ControllerExpandVolume 接口。
#4. external-snapshotter
external-snapshotter 是一個 Sidecar 容器,用于實現(xiàn)外部存儲卷的快照功能。
它是通過監(jiān)聽 Kubernetes Snapshot CRD 對象,來觸發(fā) Controller Service 中的 CreateSnapshot 和 DeleteSnapshot 兩個接口調(diào)用。
它負責(zé)在外部存儲系統(tǒng)上創(chuàng)建、刪除和管理快照,以便于實現(xiàn)數(shù)據(jù)備份、恢復(fù)和復(fù)制等功能。
思考
這玩意兒有什么用呢?VolumeSnapshot 允許在 Kubernetes 集群中創(chuàng)建卷的快照,這些快照可以用于數(shù)據(jù)備份和應(yīng)用程序恢復(fù)。當(dāng)應(yīng)用程序出現(xiàn)故障或數(shù)據(jù)損壞時,我們可以使用先前創(chuàng)建的快照來還原應(yīng)用程序的狀態(tài)。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redis-data-my-redis-master-0
spec:
dataSource:
name: my-server-snapshot-0
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi#5. liveness-probe
liveness-probe 是一個 Sidecar 容器,用于監(jiān)控 CSI 驅(qū)動程序的運行狀況,并通過 Liveness Probe 機制向 Kubernetes 報告。
如果驅(qū)動器出現(xiàn)故障或停止響應(yīng),Kubernetes 將重啟相關(guān)的 Pod(Controller Service/Node Service) 以確保服務(wù)的可用性。
有關(guān)具體的用法和配置示例,可以參考這里[3]的說明文檔。
思考
實際上 livenessProbe 是通過 CSI 驅(qū)動程序在容器上設(shè)置的。liveness-probe sidecar 容器的主要作用是提供了 /healthz 這個 HTTP 端點。它背后 checkProbe 最終向 CSI 驅(qū)動程序的 Identity Service 中的 Probe 接口發(fā)起調(diào)用,用來檢測插件是否處于健康狀態(tài)。
這種架構(gòu)使得插件的健康狀態(tài)檢查與應(yīng)用程序分離,通過 CSI 驅(qū)動程序的 Probe 接口進行通信。
事實上 Kubernetes 從 v1.23 開始具有內(nèi)置 gRPC 健康探測,已經(jīng)不需要這么麻煩了。
#6. node-driver-registrar
node-driver-registrar 是一個作為 Sidecar 容器運行的組件,其主要職責(zé)是通過直接調(diào)用 CSI 驅(qū)動程序的 Node Service 中的 NodeGetInfo 接口,獲取驅(qū)動程序的信息。
然后,它會利用 kubelet 的插件注冊機制,將這些驅(qū)動程序的信息注冊到相應(yīng)節(jié)點的 kubelet 中。
小結(jié)
我們需要記住這張能力關(guān)系組合表,它對部署 CSI 驅(qū)動程序和排查問題非常的有用。
圖片
如何實現(xiàn)一個 CSI 插件?
要實現(xiàn)一個 CSI 驅(qū)動程序,確實只需要完成一系列接口的實現(xiàn)即可,但僅僅完成這些接口的實現(xiàn)還不足以構(gòu)建一個穩(wěn)健、可用的 CSI 驅(qū)動程序,構(gòu)建一個穩(wěn)健的驅(qū)動程序還需要考慮方方面面。
CSI Plugin components
下面是每一個 CSI 驅(qū)動程序要實現(xiàn)的接口清單。
其中 Identity Service 負責(zé)提供 CSI 驅(qū)動程序的身份信息,Controller Service 負責(zé) Volume 的管理,Node Service 負責(zé)將 Volume 掛載到 Pod 中。
圖片
正如前面提到的,一個 CSI 驅(qū)動程序能提供什么樣的能力,取決于各自存儲廠商的實現(xiàn),三個組件都有對外暴露能力的接口,比如
- Identity Service 中的 GetPluginCapabilities 方法,表示該 CSI 驅(qū)動程序主要提供了哪些功能。
- Controller Service 中的 ControllerGetCapabilities 方法,實際上告訴 K8s,CSI 驅(qū)動程序具備哪些能力。這些能力可以包括卷的創(chuàng)建、刪除、擴容、快照等操作。
- Node Service 中的 NodeGetCapabilities 方法,提供 Node plugin 的能力列表。
CSI lifecycle
在通常情況下,每個 Volume 都會經(jīng)歷完整的生命周期過程。
圖片
從創(chuàng)建 PersistentVolumeClaim(PVC)開始,接著被 Pod 所使用,這個過程包括三個主要階段:Provision -> Attach -> Mount。
隨后,從 Pod 開始被刪除,直到 PVC 被刪除,整個過程又經(jīng)歷了另外三個關(guān)鍵階段: Unmount -> Detach -> Delete。
然而,存在一種特殊的存儲卷,它就是 Ephemeral Inline Volumes,它可以通過改變 CSIDriver 的規(guī)范中的 volumeLifecycleModes 參數(shù)來改變其生命周期。
apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
name: xfs2.csi.basebit.ai
spec:
...
volumeLifecycleModes:
- EphemeralEphemeral 模式表示存儲卷是臨時的,會隨著 Pod 的生命周期結(jié)束而被釋放。對于這種類型的存儲卷,Kubelet 在向 CSI 驅(qū)動請求卷時,只會調(diào)用 NodePublishVolume,省略了其他階段(例如 CreateVolume、NodeStageVolume)的調(diào)用。而在 Pod 結(jié)束需要釋放存儲卷時,只會調(diào)用 NodeUnpublishVolume。
具體的 Pod 規(guī)范如下所示:
kind: Pod
apiVersion: v1
metadata:
name: my-csi-app
spec:
containers:
- name: my-frontend
image: busybox
volumeMounts:
- mountPath: "/data"
name: my-csi-inline-vol
command: ["sleep","1000000"]
volumes:
- name: my-csi-inline-vol
csi:
driver: xfs2.csi.basebit.ai
volumeAttributes:
foo: bar這里的 volumeAttributes 用于指定驅(qū)動程序需要準(zhǔn)備的卷的屬性。這些屬性在每個驅(qū)動程序中都是特定的,沒有標(biāo)準(zhǔn)化的實現(xiàn)方法。
Ephemeral 使用案例
Secrets Store CSI Driver[4]允許用戶將 Secret 作為內(nèi)聯(lián)卷從外部掛載到一個 Pod 中。當(dāng)密鑰存儲在外部管理服務(wù)或 Vault 實例中時,這可能很有用。
Cert-Manager CSI Driver[5] 與 cert-manager[6] 協(xié)同工作, 無縫地請求和掛載證書密鑰對到一個 Pod 中。這使得證書可以在應(yīng)用 Pod 中自動更新。
通過這個特性,再一次說明了我們并不要求所有的接口都需要實現(xiàn),取決于插件實現(xiàn)方提供什么樣的能力,我們再去實現(xiàn)相應(yīng)的邏輯即可。
CSI idempotent
我們應(yīng)該確保所有的 CSI 操作都是冪等的,這意味著同一操作被多次調(diào)用時,結(jié)果始終保持一致,不會因為多次調(diào)用而導(dǎo)致狀態(tài)變化或產(chǎn)生額外的副作用。這種冪等性是保證系統(tǒng)穩(wěn)定性和一致性的關(guān)鍵因素。
舉個例子,假設(shè)我們做一個 DeleteVolume 的操作,如果底層的 Volume 已經(jīng)不存在了,依然不能報錯。無論是第一次執(zhí)行 DeleteVolume 還是多次重試,操作的最終結(jié)果都應(yīng)該是相同的。
這里不得不提一下 CSI Sanity Test,它可用于驗證 CSI 驅(qū)動程序的基本功能和穩(wěn)定性。它會模擬不同的錯誤和異常情況,例如創(chuàng)建已存在的卷、卸載不存在的卷等,以驗證驅(qū)動程序?qū)@些情況的處理是否正確。
它能夠驗證 CSI 驅(qū)動程序是否符合 Kubernetes CSI 規(guī)范并且可以正確運行,對開發(fā) CSI 驅(qū)動程序非常的有幫助。比如說可以幫助開發(fā)人員快速定位和修復(fù)常見問題,減少在生產(chǎn)環(huán)境中出現(xiàn)意外問題的可能性。
官方文檔中詳細闡述了規(guī)范(Container Storage Interface,CSI)的內(nèi)容,同時還提供了與開發(fā)相關(guān)的注意事項。這些注意事項涵蓋了規(guī)范中的一些關(guān)鍵要點,以及在開發(fā)過程中可能會遇到的挑戰(zhàn)和解決方案。我們可以在 Container Storage Interface (CSI) Specification [7] 找到這些詳細信息。
如何部署 CSI?
標(biāo)準(zhǔn)的 CSI 驅(qū)動程序部署架構(gòu)如下圖所示,其中包括一個由 DaemonSet 運行的 CSI Node 組件,以及一個運行在 StatefulSet 內(nèi)的 CSI Controller 組件。
? 這兩個容器通過本地 Socket (Unix Domain Socket, UDS)進行通信,并使用 gRPC 協(xié)議。CSI 插件直接與同一宿主機上的 K8s 組件進行交互,通過本機進程之間的 Unix 域套接字通信,相較于 TCP 套接字,具備更高的通信效率和性能。
圖片
在部署 CSI Node 時,需要將宿主機上的 kubelet 目錄(/var/lib/kubelet)掛載到驅(qū)動程序的容器內(nèi),且需將 Mount Propagation 設(shè)置為 Bidirectional。這樣,驅(qū)動程序容器內(nèi)的后續(xù) Mount/Umount 操作能夠傳播到宿主機上。
請注意,這只是一個高層次的架構(gòu)概述,具體的實施細節(jié)可能會因不同的 CSI 插件和環(huán)境而有所變化。
CSI 在集群部署成功后,可以用以下兩個命令來做下檢查:
#1. 查看集群內(nèi)安裝的 CSI Driver
kubectl get csidrivers#2. 列出哪些節(jié)點具有 CSI
kubectl get csinodes總結(jié)
CSI 是 Kubernetes 存儲體系中的核心組件,為存儲供應(yīng)商提供了靈活且可擴展的集成方式,也為 Kubernetes 用戶提供了高效穩(wěn)定的存儲解決方案。
通過標(biāo)準(zhǔn)化容器編排器與存儲供應(yīng)商之間的接口,CSI 構(gòu)建了一種統(tǒng)一的范式,確保所有與 CSI 兼容的存儲系統(tǒng)都遵循相同的實現(xiàn)規(guī)范。事實上,通過編寫一個 CSI 驅(qū)動程序,我們不僅為 Kubernetes 存儲架構(gòu)增添了新的維度,還深化了對存儲資源管理的理解。
下一期,我將繼續(xù)與大家分享在實際工作中使用 CSI Driver 遇到的問題和挑戰(zhàn)。
參考資料
[1]Kubernetes CSI Sidecar Containers: https://kubernetes-csi.github.io/docs/sidecar-containers.html
[2]StorageClass Secrets: https://kubernetes-csi.github.io/docs/secrets-and-credentials-storage-class.html
[3]這里: https://github.com/kubernetes-csi/livenessprobe/blob/master/deployment/kubernetes/livenessprobe-sidecar.yaml
[4]Secrets Store CSI Driver: https://github.com/kubernetes-sigs/secrets-store-csi-driver
[5]Cert-Manager CSI Driver: https://github.com/cert-manager/csi-driver
[6]cert-manager: https://cert-manager.io/
[7]Container Storage Interface (CSI) Specification : https://github.com/container-storage-interface/spec/blob/master/spec.md
[8]CSI Documentation: https://kubernetes-csi.github.io/docs/
[9]CloudNativeCon EU 2018 CSI Jie Yu: https://schd.ws/hosted_files/kccnceu18/fb/CloudNativeCon%20EU%202018%20CSI%20Jie%20Yu.pdf
分享題目:KubernetesStorage:淺談如何實現(xiàn)一個CSI插件
轉(zhuǎn)載來于:http://www.5511xx.com/article/djcohje.html


咨詢
建站咨詢
