Kubernetes存儲系統介紹及機制實現
本文分為三大部分。第一部分主要介紹Kubernetes中常用的幾種存儲,及其使用場景和生命周期等等。第二部分試圖介紹一些設計原則和基本架構,並簡要介紹各種存儲plugin的實現機制及持久卷的一些特性,例如訪問模式、回收策略等等。動態卷供給是一個Kubernetes獨有的功能,這一功能允許按需創建存儲卷,使管理員不必預先創建存儲卷,而是隨用戶需求進行創建。第三部分會介紹一下v1.9中存儲的一些新特性。
一、Kubernetes中存儲的應用場景
在Kubernetes中部署和運行的服務大致分為:
1. 無狀態服務
Kubernetes使用ReplicaSet來保證一個服務的實例數量,如果說某個Pod實例由於某種原因掛掉或崩潰,ReplicaSet會立刻用這個Pod的模版新啟一個Pod來替代它。由於是無狀態的服務,新Pod與舊Pod一模一樣。此外Kubernetes通過Service(一個Service後面可以掛多個Pod)對外提供一個穩定的訪問介面,實現服務的高可用。
2. 普通有狀態服務
和無狀態服務相比,它多了狀態保存的需求。Kubernetes提供了以Volume和Persistent Volume為基礎的存儲系統,可以實現服務的狀態保存。
3. 有狀態集群服務
和普通有狀態服務相比,它多了集群管理的需求。要運行有狀態集群服務要解決的問題有兩個,一個是狀態保存,另一個是集群管理。Kubernetes為此開發了StatefulSet(以前叫做PetSet),方便有狀態集群服務在Kubernetes上部署和管理。
簡單來說是通過Init Container來做集群的初始化工作,用Headless Service來維持集群成員的穩定關係,用動態存儲供給來方便集群擴容,最後用StatefulSet來綜合管理整個集群。
分析以上的服務類型,Kubernetes中對於存儲的使用主要集中在以下幾個方面:
服務的基本配置文件讀取、密碼密鑰管理等;
服務的存儲狀態、數據存取等;
不同服務或應用程序間共享數據。
二、Kubernetes中幾種常見的存儲系統
目前Kubernetes所支持的Volume Plugins如下表所示。
Kubernetes已經提供非常豐富的Volume和Persistent Volume插件,大家可以根據自己業務的需要,使用這些插件給容器提供存儲服務。每一種Plugin的使用方法和注意事項在此不做贅述,請參考Kubernetes Volume 的官方文檔[1]。
容器存儲介面(Container Storage Interface,CSI[2])是一項跨行業標準倡議,旨在降低雲原生存儲開發工作的門檻,從而進一步確保兼容性水平。Kubernetes v1.9已經引入了CSI的一套alpha實現版本,將新分卷插件的安裝流程簡化至與安裝pod相當,並允許第三方存儲供應商在無需接觸核心Kubernetes代碼庫的前提下開發自己的解決方案。
如果上述的這些Plugin不滿足業務要求, 你可以通過以下兩種途徑進行二次開發。
可以使用FlexVolume實現自己的Volume插件。此Plugin仍是alpha版本,後向兼容性需要考慮。具體方法在此不做贅述,參考FlexVolume的社區文檔[3]。
推薦使用CSI。目前還只是alpha版本,使用時需要在feature-gate中enable,不推薦在production環境中使用。v1.9已經把CSI作為in-tree plugin,把out-off-tree volume插件的開發從 Kubernetes 中脫離出來,極大地方便了插件的開發、維護和集成。如何使用CSI,可參考How to Use Kubernetes 1.9.0 with CSI[4]。
三、Kubernetes存儲的設計與基本架構
Kubernete存儲在設計的時候遵循著Kubernetes的一貫哲學,即聲明式(Declarative)架構。同時為了儘可能多地兼容各種存儲平台,Kubernetes以in-tree plugin的形式來對接不同的存儲系統,滿足用戶可以根據自己業務的需要使用這些插件給容器提供存儲服務。同時兼容用戶使用FlexVolume和CSI定製化插件。相比較於Docker Volume,支持的存儲功能更加豐富和多樣。
Kubernetes中mount 一個PV的基本過程包括:
用戶通過API創建一個包含PVC的Pod;
Scheduler把這個Pod分配到某個節點,比如Node1;
Node1上的Kubelet開始等待Volume Manager準備device;
PV controller調用相應Volume Plugin(in-tree或者out-of-tree),創建PV,並在系統中與對應的PVC綁定;
Attach/Detach controller或者Volume Manager通過Volume Plugin實現device掛載(Attach);
Volume Manager等待device掛載完成後,將卷掛載到節點指定目錄(mount), 比如/var/lib/kubelet/pods/xxxxxxxxxxx/volumes/aws-ebs/vol-xxxxxxxxxxxxxxxxx;
Node1上的Kubelet此時被告知volume已經準備好後,開始啟動Pod,通過volume mapping將PV已經掛載到相應的容器中去。
其實對於Kubernetes中大部分的Volume Plugin來說,mount的過程遵循著如下的規則:
這種方式的好處相當於熱插拔,一旦Pod掛掉,kubelet可以馬上重啟,並快速mount volume,不會出現類似於device busy的情形。
但是對於hostpath這個Plugin而言,直接就是 /some/global/mount/path -> container volume。
四、Persistent Volume與Persistent Volume Claim
一個運行中的容器,預設情況下,對文件系統的寫入,都是發生在其分層文件系統的可寫層的(Copy-on-Write)。當遷移的應用程序從開發到生產環境時候,開發人員面臨著巨大的挑戰。當容器掛掉、崩潰或運行結束時,任何與之相關的數據都會丟失。為了解決這個問題引發的數據丟失,我們需要將數據存儲持久化,也可以稱為Persistent Volume。
Kubernetes使用兩種資源管理存儲:
PersistentVolume(簡稱PV):由管理員添加的的一個存儲的描述,是一個全局資源,包含存儲的類型,存儲的大小和訪問模式等。它的生命周期獨立於Pod,例如當使用它的Pod銷毀時對PV沒有影響。
PersistentVolumeClaim(簡稱PVC):是Namespace里的資源,描述對PV的一個請求。請求信息包含存儲大小,訪問模式等。
Kubernetes中的Volume則是基於Docker進行擴展,使用Docker Volume掛載宿主機上的文件目錄到容器中。
一般來說,Kubernetes中Pod通過如下三種方式來訪問存儲資源。
直接訪問
該種方式移植性較差,可擴展能力差,把Volume的基本信息完全暴露給用戶,有嚴重的安全隱患,同時需要協調不同users對Volume的訪問。
靜態provision
動態provision
StorageClass將說明Volume將由哪種Volume Plugin創建、創建時參數以及從其他功能性/非功能性角度描述的後台volume的各種參數。一般為storage cluster的一些配置信息,以及label注釋信息。
一般來說,PV和PVC的生命周期分為5個階段:
Provisioning,即PV的創建,可以直接創建PV(靜態方式),也可以使用StorageClass動態創建
Binding,將PV分配給PVC
Using,Pod通過PVC使用該Volume
Releasing,Pod釋放Volume並刪除PVC
Reclaiming,回收PV,可以保留PV以便下次使用,也可以直接從雲存儲中刪除
根據這5個階段,Volume的狀態有以下4種:
Available:可用
Bound:已經分配給PVC
Released:PVC解綁但還未執行回收策略
Failed:發生錯誤
五、v1.9中對存儲做了哪些更改
引入了CSI alpha版本的實現,可見第二部分關於CSI的介紹。
修復Bug:刪除運行狀態container的PVC
這個bug會導致數據丟失。社區的解決辦法是引入一個Finalizer來保護PVC。
詳細的步驟請參考相關的Proposal[5]及其代碼實現[6]。
簡單來說,這個Fianlizer類似於垃圾回收(GC)裡面的指針計數,當這個使用這個PVC的POD都被刪除(deleted)或處於完成狀態(completed)時,才可以刪除這個PVC。從而避免了刪除正在運行中的container的PVC,從而引發數據丟失。
Q&A
Q:Kubernetes和Cloud Foundry有什麼區別,優勢在什麼地方?
A:Cloud Foundry更像是Application PaaS,Kubernetes主要是Container PaaS。CF不會直接把container層暴露給用戶,Kubernetes則不然,你可以直接訪問container。個人覺得kubernetes的部署和使用更簡單,更直接,操作起來也更方便。
Q:請問對於Galera Cluster的集群存儲如何設計存儲方案,CSI有考慮有狀態儲存的解決方案嗎?
A:對於有狀態服務,請使用StatefulSet來部署你的應用。Volume只是一個存儲的地方。StatefulSet會負責給Pod設置順序等等,保證是有序的,優雅的刪除停止和擴展。
Q:有沒有對象存儲,比如Ceph RGW,在Kubernetes集群中的使用案例,比如用戶通過客戶端上傳、下載PVC中的數據之類的?
A:有試過RGW來存儲數據,但是性能不是很好,速度要慢很多。在Kubernetes集群中可以使用RGW來存儲一些靜態的文件,比如配置文件,Nginx靜態html文件之類,用戶也可以下載PV中的數據。不建議對RGW中的數據進行頻繁的更改。
Q:能否詳細說下CSI?
A:CSI在kubernetes v1.9才引入。這部分內容比較多,可以去看看CSI文檔[2],以及Kubernetes官方的介紹[7],以及這個feature實現代碼[8]。
Q:RBD用什麼插件,有沒有什麼坑?
A:需要在kubelet節點上安裝ceph-common這個包,在volume mount的時候,會調用相關的命令。基本上沒什麼坑,RBD提供的volume還是很好使的。
Q:存儲這塊IO性能下降大概多少呢,有實測過嗎?
A:存儲的性能與Cluster的能力、存儲的類型有很大關係。實測過RBD的IO性能,當然case by case,當時測出來的結果還是很不錯的,相比較與CephFS、GlusterFS。
Q:多個Pod共享一個Nas,是否可行,需要注意什麼?
A:可行,但是需要注意Volume的讀寫許可權,這個可以通過mount時候的PV的access mode進行設置,比如ReadWrite、ReadWriteOnce等等。
Q:CSI和社區孵化的volume provisioner有什麼區別?
A:CSI的主要目的還是為了給容器存儲定義一個統一的介面,方便進行定製化,以及新功能添加。社區孵化的volume provisioner,你指的應該是external-storage這個項目吧,這個項目的主要目的是為了方便對in-tree的那些Plugin進行修改和定製,這樣可以獨立地進行更新。
Q:請問你現在有把MySQL可以放進PV裡面么?求介紹這方面的經驗。
A:如果只是單節點的MySQL,直接放進PV就好了,跟其它正常服務一樣。對於多節點的MySQL Cluster,在部署的時候就需要注意了,建議部署成StatefulSet,有狀態的服務。之前有試過用Galera Cluster For MySQL來部署集群,發現效果不是很好,尤其是在隨意啟停Pod的時候,Cluster的沒辦法自組成新的集群,新節點也無法加入集群。你可以再次試驗下,確認一下。 後來使用MySQL Cluster CGE,效果很好,Cluster能夠及時回復並重新組織起來。
TAG:Docker |