溫故知新-EverDB容器化之旅
今天給大家?guī)怼澳愫茫沂荅verDB!”系列文章的第二篇—容器化之旅。
容器天生的部署快速、輕量、便于調(diào)度特性非常適合故障場景的模擬,因此EverDB容器化是我們的第一步工作。本篇文章將介紹基于k8s的EverDB容器化實現(xiàn)方案。
為什么選擇k8s
k8s全稱Kubernetes,是一個開源的、基于容器技術(shù)的分布式架構(gòu)解決方案,提供了容器自動化部署、管理、編排,伸縮等能力,使應(yīng)用容器化更加簡單高效。同時,k8s平臺在故障轉(zhuǎn)移,資源調(diào)度、隔離,負(fù)載均衡方面的特性,也更契合EverDB自身架構(gòu)特點和測試、管理需求,因此EverDB容器化方案選定基于k8s來實現(xiàn)。
部署到k8s的技術(shù)路線
Helm工具
Helm是k8s的包管理器,類似我們在Ubuntu中使用的apt、Centos中使用的yum一樣,能快速查找、下載和安裝軟件包。在Helm里面,最重要的應(yīng)用包叫Charts,這是一個應(yīng)用的定義描述,里面包括了這個應(yīng)用的一些元數(shù)據(jù),以及該應(yīng)用的k8s資源定義模板及其配置。在擁有足夠完善的Charts情況下,只需要簡單的install就可以快速部署服務(wù)。
Operator思路
Operator是用k8s原生方式去管理應(yīng)用的一種實現(xiàn)思路,通過k8s擴(kuò)展API,使用CRD自定義資源對象,并實現(xiàn)對應(yīng)的控制器來實現(xiàn)對應(yīng)用的部署及管理。
Helm和Operator兩種方案在k8s應(yīng)用管理上各有優(yōu)勢,前者的優(yōu)勢在于將資源模板化,方便共享,并在不同的配置中復(fù)用;后者則更加針對復(fù)雜應(yīng)用的自動化管理。此次部署到k8s平臺,考慮到實現(xiàn)成本和當(dāng)前需求,決定基于Helm來實現(xiàn)EverDB容器化方案。
整體方案
在k8s平臺上,應(yīng)用可分為有狀態(tài)和無狀態(tài)兩種。EverDB的數(shù)據(jù)節(jié)點MySQL、調(diào)度節(jié)點Grid、配置節(jié)點ZooKeeper均需要保持運(yùn)行狀態(tài)參數(shù)并對外提供穩(wěn)定服務(wù),數(shù)據(jù)節(jié)點和配置節(jié)點還需要將數(shù)據(jù)和配置信息持久化到存儲器,因此部署到k8s上均屬于StatefulSet類型的有狀態(tài)應(yīng)用。
EverDB架構(gòu)圖
EverDB的三個組件設(shè)計需要配置的k8s資源如下表所示:
Pod控制器類型 | ConfigMap | NodePort | 無頭服務(wù) | 持久化存儲 | 輔助容器 | |
Mysql | StatefulSet | ● | ● | ● | ● | ● |
Zookeeper | StatefulSet | ● | ○ | ● | ● | ○ |
dbscale | StatefulSet | ● | ● | ● | ○ | ○ |
MySQL作為EverDB的底層數(shù)據(jù)存儲引擎,在部署至k8s時,除需具備數(shù)據(jù)實例配置、實例初始化、數(shù)據(jù)持久化存儲,對外訪問服務(wù)等功能外,還要有監(jiān)控、備份等輔助容器;
ZooKeeper作為EverDB的配置管理節(jié)點,其所管理的配置信息同樣需要持久化存儲,對外提供訪問服務(wù);
Grid作為調(diào)度節(jié)點,其元數(shù)據(jù)保存在底層數(shù)據(jù)節(jié)點上,而配置參數(shù)通過初始化ConfigMap完成參數(shù)加載后,保存到遠(yuǎn)端配置節(jié)點ZooKeeper上,即使發(fā)生Pod故障,Grid可以從ZooKeeper拉取配置信息,因此其不需要持久化的PV存儲數(shù)據(jù)。
服務(wù)訪問
EverDB對外需要提供數(shù)據(jù)庫服務(wù),對內(nèi)組件間也需要能夠互聯(lián)互通,那么各個Pod獨立運(yùn)行,他們之間的聯(lián)系由誰來建立呢?
這就要介紹k8s的核心資源對象中Service,Service是一個抽象概念,它定義了一組Pod的邏輯集合和一個訪問它們的負(fù)載均衡策略。k8s的Service可以定義一個集群內(nèi)部的服務(wù)訪問入口地址(ClusterIP),Service與Pod間通過LabelSelector來建立關(guān)聯(lián),應(yīng)用通過這樣一個入口地址訪問其背后的一組Pod實例。這樣,在Pod發(fā)生銷毀或重建導(dǎo)致PodIP發(fā)生變化時,Service可以自動感知且提供的ClusterIP不會發(fā)生改變,我們?nèi)钥梢酝ㄟ^Service訪問后端的Pod。
而對于EverDB集群內(nèi)部節(jié)點之間的通信,需要實現(xiàn)一對一通信且不受PodIP 變化的影響。比如EverDB集群中的Mysql主從實例,在進(jìn)行主從同步時,從實例(Slave)需要能直接訪問主實例(Master)這一確切Pod,并不需要負(fù)載均衡,且在任何PodIP 發(fā)生變化時主從同步均不受影響,顯然上述Service的定位并不適合這樣的場景。
別急,k8s還設(shè)計了HeadlessService(無頭服務(wù))這一特殊類型的Service。HeadlessService不分配ClusterIP,訪問者可以通過解析該Service的DNS來獲取Pod的地址,就像訪問域名一樣。HeadlessServie 的域名一般是“{podname}.{headlessservice}.{namespace}”的形式。與Deployment類型Pod的隨機(jī)化podname相比,StatefulSet類型的Pod,其podname格式為{StatefulSetname}-{固定編號},這也使得即使對Pod進(jìn)行重啟、節(jié)點遷移等操作,域名本身并不會發(fā)生變動。
EverDB數(shù)據(jù)節(jié)點部署至k8s示例圖
因此對于EverDB集群,我們使用基于ClusterIP 類型的Service對外提供EverDB數(shù)據(jù)庫服務(wù)統(tǒng)一入口,對內(nèi)提供多個Grid調(diào)度節(jié)點的負(fù)載均衡能力;使用HeadlessService 實現(xiàn)EverDB集群內(nèi)部節(jié)點間的通信能力,且不受PodIP 變化影響。
持久化存儲
EverDB作為有狀態(tài)的應(yīng)用,部署在k8s平臺需要解決存儲問題,即當(dāng)應(yīng)用Pod被刪除或重新創(chuàng)建時,內(nèi)部數(shù)據(jù)不會丟失。PV(PersistentVolume)可以看作k8s集群可用的存儲資源,PVC(PersistentVolumeClaim)則是對存儲資源的需求。對于存儲資源,k8s平臺支持兩種供應(yīng)模式:靜態(tài)模式(Static)和動態(tài)模式(Dynamic),在EverDB集群中,已支持這兩種供應(yīng)模式。
在靜態(tài)模式中,需要集群管理員通過手動方式創(chuàng)建PV,EverDB采用的是基于LocalPV方法的持久化存儲,該方法主要應(yīng)用于生產(chǎn)環(huán)境中,LocalPV對應(yīng)的存儲介質(zhì)通常是一塊額外掛載在宿主機(jī)的磁盤,實現(xiàn)“一個PV一塊盤”,不僅能夠有效減少宿主機(jī)宕機(jī)導(dǎo)致的數(shù)據(jù)丟失,而且增強(qiáng)了集群存儲擴(kuò)展的能力。
靜態(tài)模式下LocalPV和PVC原理圖
在動態(tài)模式中,EverDB采用了基于HostPath的方法,該方法主要應(yīng)用于開發(fā)測試環(huán)境中,使用宿主機(jī)本地目錄,有效避免IO開銷并擁有更高的讀寫性能。同時為了避免單機(jī)測試的問題,結(jié)合了Github開源項目LocalPath Provisioner,可以有效利用集群節(jié)點中的本地存儲,通過SrorgeClass(存儲類)的設(shè)置,只需PVC對存儲類型進(jìn)行聲明,系統(tǒng)將自動完成PV的創(chuàng)建和綁定。
動態(tài)模式下StorageClass、PV和PVC原理圖
配置管理
容器的啟動總是需要些參數(shù)的,給容器內(nèi)應(yīng)用傳遞參數(shù)通常有以下幾種方式:
1、直接將配置文件打包到鏡像中;
2、在定義Pod時,添加自定義命令行參數(shù),設(shè)定args:[“命令參數(shù)”];
3、使用環(huán)境變量來給Pod中的應(yīng)用傳參修改配置。
ConfigMap的設(shè)計就是為了讓鏡像和配置文件解耦,以便實現(xiàn)鏡像的可移植性和可復(fù)用性,一個ConfigMap其實就是一系列配置信息的集合。
ConfigMap存在兩種方式將配置參數(shù)注入到容器中:
1、將環(huán)境變量直接定義在ConfigMap中,當(dāng)Pod啟動時,通過env來引用ConfigMap中定義的環(huán)境變量;
2、將一個完整的配置文件封裝,通過共享卷的方式掛載進(jìn)Pod中實現(xiàn)給應(yīng)用傳參。
EverDB的三個組件的Pod在容器拉起時,需要配置性能參數(shù),主從信息等。EverDB配置容器運(yùn)行參數(shù)時,對于復(fù)雜的配置文件信息,采用ConfigMap創(chuàng)建配置文件的形式實現(xiàn),在Pod啟動時,將其掛載到容器中,而對于一些簡單的運(yùn)行參數(shù),則是通過環(huán)境變量的形式注入到容器中。
依據(jù)上述EverDB各組件對ConfigMap、StatefulSet、持久化存儲、服務(wù)訪問等需求,創(chuàng)建對應(yīng)的Helm模板,將EverDB的各組件封裝為Chart包,在啟動子組件時,只需要對一些必要的參數(shù)進(jìn)行更改設(shè)置,即可完成定制化的EverDB集群安裝。即避免了手動部署易于出錯的問題,又能方便集群在k8s上的的定制化與快速部署,穩(wěn)定且高效,靈活而優(yōu)雅。
結(jié)束語
EverDB容器化實現(xiàn)不僅便于我們在混沌實驗中實現(xiàn)故障注入,也使我們在數(shù)據(jù)庫云化道路上邁出了里程碑式的一步!