容器化 RDS:藉助 CSI 擴充套件 Kubernetes 儲存能力

沃趣科技發表於2018-06-15
容器化RDS系列文章:


RDS 並不是新生事物,新鮮的是通過容器技術和容器編排技術構建 RDS。對金融客戶而言,他們有強烈擁抱 Docker 和 Kubernetes 的願望,但可用性是嘗試新技術的前提。儲存是持久化應用的關鍵資源,它並不性感,卻是 Monolithic 應用走向 Cloud-Native 架構的關鍵。Kubernetes 儲存子系統已經非常強大,但是還欠缺一些基礎功能,譬如支援 Expand Volume(部分 Storage Vendor 支援)和 SnapShot。本文嘗試從我們的實現分享如下幾個內容:
  • 現有 Kubernetes 儲存外掛系統問題

  • Container Storage Interface(CSI)

  • 基於CSI 和分散式檔案系統實現在 MySQL 的 Volume 動態擴充套件

  • 對 CSI 的展望


名詞說明:
原名 簡稱
容器編排系統 CO.
儲存提供者 SP.
儲存外掛介面 Volume Plugin Interface
儲存驅動 Volume Driver

容器儲存介面

Container Storage Interface

CSI

如有遺漏,不吝賜教。

現有 Kubernetes 儲存外掛系統問題

可供選的容器編排系統(後面簡稱 CO.)不少,除去 Kubernetes 還有 Mesos、Swarm、Cloud Foundry。以 Kubernetes 為例,其通過 PersistentVolume 抽象對以下儲存的支援:

  • GCEPersistentDisk

  • AWSElasticBlockStore

  • AzureFile

  • AzureDisk

  • FC(Fibre Channel)**

  • FlexVolume

  • Flocker

  • NFS

  • iSCSI

  • RBD(Ceph Block Device)

  • CephFS

  • Cinder(OpenStack block storage)

  • GlusterFS

  • VsphereVolume

  • Quobyte Volumes

  • HostPath

  • VMware Photon

  • Portworx Volumes

  • ScaleIO Volumes

  • StorageOS


不可謂不豐富。Kubernetes 以外掛化的方式支援儲存廠商(後面簡稱 SP.)。SP. 通過實現 Kubernetes 儲存外掛介面(後面簡稱 Volume Plugin Interface)的方式提供自己的儲存驅動(後面簡稱 Volume Driver)。
  • VolumePlugin

  • PersistentVolumePlugin

  • DeletableVolumePlugin

  • ProvisionableVolumePlugin

  • ExpandableVolumePlugin

  • Provisioner

  • Deleter


以上介面並不需要全部實現,其中 VolumePlugin[1] 是必須實現的介面。
系統架構圖如下:


這種方式為 Kubernetes 提供了豐富的儲存支援列表,但是在具體實現上,SP. Volume Driver 程式碼也在 Kubernetes 程式碼倉庫(又叫in-tree),它帶來幾個顯著的問題。
從 Kubernetes 的角度看:
  • 需要在 Kubernetes 中給各個 SP. 賦權以便他們能夠提交程式碼到倉庫;

  • Volume Driver 由各個 SP. 提供,Kubernetes 的開發者並不瞭解每個細節,導致這些程式碼難於維護和測試;

  • Kubernetes 的釋出節奏和各位 SP. Volume Driver 的節奏並不一致,隨著支援的 SP. 增多,溝通、維護、測試成本會越來越高;

  • 這些 SP. Volume Driver 並不是 Kubernetes 本身需要的。


從 SP. 的角度看:
  • 提交一個新特性或者修復 bug,都需要提交程式碼到 Kubernetes 倉庫,在本地編譯 Kubernetes 的都知道,這個過程是很痛苦的,這對 SP. 而言是完全不必要的成本。


一個統一的,大家認可的容器儲存介面越來越有必要。

Container Storage Interface(CSI)

基於這些問題和挑戰,CO 廠商提出 Container Storage Interface 用來定義容器儲存標準,它獨立於 Kubernetes Storage SIG,由 Kubernetes、Mesos、Cloud Foundry 三家一起推動。個人理解它有如下2個核心目標:

  • 提供統一的 CO. 和 SP. 都遵循的容器儲存介面。

  • 一旦 SP. 基於 CSI 實現了自身的 Volume Driver,即可在所有支援 CSI 的 CO 中平滑遷移。


Note:Container Storage Interface 定義[2]。
還有一個附帶的好處是,一旦 CO. 和 SP. 都遵循 CSI,就便於將 Volume Driver 解耦到 Kubernetes 的外部(又叫 out-of-tree)。Kubernetes 只用維護 CSI 介面,不用再維護 SP. 的具體實現,維護成本大大降低。
CSI 優化下的架構圖:


可以看到,Kubernetes 和 SP. 從此涇渭分明,但事情並沒有那麼簡單,借用一位大神說的:The performance improvement does not materialize from the air, it comes with code complexity increase.和效能一樣,擴充套件性和解耦也不是憑空出現。上圖可進一步細化成下圖:


明顯的變化有:
  • Controller Plane、Kubelet 不再直接與 Volume Driver 互動,引入 external-provisioner 和 external-attacher 完成該工作;

  • SP. Volume Driver 會由獨立的容器執行;

  • 為了實現 external-provisioner、external-attacher 和 SP. Volume Driver 的互動引入 gRPC 協議(標紅箭頭)。


還有一些其他的變化:
  • 在 Kubernetes 端 引入新的物件:


    CSIPersistentVolumeSource:該型別 PV 由 CSI Driver 提供

    VolumeAttachment:同步 Attach 和 Dettach 資訊


  • 引入新的名稱:


    mount/umount:NodePublishVolume/NodeUnpublishVolume

    attach/dettach:ControllerPublishVolume/ControllerUnpublishVolume


Note:Kubernetes 適配 CSI 設計文件[3]。
可見,為了達到這個目標,相比原來複雜不少,但收益巨大,Kubernetes 和 SP. 的開發者可以專注於自身的業務。
即便如此,在現有的 CSI 上做擴充套件有時也在所難免。

基於 CSI 和分散式檔案系統實現在 MySQL 上的 Dynamically Expand Volume

Kubernetes 儲存子系統已經非常強大,但是還欠缺一些基礎功能,譬如支援 Expand Volume (部分 Storage Vendor 支援)和 SnapShot,尤其是 Expand Volume,這是必須的功能,因為隨著業務的變化,容量的增加在所難免,一旦容量接近閾值,若以遷庫的方式擴充套件儲存容量,成本太高。
但現狀並不樂觀,Kubernetes 1.10.2 使用 CSI 0.2.0,其並不包含 Expand Volume 介面,這意味著即便底層的儲存支援擴容,Kubernetes 也無法使用該功能,所以我們需要做點 hard code 實現該功能:
  • 擴充套件 CSI Spec

  • 擴充套件 CSI Plugin

  • 基於 CSI Spec 實現 Storage Driver

  • 演示

  • 其他


擴充套件 CSI Spec
前面提到,在 CSI 中引入了 gRPC,個人理解在 CSI 的場景有如下3個優點:
  • 基於 protobuf 定義強型別結構,便於閱讀和理解

  • 通過 stub 實現遠端呼叫,程式設計邏輯更清晰

  • 支援雙工和流式,提供雙向互動和實時互動


網路上介紹 gRPC 和 protobuf 的文章很多,這裡不贅述。
通過 gRPC,實現 CSI 各個元件的互動。為支援 expand volume 介面,需要修改 csi.proto,在 CSI 0.2.0 的基礎上新增需要的 rpc,重點如下:

點選(此處)摺疊或開啟

  1. service Controller {
  2.  rpc CreateVolume (CreateVolumeRequest)
  3.    returns (CreateVolumeResponse) {}
  4. ……
  5.  rpc RequiresFSResize (RequiresFSResizeRequest)
  6.    returns (RequiresFSResizeResponse) {}
  7.  rpc ControllerResizeVolume (ControllerResizeVolumeRequest)
  8.    returns (ControllerResizeVolumeResponse) {}
  9. }
  10. service Node { rpc NodeStageVolume (NodeStageVolumeRequest)
  11.    returns (NodeStageVolumeResponse) {}
  12. ……
  13.  rpc NodeResizeVolume (NodeResizeVolumeRequest)
  14.    returns (NodeResizeVolumeResponse) {}
  15. }
原 csi.proto[4] 和設計文件[5]。
通過 CSI 提供的編譯功能,用新編譯出來的 csi.pb.go 替換 external-provisioner、external-attacher、csi-driver 和 kubernetes 中現有的 csi.pb.go。
Note:CSI 編譯時預設使用最新版本的 protobuf 第三方包,這可能會導致 csi.pb.go 跟 Kubernetes 中依賴的 protobuf 第三方包不相容,編譯時需要切換到統一版本。
擴充套件 CSI Plugin
在現有 CSI Plugin 基礎上,實現介面 ExpandableVolumePlugin:

點選(此處)摺疊或開啟

  1. type ExpandableVolumePlugin interface {
  2. VolumePlugin
  3. ExpandVolumeDevice(spec *Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error)
  4. RequiresFSResize() bool
  5. }
仿照 csiMountMgr 的方式呼叫 CSI Driver 中的 ControllerResizeVolume。
基於 CSI Spec 實現 Storage Driver
  • CSI Driver 實現如下所有介面:

  • CreateVolume

  • DeleteVolume

  • ControllerPublishVolume

  • ControllerUnpublishVolume

  • ValidateVolumeCapabilities

  • ListVolumes

  • GetCapacity

  • ControllerGetCapabilities

  • RequiresFSResize

  • ControllerResizeVolume


這裡涉及到比較複雜的除錯和適配工作,還有一些其他的工作:
  • 定義 CSI 對應的 StorageClass,並設定 allowVolumeExpansion 為 true

  • 啟用 Feature Gates:ExpandPersistentVolumes

  • 新增 Admission Control:PersistentVolumeClaimResize

  • ……


演示


通過擴充套件 Container Storage Interface 0.2.0,我們在Kubernetes 1.10.2 上實現了線上擴容檔案系統擴容。對 MySQL 例項製造兩種型別工作負載:
  • 通過鍵值更新和查詢

  • 批量資料載入資料


通過上圖可以觀察到:
  • 讀數一:MySQL QPS 在正常波動範圍內;

  • 讀數二:持續批量載入資料,MySQL 檔案系統容量不斷變大;

  • 讀數三:在20分鐘內,線上動態擴容 Volume 和 Filesystem 2 次, 過程高效平滑。


其他
工作到這裡基本結束,要改動的地方不少,客觀上並不簡單。如果沒有 Kubernetes 和 CSI,難度會更大。
通過此方法可以完成對其他 Storage Vendor 的擴充套件,譬如 FCSan、iSCSI。

對 CSI 的展望

目前 CSI 已發展到 0.2.0,0.3.0 也釋出在即。

0.3.0 中呼聲最高的特性是 Snapshot。藉助該功能,可以實現備份和異地容災。但是為了實現該功能,在 Kubernetes 現有的 Control-Plane 上還要新增新的 Controller,客觀上,複雜度會進一步提高。
同時,部分 in-tree Volume Driver 已通過 CSI 遷移到外部,考慮到 Kubernetes 整體的釋出節奏和 API 的穩定性,個人覺得節奏不會太快。
除此之外,CSI 可能還有更多工作要做。以一個高可用的場景為例,當一個 Node 發生故障時,CO 觸發 Unmount->Dettach->Attach->Mount 的流程,配合 Pod 的漂移,藉助 CSI 定義的介面,Unmount、Dettach、Attach、Mount 由SP. 自身現實,但是 Unmount->Dettach->Attach->Mount 的流程還是有 CO 控制,這個流程並不標準,但是對 workload 又至關重要。


又想起<人月神話>中的那句 “No Silver Bullet”。
相關連結:
  1. https://github.com/kubernetes/kubernetes/blob/afa68cc28749c09f8655941b111e46d85689daf8/pkg/volume/plugins.go#L95

  2. https://github.com/container-storage-interface/spec/blob/master/spec.md

  3. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/storage/container-storage-interface.md

  4. https://github.com/container-storage-interface/spec/blob/master/csi.proto

  5. https://docs.google.com/document/d/1kVrNwA2f4ite8_9QvCy-JQA_00hxGGMdER3I84dUDqQ/edit?usp=sharing



| 作者簡介

熊中哲,沃趣科技產品及研發負責人

曾就職於阿里巴巴和百度,超過10年關係型資料庫工作經驗,目前致力於將雲原生技術引入到關係型資料庫服務中。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28218939/viewspace-2156250/,如需轉載,請註明出處,否則將追究法律責任。

相關文章