K8S儲存外掛-FlexVolume && CSI
K8S的自定義儲存外掛
和K8S的網路不太一樣,K8S的網路只有CNI一種介面暴露方式,所有的網路實現基於第三方進行開發實現,但儲存的內建實現就多達20多種,#K8S目前支援的外掛型別#。但內建的往往不滿足定製化的需求,所以和CNI 一樣,K8S 也暴露了對外的儲存介面,和CNI 一樣,通過實現對應的介面方法,即可建立屬於自己的儲存外掛,但和CNI 有點區別的是,K8S的儲存外掛的自定義實現方式,有FlexVolume 和 CSI 兩種,兩者的差別可以看做是新老功能的差異,但目前為止,FlexVolume 同樣也有用武之地。
FlexVolume
熟悉CNI的編寫方法的,對FlexVolume的編寫一定不陌生,CNI編寫完成後,是會拆分為2個二進位制檔案(CNI,IPAM)和一個配置檔案,放在每個Node節點上,kubelet在建立Pod 時候,會呼叫對應的二進位制檔案進行網路的建立,同樣也可以使用daemonset的方式容器化部署,FlexVolume 也一樣,編寫完成後一樣以二進位制的方式進行部署,和CNI 一樣,FlexVolume需要實現類似CNI的cmdadd,cmddel的方法,具體需要實現以下幾個方法:
- init:kubelet/kube-controller-manager 初始化儲存外掛時呼叫,外掛需要返回是否需要attach 和 detach 操作
- attach:將儲存卷掛載到 Node 上
- detach:將儲存卷從 Node 上解除安裝
- waitforattach: 等待 attach 操作成功(超時時間為 10 分鐘)
- isattached:檢查儲存卷是否已經掛載
- mountdevice:將裝置掛載到指定目錄中以便後續 bind mount 使用
- unmountdevice:將裝置取消掛載
- mount:將儲存卷掛載到指定目錄中
- umount:將儲存卷取消掛載
基本返回格式:
{
"status": "<Success/Failure/Not supported>",
"message": "<Reason for success/failure>",
"device": "<Path to the device attached. This field is valid only for attach & waitforattach call-outs>"
"volumeName": "<Cluster wide unique name of the volume. Valid only for getvolumename call-out>"
"attached": <True/False (Return true if volume is attached on the node. Valid only for isattached call-out)>
"capabilities": <Only included as part of the Init response>
{
"attach": <True/False (Return true if the driver implements attach and detach)>
}
}
那麼kublet和他的呼叫關係是啥?看一下kubelet呼叫的FlexVolume的一段程式碼(pod mount dir):
// SetUpAt creates new directory.
func (f *flexVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
...
call := f.plugin.NewDriverCall(mountCmd)
// Interface parameters
call.Append(dir)
extraOptions := make(map[string]string)
// pod metadata
extraOptions[optionKeyPodName] = f.podName
extraOptions[optionKeyPodNamespace] = f.podNamespace
...
call.AppendSpec(f.spec, f.plugin.host, extraOptions)
_, err = call.Run()
...
return nil
}
再看下一個PV的yml的栗子對比一下:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-flex-nfs
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
flexVolume:
driver: "k8s/nfs"
fsType: "nfs"
options:
server: "1.1.1.1"
share: "export"
先只看FlexVolume, dirver這裡的k8s/nfs 就是FlexVolume的具體位置,注意k8s~nfs解析出來的就是k8s/nfs, FlexVolume的預設位置在/usr/libexec/kubernetes/kubelet-plugins/volume/exec/
,所以上述yml用的FlexVolume外掛具體位置在/usr/libexec/kubernetes/kubelet-plugins/volume/exec/k8s~nfs/nfs
。
pv的yml 裡面有對應的options, 就是kubelet程式碼裡的extraOptions := make(map[string]string)
,是一個map型別,kubelet解析yml裡面的option引數,傳入該map變數,然後執行FlexVolume的具體方法,比如栗子裡的mount,將options的引數傳入,實現瞭如下的效果:
/usr/libexec/kubernetes/kubelet-plugins/volume/exec/k8s~nfs/nfs mount <mount dir> <json param>
git 上fork的幾個demo,方便查詢。
可以看到FlexVolume實現邏輯非常簡單,但解決的問題也非常有限,使用者還是需要手動建立PV,且每次呼叫外掛執行的都是偏原子操作的,即單次只執行attach,mount,umount等動作,所以,其作用,只是一個針對不同儲存自定義建立PV的外掛,假設我需要動態生成PV 並動態繫結PVC,FlexVolume就不具備可操作性,所以CSI來了。
CSI
先說下CSI和FlexVolume的基本差異,FlexVolume實現了Attach(掛載儲存到Node)和Mount(Node的目錄掛載到Pod), 缺少了PV的動態生成(需要運維手動在儲存上配置然後再建立手動建立PV),而CSI就是在FlexVolume的基礎上,實現了PV的動態生成。
CSI的呼叫和FlexVolume不一樣,在呼叫的時候,需要有一個註冊的過程,找個CSI的程式碼看下:
- CSI-Code-Demo:
目錄機構:
簡單先描述一下呼叫順序:
- driver.go, 主要是Run()方法,啟動RPC服務,註冊到CSI.
- identity.go, 主要是GetPluginInfo()方法,獲取外掛的各類資訊,如外掛名字等,GetPluginCapabilities,獲取CSI外掛的各項功能,比如是否支援Attach之類的,還有個Probe()方法,提供給K8s的探針,用於健康檢查CSI的狀態。
- controller.go, CSI實現的具體方法,比如操作儲存(CreateVolume 和 DeleteVolume),Attach儲存(ControllerPublishVolume 和 ControllerUnpublishVolume方法)
- node.go, 這一步主要實現的是mount的操作,即將目錄掛載到Pod裡,但是和簡單的mount方式不一樣,這裡分為了MountDevice(預處理,掛載到一個臨時目錄進行格式化) 和 SetUp(最終繫結,將臨時目錄繫結到實際的目錄), 分別對應了NodeStageVolume/NodeUnstageVolume 和 NodePublishVolume/NodeUnpublishVolume 方法。
上面只是描述了CSI外掛的呼叫順序,那麼問題來了,每一步,分別是誰去呼叫的呢?
回過頭先看下CSI的架構圖:
發現CSI的架構實際分了3塊,第一塊K8S-Core,即K8S的核心元件,第二塊Kubernetes External Component, 這是Kubernetes支援CSI的擴充套件元件,第三塊External Component:傳統意義上的CSI,即上面的那個demo程式碼。從官網架構圖的箭頭可以看到整體的呼叫關係:
- daemonset在每臺主機上執行了driver-registrar的container,和kubelet一一對應。
- kubelet呼叫driver-registrar,driver-registrar向CSI indentity(即程式碼裡的driver.go和identity.go)進行了註冊,並獲取了CSI 外掛的基本功能以及資訊。
- External provisioner watch 了master的apiserver,監聽pvc物件,一旦發現有建立, 則External provisioner會呼叫CSI Controller(呼叫了controller.go 裡的CreateVolume/DeleteVolume方法)進行了儲存端的建立,即自動生成了PV。
- External attacher,監聽了VolumeAttachment物件,一個發現有掛載,則呼叫CSI Controller 和 CSI Node進行卷的Attach和mount操作(分別呼叫了controller.go和node.go)。
從上面的呼叫邏輯可以看出,出了CSI本體(這裡暫稱為CSI Driver),還需要部署External Component裡的三個container,且這3個container裡只有driver-registrar需要和kubelet呼叫,所以在實際部署中,需要以daemonset的方式,將driver-registrar和CSI Driver作為side-car模式的部署方式進行部署,其他2個External provisioner和 External attacher以statefulset的方式,和CSI Driver一起作為side-car模式的部署方式進行部署。
簡單畫個部署圖:
個人公眾號, 分享一些日常開發,運維工作中的日常以及一些學習感悟,歡迎大家互相學習,交流
相關文章
- k8s CSI 外掛註冊原理K8S
- kubernetes/k8s CSI分析-容器儲存介面分析K8S
- [k8s] k8s基於csi使用rbd儲存K8S
- 容器儲存介面--CSI
- k8s的nfs儲存外掛設定過程K8SNFS
- Props 混入 外掛 插槽 本地儲存
- 如何接入 K8s 持久化儲存?K8s CSI 實現機制淺析K8S持久化
- 從零開始入門 K8s | Kubernetes 儲存架構及外掛使用K8S架構
- 一文讀懂容器儲存介面 CSI
- k8s階段03 持久卷, PV和PVC, CSI儲存方案示例csi-driver-nfs, OpenEBS, ConfigMap, Secret, DownwardAPI和ProjectedK8SNFSAPIProject
- 實現Dart版本物件儲存(COS)外掛Dart物件
- 沃趣微講堂 | 深入淺出Kubernetes儲存(四):儲存新方向-CSI
- 自建Kubernetes叢集如何使用阿里雲CSI儲存元件阿里元件
- wordpress外掛在伺服器上的儲存位置伺服器
- k8s之資料儲存-配置儲存K8S
- 雲原生儲存詳解:容器儲存與 K8s 儲存卷K8S
- 容器化 RDS:藉助 CSI 擴充套件 Kubernetes 儲存能力套件
- k8s之資料儲存-高階儲存K8S
- Kubernetes 使用 ceph-csi 消費 RBD 作為持久化儲存持久化
- Oracle 共享儲存掛載Oracle
- k8s網路外掛K8S
- k8s StorageClass 儲存類K8S
- k8s資料儲存K8S
- K8S之Volume儲存K8S
- K8S Calico網路外掛K8S
- vscode-rustfmt:Rust儲存時自動格式化的Vscode外掛VSCodeRust
- 在K8S中,CSI模型有哪些?K8S模型
- k8s~envoy上新增wasm外掛K8SASM
- Typecho COS外掛實現網站靜態資源儲存到COS,降低本地儲存負載網站負載
- k8s動態儲存篇--NFSK8SNFS
- k8s使用rbd作為儲存K8S
- 如何配置K8S儲存叢集?K8S
- 容器服務 TKE 儲存外掛與雲硬碟 CBS 最佳實踐應用硬碟
- 使用VsCode的遠端儲存庫外掛不用克隆下載Github專案VSCodeGithub
- k8s與各網路外掛整合K8S
- 在K8S中,共享儲存的作用?K8S
- Kubernetes K8S之儲存ConfigMap詳解K8S
- 物件儲存服務OBS obsfs掛載物件