kubernetes/k8s CSI分析-容器儲存介面分析

良凱爾 發表於 2021-07-24
k8s Kubernetes

更多 k8s CSI 的分析,可以檢視這篇部落格kubernetes ceph-csi分析,以 ceph-csi 為例,做了詳細的原始碼分析。

概述

kubernetes的設計初衷是支援可插拔架構,從而利於擴充套件kubernetes的功能。在此架構思想下,kubernetes提供了3個特定功能的介面,分別是容器網路介面CNI、容器執行時介面CRI和容器儲存介面CSIkubernetes通過呼叫這幾個介面,來完成相應的功能。

下面我們來對容器儲存介面CSI來做一下介紹與分析。

在本文中,會對CSI是什麼、為什麼要有CSICSI系統架構做一下介紹,然後對CSI所涉及的k8s物件與元件進行了簡單的介紹,以及k8sCSI儲存進行相關操作的流程分析,儲存相關操作包括了儲存建立、儲存擴容、儲存掛載、解除儲存掛載以及儲存刪除操作。

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將儲存體系抽象出了外部儲存元件介面即CSIkubernetes通過grpc介面與第三方儲存廠商的儲存卷外掛系統進行通訊。

這樣一來,對於第三方儲存廠商來說,既可以單獨釋出和部署自己的儲存外掛,進行正常迭代,而又無需接觸kubernetes核心程式碼,降低了開發的複雜度。同時,對於kubernetes來說,這樣不僅降低了自身的維護成本,還能為使用者提供更多的儲存選項。

CSI系統架構

這是一張k8s csi的系統架構圖,圖中所畫的元件以及k8s物件,接下來會一一進行分析。

kubernetes/k8s CSI分析-容器儲存介面分析

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

涉及元件與作用

kubernetes/k8s CSI分析-容器儲存介面分析

下面來介紹下涉及的元件與作用。

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的流程

kubernetes/k8s CSI分析-容器儲存介面分析

(1)使用者建立pvc
(2)PV controller watch到pvc的建立,尋找合適的pv與之繫結。
(3)(4)當找不到合適的pv時,將呼叫volume plugin來建立volume,並建立pv物件,之後該pv物件與pvc物件繫結。
(5)使用者建立掛載pvcpod
(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為例。

kubernetes/k8s CSI分析-容器儲存介面分析

(1)使用者建立pvc
(2)PV controller watch到pvc的建立,尋找合適的pv與之繫結。當尋找不到合適的pv時,將更新pvc物件,新增annotationvolume.beta.kubernetes.io/storage-provisioner,讓external-provisioner元件開始開始建立儲存與pv物件的操作。
(3)external-provisioner元件watch到pvc的建立/更新事件,判斷annotationvolume.beta.kubernetes.io/storage-provisioner的值,即判斷是否是自己來負責做建立操作,是則呼叫csi-plugin ControllerServer來建立儲存,並建立pv物件(這裡的pv物件使用了提前繫結特性,將pvc資訊填入了pv物件的spec.claimRef屬性)。
(4)PV controller將上一步建立的pvpvc繫結。
(5)使用者建立掛載pvcpod
(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 pluginceph-csiexternal-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. 儲存建立

流程圖

kubernetes/k8s CSI分析-容器儲存介面分析

流程分析

(1)使用者建立pvc物件;

(2)pv controller監聽pvc物件,尋找現存的合適的pv物件,與pvc物件繫結。當找不到現存合適的pv物件時,將更新pvc物件,新增annotationvolume.beta.kubernetes.io/storage-provisioner,讓external-provisioner元件開始開始建立儲存與pv物件的操作;當找到時,將pvcpv繫結,結束操作。

(3)external-provisioner元件監聽到pvc的新增事件,判斷pvcannotationvolume.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. 儲存擴容

流程圖

kubernetes/k8s CSI分析-容器儲存介面分析

流程分析

(1)修改pvc物件,修改申請儲存大小(pvc.spec.resources.requests.storage);

(2)修改成功後,external-resizer監聽到該pvcupdate事件,發現pvc.Spec.Resources.Requests.storgagepvc.Status.Capacity.storgage大,於是調ceph-csi元件進行 controller端擴容;

(3)ceph-csi元件呼叫ceph儲存,進行底層儲存擴容;

(4)底層儲存擴容完成後,ceph-csi元件更新pv物件的.Spec.Capacity.storgage的值為擴容後的儲存大小;

(5)kubeletvolume managerreconcile()調諧過程中發現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=trueAttach/Detach controller進行Attach/Detach 操作。
kubernetes/k8s CSI分析-容器儲存介面分析

(2)kubelet啟動引數--enable-controller-attach-detach=falsekubeletvolume manager進行Attach/Detach 操作。
kubernetes/k8s CSI分析-容器儲存介面分析

流程分析

(1)使用者建立一個掛載了pvcpod

(2)AD controllervolume 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元件的NodeStageVolumeNodePublishVolume完成儲存的掛載。

4. 解除儲存掛載

流程圖

(1)kubelet啟動引數--enable-controller-attach-detach=trueAttach/Detach controller進行Attach/Detach 操作。
kubernetes/k8s CSI分析-容器儲存介面分析

(2)kubelet啟動引數--enable-controller-attach-detach=falsekubeletvolume manager進行Attach/Detach 操作。
kubernetes/k8s CSI分析-容器儲存介面分析

流程分析

(1)使用者刪除宣告瞭pvcpod

(2)AD controllervolume manager中的reconcile()發現有volume未執行dettach操作,於是進行dettach操作,即刪除VolumeAttachment物件;

(3)AD controllervolume manager等待VolumeAttachment物件刪除成功;

(4)AD controller更新node物件的.Status.VolumesAttached屬性值,將標記為attached的該volume從屬性值中去除;

(5)kubelet中的volume manager獲取node.Status.VolumesAttached屬性值,找不到相關的volume資訊;

(6)於是volume manager中的reconcile()呼叫ceph-csi元件的NodeUnpublishVolumeNodeUnstageVolume完成儲存的解除掛載操作。

5. 刪除儲存

流程圖

kubernetes/k8s CSI分析-容器儲存介面分析

流程分析

(1)使用者刪除pvc物件;

(2)pv controller發現與pv繫結的pvc物件被刪除,於是更新pv的狀態為released

(3)external-provisioner watch到pv更新事件,並檢查pv的狀態是否為released,以及回收策略是否為delete

(4)確認了pv物件的狀態以及回收策略之後,接下來external-provisioner元件會呼叫ceph-csiDeleteVolume來刪除儲存;

(5)ceph-csi元件的DeleteVolume方法,呼叫ceph叢集命令,刪除底層儲存;

(6)刪除底層儲存後,external-provisioner元件刪除pv物件。

總結

CSI即Container Storage Interface(容器儲存介面)。

為了解決第三方儲存廠商的儲存卷外掛程式碼整合到kubernetes程式碼中所帶來的各種問題,kubernetes將儲存體系抽象出了外部儲存元件介面即CSIkubernetes通過grpc介面與第三方儲存廠商的儲存卷外掛系統進行通訊,來操作儲存,從而提供容器儲存服務。

這樣一來,對於第三方儲存廠商來說,既可以單獨釋出和部署自己的儲存外掛,進行正常迭代,而又無需接觸kubernetes核心程式碼,降低了開發的複雜度。同時,對於kubernetes來說,這樣不僅降低了自身的維護成本,還能為使用者提供更多的儲存選項。

最後,再來回顧一下kubernetes CSI的架構。
kubernetes/k8s CSI分析-容器儲存介面分析

更多 k8s CSI 的分析,可以檢視這篇部落格kubernetes ceph-csi分析,以 ceph-csi 為例,做了詳細的原始碼分析。