當前位置:
首頁 > 最新 > 容器化RDS:PersistentLocalVolumes和VolumeScheduling

容器化RDS:PersistentLocalVolumes和VolumeScheduling

資料庫的高可用方案非常依賴底層的存儲架構,這也是集中式存儲作為核心資料庫業務標配的原因之一。現在,越來越多的用戶開始在生產環境中使用基於資料庫層的複製技術來保障數據多副本和數據一致性,該架構使用戶可以使用「Local」存儲構建「Zero Data Lost」的高可用集群,而不依賴「Remote」 的集中式存儲。

「Local」和「Remote」隱含著Kube-Scheduler在調度時需要對Volume的「位置」可見。

本文嘗試從使用本地存儲的場景出發,分享「VolumeScheduling」在代碼中的具體實現和場景局限,以下的總結來自於(能力有限,不盡之處,不吝賜教):

https://github.com/kubernetes/community/pull/1054

https://github.com/kubernetes/community/pull/1140

https://github.com/kubernetes/community/pull/1105

Kubernetes 1.9和1.10部分代碼

本地卷

相比「Remote」的卷,本地卷:

更好的利用本地高性能介質(SSD,Flash)提升資料庫服務能力 QPS/TPS(其實這個結論未必成立,後面會有贅述)

更閉環的運維成本,現在越來越多的資料庫支持基於Replicated的技術實現數據多副本和數據一致性(比如MySQL Group Replication / MariaDB Galera Cluster / Percona XtraDB Cluster的),DBA可以處理所有問題,而不在依賴存儲工程師或者SA的支持。

MySQL Group Replication / MariaDB Galera Cluster / Percona XtraDB Cluster 方案詳見:《容器化RDS:計算存儲分離還是本地存儲?》

在1.9之後,可以通過Feature Gate 「PersistentLocalVolumes」使用本地卷。

目前local.path可以是MountPoint或者是BlockDevice。這是我們要使用 MySQL Group Replication / MariaDB Galera Cluster / Percona XtraDB Cluster架構的基礎。

但還不夠,因為Scheduler並不感知卷的「位置」

這裡需要從PVC綁定和Pod調度說起。

原有調度機制的問題

當申請匹配workload需求的資源時,可以簡單的把資源分為「計算資源」和「存儲資源」。

以Kubernetes申請Statefulset資源YAML為例:

YAML中定義了Pod對「計算」和「存儲」資源的要求。隨後Statefulset Controller創建需要的PVC和Pod:

PVC綁定

Pod借用PVC描述需要的存儲資源,PVC是PV的抽象,就像VFS是Linux對具體文件系統的抽象,所以在PVC創建之後,還需要將PVC跟卷進行綁定,也即是PV。PersistentVolume Controller會遍歷現有PV和可以動態創建的StorageClass(譬如NFS、Ceph、EBS),找到滿足條件(訪問許可權、容量等)進行綁定。

大致流程如下:

Pod調度

Scheduler基於資源要求找到匹配的節點,以過濾和打分的方式選出「匹配度」最高的Node,流程大致如此:

Scheduler通用調度策略詳見:《容器化RDS:調度策略》

問題

總結以上流程:

PVC綁定在Pod調度之前,PersistentVolume Controller不會等待Scheduler調度結果,在Statefulset中PVC先於Pod創建,所以PVC/PV綁定可能完成在Pod調度之前。

Scheduler不感知卷的「位置」,僅考慮存儲容量、訪問許可權、存儲類型、還有第三方CloudProvider上的限制(譬如在AWS、GCE、Aure上使用Disk數量的限制)

當應用對卷的「位置」有要求,比如使用本地卷,可能出現Pod被Scheduler調度到NodeB,但PersistentVolume Controller綁定了在NodeD上的本地卷,以致PV和Pod不在一個節點,如下圖所示:

不僅僅是本地卷,如果對存儲「位置」(譬如:Rack、Zone)有要求,都會有類似問題。

好比,Pod作為下屬,它實現自身價值的核心資源來自於兩個上級Scheduler和PersistentVolume Controller,但是這兩個上級從來不溝通,甚至出現矛盾的地方。作為下屬,要解決這個問題,無非如下幾種選擇:

嘗試讓兩個老闆溝通

站隊,挑一個老闆,只聽其中一個的指揮

辭職

Kubernetes做出了「正常人」的選擇:站隊。

如果Pod使用的Volume對「位置」有要求(又叫Topology-Aware Volume),通過延時綁定(DelayBinding)使PersistentVolume Controller不再參與,PVC綁定的工作全部由Scheduler完成。

在通過代碼了解特性「VolumeScheduling」的具體實現時,還可以先思考如下幾個問題:

如何標記Topology-Aware Volume

如何讓PersistentVolume Controller不再參與,同時不影響原有流程

Feature:VolumeScheduling

Kubernetes在卷管理中通過策略VolumeScheduling重構Scheduler以支持Topology-Aware Volume,步驟大致如下:

預分配使用本地卷的PV。

通過NodeAffinity方式標記Topology-Aware Volume和「位置」信息:

創建StorageClass,通過StorageClass間接標記PVC的綁定需要延後(綁定延時)。

標記該PVC需要延後到Node選擇出來之後再綁定:

創建StorageClass 「X」(無需Provisioner),並設置StorageClass.VolumeBindingMode = VolumeBindingWaitForFirstConsumer

PVC.StorageClass設置為X

依照原有流程創建PVC和Pod,但對於需要延時綁定的PVC,PersistentVolume Controller不再參與。

通過PVC.StorageClass,PersistentVolume Controller得知PVC是否需要延時綁定。

如需延時綁定,do nothing。

執行原有Predicates函數

執行添加Predicate函數CheckVolumeBinding校驗候選Node是否滿足PV物理拓撲(主要邏輯由FindPodVolumes提供):

已綁定PVC:對應PV.NodeAffinity需匹配候選Node,否則該節點需要pass

未綁定PVC:該PVC是否需要延時綁定,如需要,遍歷未綁定PV,其NodeAffinity是否匹配候選Node,如滿足,記錄PVC和PV的映射關係到緩存bindingInfo中,留待節點最終選出來之後進行最終的綁定。

以上都不滿足時 : PVC.StorageClass是否可以動態創建 Topology-Aware Volume(又叫 Topology-aware dynamic provisioning)

執行原有Priorities函數

執行添加Priority函數PrioritizeVolumes。Volume容量匹配越高越好,避免本地存儲資源浪費。

Scheduler選出Node

由Scheduler進行API update,完成最終的PVC/PV綁定(非同步操作,時間具有不確定性,可能失敗)

從緩存bindingInfo中獲取候選Node上PVC和PV的綁定關係,並通過API完成實際的綁定

如果需要StorageClass動態創建,被選出Node將被賦值給StorageClass.topologyKey,作為StorageClass創建Volume的拓撲約束,該功能的實現還在討論中。

綁定被調度Pod和Node

Scheduler的流程調整如下(依據Kubernetes 1.9代碼):

從代碼層面,對Controller Manager和Scheduler都有改造,如下圖所示(依據Kubernetes 1.9代碼):

舉個例子

先運行一個例子。

預分配的方式創建使用本地存儲的PV。

為了使用本地存儲需要啟動FeatureGate:PersistentLocalVolumes支持本地存儲,1.9是alpha版本,1.10是beta版,默認開啟。

創建Storage Class。

創建使用本地存儲的Statefulset(僅列出關鍵信息)。

該Statefulset的Pod將會調度到k8s-node1-product,並使用本地存儲「local-pv」。

「PersistentLocalVolumes」和「VolumeScheduling」的局限

具體部署時。

使用局限需要考慮:

資源利用率降低。一旦本地存儲使用完,即使CPU、Memory剩餘再多,該節點也無法提供服務;

需要做好本地存儲規劃,譬如每個節點Volume的數量、容量等,就像原來使用存儲時需要把LUN規劃好一樣,在一個大規模運行的環境,存在落地難度。

高可用風險需要考慮:

當Pod調度到某個節點後,將會跟該節點產生親和,一旦Node發生故障,Pod不能調度到其他節點,只能等待該節點恢復,你能做的就是等待「Node恢復」,如果部署3節點MySQL集群,再掛一個Node,集群將無法提供服務,你能做的還是「等待Node恢復」。這麼設計也是合理的,社區認為該Node為Stateful節點,Pod被調度到其他可用Node會導致數據丟失。當然,你的老闆肯定不會聽這套解釋。

而且還要思考,初衷更好的利用本地高性能介質(SSD,Flash)提升資料庫服務能力QPS/TPS

真的成立嗎?

資料庫是IO延時敏感型應用,同時它也極度依賴系統的平衡性,基於Replicated架構的資料庫集群對集群網路的要求會很高,一旦網路成為瓶頸影響到數據的sync replication,都會極大的影響資料庫集群服務能力。目前,Kubernetes的網路解決方案還無法提供高吞吐,低延時的網路能力。

當然,我們可以做點什麼解決「等待Node恢復」的問題。

以MySQL Group Replication / MariaDB Galera Cluster / Percona XtraDB Cluster架構為例,完全可以在這個基礎上,結合Kubernetes現有的Control-Plane做進一步優化。

Node不可用後,等待閾值超時,以確定Node無法恢復

如確認Node不可恢復,刪除PVC,通過解除PVC和PV綁定的方式,解除Pod和Node的綁定

Scheduler將Pod調度到其他可用Node,PVC重新綁定到可用Node的PV。

Operator查找MySQL最新備份,拷貝到新的PV

MySQL集群通過增量同步方式恢復實例數據

增量同步變為實時同步,MySQL集群恢復

最後,借用Google工程師Kelsey Hightower的一句話:

「We very receptive this Kubernetes can』t be everything to everyone.」

Kubernetes並不是「鴻茅藥酒」包治百病,了解邊界是使用的開始,泛泛而談Cloud-Native無法獲得任何收益,在特定的場景和領域,很多問題還需要我們自己解決。最後說下個人理解,如無特別必要,盡量不選用Local Volume。

作者:熊中哲,沃趣科技產品及研發負責人。曾就職於阿里巴巴和百度,超過10年關係型資料庫工作經驗,目前致力於將雲原生技術引入到關係型資料庫服務中。

Kubernetes入門與進階實戰培訓

本次培訓內容包括:Docker基礎、容器技術、Docker鏡像、數據共享與持久化、Docker三駕馬車、Docker實踐、Kubernetes基礎、Pod基礎與進階、常用對象操作、服務發現、Helm、Kubernetes核心組件原理分析、Kubernetes服務質量保證、調度詳解與應用場景、網路、基於Kubernetes的CI/CD、基於Kubernetes的配置管理等,點擊了解具體培訓內容


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Docker 的精彩文章:

CNCF得意門生結業,Kubernetes全面走向成熟
Solomon Hykes離開Docker公司,自此仗劍走天涯

TAG:Docker |