更多 k8s CSI 的分析,可以檢視這篇部落格kubernetes ceph-csi分析,以 ceph-csi 為例,做了詳細的原始碼分析。
概述
kubernetes的設計初衷是支援可插拔架構,從而利於擴充套件kubernetes
的功能。在此架構思想下,kubernetes
提供了3個特定功能的介面,分別是容器網路介面CNI
、容器執行時介面CRI
和容器儲存介面CSI
。kubernetes
通過呼叫這幾個介面,來完成相應的功能。
下面我們來對容器儲存介面CSI
來做一下介紹與分析。
在本文中,會對CSI
是什麼、為什麼要有CSI
、CSI
系統架構做一下介紹,然後對CSI
所涉及的k8s
物件與元件進行了簡單的介紹,以及k8s
對CSI
儲存進行相關操作的流程分析,儲存相關操作包括了儲存建立、儲存擴容、儲存掛載、解除儲存掛載以及儲存刪除操作。
CSI是什麼
CSI是Container Storage Interface
(容器儲存介面)的簡寫。
CSI的目的是定義行業標準“容器儲存介面”,使儲存供應商(SP)能夠開發一個符合CSI標準的外掛並使其可以在多個容器編排(CO)系統中工作。CO包括Cloud Foundry
, Kubernetes
, Mesos
等。
kubernetes將通過CSI
介面來跟第三方儲存廠商進行通訊,來操作儲存,從而提供容器儲存服務。
為什麼要有CSI
其實在沒有CSI
之前kubernetes
就已經提供了強大的儲存卷外掛系統,但是這些外掛系統實現是kubernetes
程式碼的一部分,需要隨kubernetes
元件二進位制檔案一起釋出,這樣就會存在一些問題。
(1)如果第三方儲存廠商發現有問題需要修復或者優化,即使修復後也不能單獨釋出,需要與kubernetes
一起釋出,對於k8s
本身而言,不僅要考慮自身的正常迭代發版,還需要考慮到第三方儲存廠商的迭代發版,這裡就存在雙方互相依賴、制約的問題,不利於雙方快速迭代;
(2)另外第三方廠商的程式碼跟kubernetes
程式碼耦合在一起,還會引起安全性、可靠性問題,還增加了kubernetes
程式碼的複雜度以及後期的維護成本等等。
基於以上問題,kubernetes
將儲存體系抽象出了外部儲存元件介面即CSI
,kubernetes
通過grpc
介面與第三方儲存廠商的儲存卷外掛系統進行通訊。
這樣一來,對於第三方儲存廠商來說,既可以單獨釋出和部署自己的儲存外掛,進行正常迭代,而又無需接觸kubernetes
核心程式碼,降低了開發的複雜度。同時,對於kubernetes
來說,這樣不僅降低了自身的維護成本,還能為使用者提供更多的儲存選項。
CSI系統架構
這是一張k8s csi的系統架構圖,圖中所畫的元件以及k8s物件,接下來會一一進行分析。
CSI相關元件一般採用容器化部署,減少環境依賴。
涉及k8s物件
1. PersistentVolume
持久儲存卷,叢集級別資源,代表了儲存卷資源,記錄了該儲存卷資源的相關資訊。
回收策略
(1)retain:保留策略,當刪除pvc的時候,保留pv與外部儲存資源。
(2)delete:刪除策略,當與pv繫結的pvc被刪除的時候,會從k8s叢集中刪除pv物件,並執行外部儲存資源的刪除操作。
(3)resycle(已廢棄)
pv狀態遷移
available --> bound --> released
2. PersistentVolumeClaim
持久儲存卷宣告,namespace級別資源,代表了使用者對於儲存卷的使用需求宣告。
示例:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test
namespace: test
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: csi-cephfs-sc
volumeMode: Filesystem
pvc狀態遷移
pending --> bound
3. StorageClass
定義了建立pv的模板資訊,叢集級別資源,用於動態建立pv。
示例:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: csi-rbd-sc
parameters:
clusterID: ceph01
imageFeatures: layering
imageFormat: "2"
mounter: rbd
pool: kubernetes
provisioner: rbd.csi.ceph.com
reclaimPolicy: Delete
volumeBindingMode: Immediate
4. VolumeAttachment
VolumeAttachment 記錄了pv的相關掛載資訊,如掛載到哪個node節點,由哪個volume plugin來掛載等。
AD Controller 建立一個 VolumeAttachment,而 External-attacher 則通過觀察該 VolumeAttachment,根據其狀態屬性來進行儲存的掛載和解除安裝操作。
示例:
apiVersion: storage.k8s.io/v1
kind: VolumeAttachment
metadata:
name: csi-123456
spec:
attacher: cephfs.csi.ceph.com
nodeName: 192.168.1.10
source:
persistentVolumeName: pvc-123456
status:
attached: true
5. CSINode
CSINode 記錄了csi plugin的相關資訊(如nodeId、driverName、拓撲資訊等)。
當Node Driver Registrar向kubelet註冊一個csi plugin後,會建立(或更新)一個CSINode物件,記錄csi plugin的相關資訊。
示例:
apiVersion: storage.k8s.io/v1
kind: CSINode
metadata:
name: 192.168.1.10
spec:
drivers:
- name: cephfs.csi.ceph.com
nodeID: 192.168.1.10
topologyKeys: null
- name: rbd.csi.ceph.com
nodeID: 192.168.1.10
topologyKeys: null
涉及元件與作用
下面來介紹下涉及的元件與作用。
1. volume plugin
擴充套件各種儲存型別的卷的管理能力,實現第三方儲存的各種操作能力與k8s儲存系統的結合。呼叫第三方儲存的介面或命令,從而提供資料卷的建立/刪除、attach/detach、mount/umount的具體操作實現,可以認為是第三方儲存的代理人。前面分析元件中的對於資料卷的建立/刪除、attach/detach、mount/umount操作,全是呼叫volume plugin來完成。
根據原始碼所在位置,volume plugin分為in-tree與out-of-tree。
in-tree
在k8s原始碼內部實現,和k8s一起釋出、管理,更新迭代慢、靈活性差。
out-of-tree
程式碼獨立於k8s,由儲存廠商實現,有csi、flexvolume兩種實現。
csi plugin
csi plugin分為ControllerServer與NodeServer,各負責不同的儲存操作。
external plugin
external plugin包括了external-provisioner、external-attacher、external-resizer、external-snapshotter等,external plugin輔助csi plugin元件,共同完成了儲存相關操作。external plugin負責watch pvc、volumeAttachment等物件,然後呼叫volume plugin來完成儲存的相關操作。如external-provisioner watch pvc物件,然後呼叫csi plugin來建立儲存,最後建立pv物件;external-attacher watch volumeAttachment物件,然後呼叫csi plugin來做attach/dettach操作;external-resizer watch pvc物件,然後呼叫csi plugin來做儲存的擴容操作等。
Node-Driver-Registrar
Node-Driver-Registrar元件負責實現csi plugin(NodeServer)的註冊,讓kubelet感知csi plugin的存在。
元件部署方式
csi plugin controllerServer與external plugin作為容器,使用deployment部署,多副本可實現高可用;而csi plugin NodeServer與Node-Driver-Registrar作為容器,使用daemonset部署,即每個node節點都有。
2. kube-controller-manager
PV controller
負責pv、pvc的繫結與生命週期管理(如建立/刪除底層儲存,建立/刪除pv物件,pv與pvc物件的狀態變更)。
(1)in-tree:建立/刪除底層儲存、建立/刪除pv物件的操作,由PV controller呼叫volume plugin(in-tree)來完成。
(2)out-tree CSI:建立/刪除底層儲存、建立/刪除pv物件的操作由external-provisioner與csi plugin共同來完成。
AD controller
AD Cotroller全稱Attachment/Detachment 控制器,主要負責建立、刪除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來完成。
3. kubelet
volume manager
主要是管理卷的Attach/Detach(與AD controller作用相同,通過kubelet啟動引數控制哪個元件來做該操作)、mount/umount等操作。
對於csi來說,volume manager的Attach/Detach操作只建立/刪除VolumeAttachment物件,而不會真正的將資料卷掛載到節點/從節點上解除掛載;csi-attacer元件也不會做掛載/解除掛載操作,只是更新VolumeAttachment物件,真正的節點儲存掛載/解除掛載操作由kubelet中volume manager呼叫呼叫csi plugin來完成。
kubernetes建立與掛載volume(in-tree volume plugin)
先來看下kubernetes通過in-tree volume plugin來建立與掛載volume的流程
(1)使用者建立pvc
;
(2)PV controller
watch到pvc
的建立,尋找合適的pv
與之繫結。
(3)(4)當找不到合適的pv
時,將呼叫volume plugin
來建立volume,並建立pv
物件,之後該pv
物件與pvc
物件繫結。
(5)使用者建立掛載pvc
的pod
;
(6)kube-scheduler
watch到pod
的建立,為其尋找合適的node排程。
(7)(8)pod
排程完成後,AD controller
/volume manager
watch到pod
宣告的volume沒有進行attach
操作,將呼叫volume plugin
來做attach
操作。
(9)volume plugin
進行attach
操作,將volume掛載到pod
所在node節點,成為如/dev/vdb
的裝置。
(10)(11)attach
操作完成後,volume manager
watch到pod
宣告的volume沒有進行mount
操作,將呼叫volume plugin
來做mount
操作。
(12)volume plugin
進行mount
操作,將node節點上的第(9)步得到的/dev/vdb
裝置掛載到指定目錄。
kubernetes建立與掛載volume(out-of-tree volume plugin)
再來看下kubernetes通過out-of-tree volume plugin來建立與掛載volume的流程,以csi-plugin為例。
(1)使用者建立pvc
;
(2)PV controller
watch到pvc
的建立,尋找合適的pv
與之繫結。當尋找不到合適的pv
時,將更新pvc
物件,新增annotation
:volume.beta.kubernetes.io/storage-provisioner
,讓external-provisioner
元件開始開始建立儲存與pv
物件的操作。
(3)external-provisioner
元件watch到pvc
的建立/更新事件,判斷annotation
:volume.beta.kubernetes.io/storage-provisioner
的值,即判斷是否是自己來負責做建立操作,是則呼叫csi-plugin ControllerServer
來建立儲存,並建立pv
物件(這裡的pv
物件使用了提前繫結特性,將pvc
資訊填入了pv
物件的spec.claimRef
屬性)。
(4)PV controller
將上一步建立的pv
與pvc
繫結。
(5)使用者建立掛載pvc
的pod
;
(6)kube-scheduler
watch到pod
的建立,為其尋找合適的node
排程。
(7)(8)pod
排程完成後,AD controller
/volume manager
watch到pod
宣告的volume沒有進行attach
操作,將呼叫csi-attacher
來做attach
操作(實際上只是建立volumeAttachement
物件)。
(9)external-attacher
元件watch到volumeAttachment
物件的新建,呼叫csi-plugin
進行attach
操作(如果volume plugin
是ceph-csi
,external-attacher
元件watch到volumeAttachment
物件的新建後,只是修改該物件的狀態屬性,不會做attach
操作,真正的attach
操作由kubelet
中的volume manager
呼叫volume plugin
ceph-csi
來完成)。
(10)csi-plugin ControllerServer
進行attach
操作,將volume掛載到pod
所在node
節點,成為如/dev/vdb
的裝置。
(11)(12)attach
操作完成後,volume manager
watch到pod
宣告的volume沒有進行mount
操作,將呼叫csi-mounter
來做mount
操作。
(13)csi-mounter
呼叫csi-plugin NodeServer
進行mount
操作,將node節點上的第(10)步得到的/dev/vdb
裝置掛載到指定目錄。
kubernetes儲存相關操作流程具體分析(out-of-tree volume plugin,以csi plugin:ceph-csi為例)
下面來看下kubernetes
通過ceph-csi volume plugin
來建立/刪除、掛載/解除掛載ceph儲存的流程。
1. 儲存建立
流程圖
流程分析
(1)使用者建立pvc
物件;
(2)pv controller
監聽pvc
物件,尋找現存的合適的pv
物件,與pvc
物件繫結。當找不到現存合適的pv
物件時,將更新pvc
物件,新增annotation
:volume.beta.kubernetes.io/storage-provisioner
,讓external-provisioner
元件開始開始建立儲存與pv
物件的操作;當找到時,將pvc
與pv
繫結,結束操作。
(3)external-provisioner
元件監聽到pvc
的新增事件,判斷pvc
的annotation
:volume.beta.kubernetes.io/storage-provisioner
的值,即判斷是否是自己來負責做建立操作,是則呼叫ceph-csi
元件進行儲存的建立;
(4)ceph-csi
元件呼叫ceph建立底層儲存;
(5)底層儲存建立完成後,external-provisioner
根據儲存資訊,拼接pv
物件,建立pv
物件(這裡的pv
物件使用了提前繫結特性,將pvc
資訊填入了pv
物件的spec.claimRef
屬性);
(6)pv controller
監聽pvc
物件,將第(5)步建立的pv
物件,與pvc
物件繫結。
2. 儲存擴容
流程圖
流程分析
(1)修改pvc
物件,修改申請儲存大小(pvc.spec.resources.requests.storage
);
(2)修改成功後,external-resizer
監聽到該pvc
的update
事件,發現pvc.Spec.Resources.Requests.storgage
比pvc.Status.Capacity.storgage
大,於是調ceph-csi
元件進行 controller端擴容;
(3)ceph-csi
元件呼叫ceph儲存,進行底層儲存擴容;
(4)底層儲存擴容完成後,ceph-csi
元件更新pv
物件的.Spec.Capacity.storgage
的值為擴容後的儲存大小;
(5)kubelet
的volume manager
在reconcile()
調諧過程中發現pv.Spec.Capacity.storage
大於pvc.Status.Capacity.storage
,於是調ceph-csi
元件進行 node端擴容;
(6)ceph-csi
元件對node上儲存對應的檔案系統擴容;
(7)擴容完成後,kubelet
更新pvc.Status.Capacity.storage
的值為擴容後的儲存大小。
3. 儲存掛載
流程圖
kubelet啟動引數--enable-controller-attach-detach
,該啟動引數設定為 true
表示啟用 Attach/Detach controller
進行Attach/Detach
操作,同時禁用 kubelet
執行 Attach/Detach
操作(預設值為 true
)。實際上Attach/Detach
操作就是建立/刪除VolumeAttachment
物件。
(1)kubelet
啟動引數--enable-controller-attach-detach=true
,Attach/Detach controller
進行Attach/Detach
操作。
(2)kubelet
啟動引數--enable-controller-attach-detach=false
,kubelet
端volume manager
進行Attach/Detach
操作。
流程分析
(1)使用者建立一個掛載了pvc
的pod
;
(2)AD controller
或volume manager
中的reconcile()
發現有volume
未執行attach
操作,於是進行attach
操作,即建立VolumeAttachment
物件;
(3)external-attacher
元件list/watch
VolumeAttachement
物件,更新VolumeAttachment.status.attached=true
;
(4)AD controller
更新node
物件的.Status.VolumesAttached
屬性值,將該volume
記為attached
;
(5)kubelet
中的volume manager
獲取node.Status.VolumesAttached
屬性值,發現volume
已被標記為attached
;
(6)於是volume manager
中的reconcile()
呼叫ceph-csi
元件的NodeStageVolume
與NodePublishVolume
完成儲存的掛載。
4. 解除儲存掛載
流程圖
(1)kubelet
啟動引數--enable-controller-attach-detach=true
,Attach/Detach controller
進行Attach/Detach
操作。
(2)kubelet
啟動引數--enable-controller-attach-detach=false
,kubelet
端volume manager
進行Attach/Detach
操作。
流程分析
(1)使用者刪除宣告瞭pvc
的pod
;
(2)AD controller
或volume manager
中的reconcile()
發現有volume
未執行dettach
操作,於是進行dettach
操作,即刪除VolumeAttachment
物件;
(3)AD controller
或volume manager
等待VolumeAttachment
物件刪除成功;
(4)AD controller
更新node
物件的.Status.VolumesAttached
屬性值,將標記為attached
的該volume
從屬性值中去除;
(5)kubelet
中的volume manager
獲取node.Status.VolumesAttached
屬性值,找不到相關的volume
資訊;
(6)於是volume manager
中的reconcile()
呼叫ceph-csi
元件的NodeUnpublishVolume
與NodeUnstageVolume
完成儲存的解除掛載操作。
5. 刪除儲存
流程圖
流程分析
(1)使用者刪除pvc
物件;
(2)pv controller
發現與pv
繫結的pvc
物件被刪除,於是更新pv
的狀態為released
;
(3)external-provisioner
watch到pv
更新事件,並檢查pv
的狀態是否為released
,以及回收策略是否為delete
;
(4)確認了pv
物件的狀態以及回收策略之後,接下來external-provisioner
元件會呼叫ceph-csi
的DeleteVolume
來刪除儲存;
(5)ceph-csi
元件的DeleteVolume
方法,呼叫ceph叢集命令,刪除底層儲存;
(6)刪除底層儲存後,external-provisioner
元件刪除pv
物件。
總結
CSI即Container Storage Interface
(容器儲存介面)。
為了解決第三方儲存廠商的儲存卷外掛程式碼整合到kubernetes程式碼中所帶來的各種問題,kubernetes
將儲存體系抽象出了外部儲存元件介面即CSI
,kubernetes
通過grpc
介面與第三方儲存廠商的儲存卷外掛系統進行通訊,來操作儲存,從而提供容器儲存服務。
這樣一來,對於第三方儲存廠商來說,既可以單獨釋出和部署自己的儲存外掛,進行正常迭代,而又無需接觸kubernetes
核心程式碼,降低了開發的複雜度。同時,對於kubernetes
來說,這樣不僅降低了自身的維護成本,還能為使用者提供更多的儲存選項。
最後,再來回顧一下kubernetes CSI的架構。
更多 k8s CSI 的分析,可以檢視這篇部落格kubernetes ceph-csi分析,以 ceph-csi 為例,做了詳細的原始碼分析。