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