Kubernetes CSI簡介
官方spec文件: CSI Spec
術語簡稱:
- CSI:Container Storage Interface
- CO:Container Orchestrator system
- SP:Storage Provider
CSI是 Container Storage Interface
的簡稱,旨在能為容器編排引擎和儲存系統間建立一套標準的儲存呼叫介面,通過該介面能為容器編排引擎提供儲存服務。
在CSI之前,K8S裡提供儲存服務是通過一種稱為 in-tree
的方式來提供,如下圖:
這種方式需要將儲存提供者的程式碼邏輯放到K8S的程式碼庫中執行,呼叫引擎與外掛間屬於強耦合,這種方式會帶來一些問題:
- 儲存外掛需要一同隨K8S釋出。
- K8S社群需要對儲存外掛的測試、維護負責。
- 儲存外掛的問題有可能會影響K8S部件正常執行。
- 儲存外掛享有K8S部件同等的特權存在安全隱患。
- 儲存外掛開發者必須遵循K8S社群的規則開發程式碼。
當前已有的 FlexVolume
機制試圖通過呼叫一個可執行的程式包方式去解決這些問題,雖然它已經能夠做到讓儲存提供方進行獨立開發,但是有兩個問題還沒有得到很好解決:
-
在部署這些可執行檔案時,需要host的root許可權,依然存在安全隱患。
-
儲存外掛在執行mount、attach這類操作時,往往需要到host去安裝第三方工具或者載入一些依賴庫,這樣host的OS版本往往需要定製,不再是一個簡單的linux髮型版本,這樣的情況太多,會使部署變得複雜。
例如:ceph需要安裝rbd,gluster mount需要安裝mount.glusterfs等等。
基於這些問題和挑戰,CO(Container Orchestrator) 廠商提出 Container Storage Interface 用來定義容器儲存標準,它獨立於 Kubernetes Storage SIG,由 Kubernetes、Mesos、Cloud Foundry 三家一起推動。個人理解它有如下2個核心目標:
- 提供統一的 CO 和 SP 都遵循的容器儲存介面
- 基於 CSI 實現了自身的 Volume Driver,即可在所有支援 CSI 的 CO 中平滑遷移
CSI 持久化卷支援是在 Kubernetes v1.9 中引入的,作為一個 alpha 特性,必須由叢集管理員明確啟用。在kubernetes v1.10中,預設CSI是啟用的。
Kubernetes CSI架構
Kubernetes的CSI架構如下,包含三個部分:
- Kubernetes Core:Kubernetes核心元件
- Kubernetes External Component:Kubernetes支援CSI的擴充套件元件
- External Component:第三方儲存廠商開發
Kubernetes External Component
在CSI的框架裡,Kubernetes本身需要提供以下三類外掛:
- Driver registrar
一個Sidecar Container,向Kubernetes註冊CSI Driver,新增Drivers的一些資訊。
原始碼: https://github.com/kubernetes-csi/driver-registrar
- External provisioner
一個Sidecar Container,監控Kubernetes系統裡的PVC物件,呼叫對應CSI的Volume建立、刪除等介面。
原始碼: https://github.com/kubernetes-csi/external-provisioner
- External attacher
一個Sidecar Container,監控Kubernetes系統裡的VolumeAttachment物件,呼叫對應CSI的介面。
原始碼: https://github.com/kubernetes-csi/external-attacher
K8S CSI擴充套件元件的映象下載:
https://quay.io/organization/k8scsiStorage Vendor external component
在CSI的框架中,儲存提供商需要自己實現CSI標準裡的儲存外掛,主要有如下三類:
- CSI Identity
負責認證外掛的狀態資訊,實現如下介面:
service Identity { rpc GetPluginInfo(GetPluginInfoRequest) returns (GetPluginInfoResponse) {} rpc GetPluginCapabilities(GetPluginCapabilitiesRequest) returns (GetPluginCapabilitiesResponse) {} rpc Probe (ProbeRequest) returns (ProbeResponse) {} }
- CSI Controller
負責實際建立和管理volumes,實現如下介面:
service Controller { rpc CreateVolume (CreateVolumeRequest) returns (CreateVolumeResponse) {} rpc DeleteVolume (DeleteVolumeRequest) returns (DeleteVolumeResponse) {} rpc ControllerPublishVolume (ControllerPublishVolumeRequest) returns (ControllerPublishVolumeResponse) {} rpc ControllerUnpublishVolume (ControllerUnpublishVolumeRequest) returns (ControllerUnpublishVolumeResponse) {} rpc ValidateVolumeCapabilities (ValidateVolumeCapabilitiesRequest) returns (ValidateVolumeCapabilitiesResponse) {} rpc ListVolumes (ListVolumesRequest) returns (ListVolumesResponse) {} rpc GetCapacity (GetCapacityRequest) returns (GetCapacityResponse) {} rpc ControllerGetCapabilities (ControllerGetCapabilitiesRequest) returns (ControllerGetCapabilitiesResponse) {} }
- CSI Node
負責在Kubernetes Node上volume相關的功能,實現如下介面:
service Node { rpc NodeStageVolume (NodeStageVolumeRequest) returns (NodeStageVolumeResponse) {} rpc NodeUnstageVolume (NodeUnstageVolumeRequest) returns (NodeUnstageVolumeResponse) {} rpc NodePublishVolume (NodePublishVolumeRequest) returns (NodePublishVolumeResponse) {} rpc NodeUnpublishVolume (NodeUnpublishVolumeRequest) returns (NodeUnpublishVolumeResponse) {} rpc NodeGetId (NodeGetIdRequest) returns (NodeGetIdResponse) {} rpc NodeGetCapabilities (NodeGetCapabilitiesRequest) returns (NodeGetCapabilitiesResponse) {} }
CSI Drivers
當前已經有好多可以在Kubernetes中使用的CSI Drivers,參考:
https://kubernetes-csi.github.io/docs/Drivers.html開發CSI Driver
SP要開發一個CSI Driver,要遵循以下三條:
- 實現CSI介面的功能
- 實現CSI介面的冪等性
- 符合CSI返回值規範
SP實現CSI Driver
實現的CSI Driver元件有三個:
- Identity Plugin
- Controller Plugin
- Node Plugin
結合這三個元件的功能,開發者有兩種選擇:
1、兩個執行檔案:Controller + Identity,Node + Identity
2、一個執行檔案:Controller + Node + Identity
從調研結果看,官方推薦使用第二種:Controller + Node + Identity
優點:一個可執行檔案,方便維護和部署
部署CSI元件
如上介紹,Controller負責volumes的建立刪除等操作,整個叢集只需要部署一個;Node負責volume的attach、detach等操作,需要在每個節點部署一個,則Kubernetes的推薦部署方式為:
- Controller plugin:StatefulSet,replicas設為1
- Node plugin:DaemonSet
部署Kubernetes擴充套件元件
單純的開發完CSI Driver後,還需要CO提供對應的擴充套件外掛來支援CSI,這樣CO系統才能正常的使用CSI的方式呼叫儲存。
在Kubernetes系統裡,擴容外掛和CSI的部署可以按照如下方式:
Ceph SP Example
Ceph提供了RBD和CephFS兩種CSI介面的儲存。
原始碼: https://github.com/ceph/ceph-csi
Docker image:
https://quay.io/organization/cephcsiCeph CSI部署
Controller Plugin在哪裡?
在各個元件的plugin裡!(rbdplugin/cephfsplugin)
疑問:PVName與RBD Image名字是否一致?
問題: https://github.com/kubernetes/kubernetes/issues/69324
在之前的external-storage的程式碼裡,Ceph RBD Image名字與PVName完全無關聯,帶來了不必要的查詢麻煩,那新的CSI框架下,這兩個名字是否一致呢?
ceph-csi的實現
檔案:pkg/rbd/controllerserver.go
func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) { ... // Generating Volume Name and Volume ID, as according to CSI spec they MUST be different volName := req.GetName() uniqueID := uuid.NewUUID().String() if len(volName) == 0 { volName = rbdVol.Pool + "-dynamic-pvc-" + uniqueID } rbdVol.VolName = volName ... }
從ceph-csi的rbd實現看,volName是否是隨機UUID跟CreateVolume傳入的引數相關。
Kubernetes擴充套件元件實現
檔案:pkg/controller/controller.go
func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) { ... pvName, err := makeVolumeName(p.volumeNamePrefix, fmt.Sprintf("%s", options.PVC.ObjectMeta.UID), p.volumeNameUUIDLength) ... }
檔案:cmd/csi-provisioner/csi-provisioner.go
var ( ... volumeNamePrefix= flag.String("volume-name-prefix", "pvc", "Prefix to apply to the name of a created volume.") volumeNameUUIDLength = flag.Int("volume-name-uuid-length", -1, "Truncates generated UUID of a created volume to this length. Defaults behavior is to NOT truncate.") ... )
從這裡可以看出,Kubernetes的Controller元件實現裡,支援啟動時候指定:
pvc -1
所以在CSI的框架下,預設Kubernetes的Controller元件會傳入Volume Name,底層Ceph RBD CSI實現中也會檢查這個Name,如果設定的話就會依據它來建立RBD Image。