vivo大規(guī)模 Kubernetes 集群自動(dòng)化運(yùn)維實(shí)踐
作者 | vivo 互聯(lián)網(wǎng)服務(wù)器團(tuán)隊(duì)-Zhang Rong
一、背景
隨著vivo業(yè)務(wù)遷移到k8s的增長,我們需要將k8s部署到多個(gè)數(shù)據(jù)中心。如何高效、可靠的在數(shù)據(jù)中心管理多個(gè)大規(guī)模的k8s集群是我們面臨的關(guān)鍵挑戰(zhàn)。kubernetes的節(jié)點(diǎn)需要對(duì)os、docker、etcd、k8s、cni和網(wǎng)絡(luò)插件的安裝和配置,維護(hù)這些依賴關(guān)系繁瑣又容易出錯(cuò)。
以前集群的部署和擴(kuò)縮容主要通過ansible編排任務(wù),黑屏化操作、配置集群的inventory和vars執(zhí)行ansible playbook。集群運(yùn)維的主要困難點(diǎn)如下:
- 需要人工黑屏化集群運(yùn)維操作,存在操作失誤和集群配置差異。
- 部署腳本工具沒有具體的版本控制,不利于集群的升級(jí)和配置變更。
- 部署腳本上線需要花費(fèi)大量的時(shí)間驗(yàn)證,沒有具體的測試用例和CI驗(yàn)證。
- ansible任務(wù)沒有拆分為模塊化安裝,應(yīng)該化整為零。具體到k8s、etcd、addons的等角色的模塊化管理,可以單獨(dú)執(zhí)行ansible任務(wù)。
- 主要是通過二進(jìn)制部署,需要自己維護(hù)一套集群管理體系。部署流程繁瑣,效率較低。
- 組件的參數(shù)管理比較混亂,通過命令行指定參數(shù)。k8s的組件最多有100以上的參數(shù)配置。每個(gè)大版本的迭代都在變化。
本文將分享我們開發(fā)的Kubernetes-Operator,采用K8s的聲明式API設(shè)計(jì),可以讓集群管理員和Kubernetes-Operator的CR資源進(jìn)行交互,以簡化、降低任務(wù)風(fēng)險(xiǎn)性。只需要一個(gè)集群管理員就可以維護(hù)成千上萬個(gè)k8s節(jié)點(diǎn)。
二、集群部署實(shí)踐
2.1 集群部署介紹
主要基于ansible定義的OS、docker、etcd、k8s和addons等集群部署任務(wù)。
主要流程如下:
- Bootstrap OS
- Preinstall step
- Install Docker
- Install etcd
- Install Kubernetes Master
- Install Kubernetes node
- Configure network plugin
- Install Addons
- Postinstall setup
上面看到是集群一鍵部署關(guān)鍵流程。當(dāng)在多個(gè)數(shù)據(jù)中心部署完k8s集群后,比如集群組件的安全漏洞、新功能的上線、組件的升級(jí)等對(duì)線上集群進(jìn)行變更時(shí),需要小心謹(jǐn)慎的去處理。我們做到了化整為零,對(duì)單個(gè)模塊去處理。避免全量的去執(zhí)行ansible腳本,增加維護(hù)的難度。針對(duì)如docker、etcd、k8s、network-plugin和addons的模塊化管理和運(yùn)維,需提供單獨(dú)的ansible腳本入口,更加精細(xì)的運(yùn)維操作,覆蓋到集群大部分的生命周期管理。同時(shí)kubernetes-operator的api設(shè)計(jì)的時(shí)候可以方便選擇對(duì)應(yīng)操作yml去執(zhí)行操作。
集群部署優(yōu)化操作如下:
(1)k8s的組件參數(shù)管理通過
ConmponentConfig[1]提供的API去標(biāo)識(shí)配置文件。
- 【可維護(hù)性】當(dāng)組件參數(shù)超過50個(gè)以上時(shí)配置變得難以管理。
- 【可升級(jí)性】對(duì)于升級(jí),版本化配置的參數(shù)更容易管理。因?yàn)樯鐓^(qū)一個(gè)大版本的參數(shù)沒有變化。
- 【可編程性】可以對(duì)組件(JSON/YAML)對(duì)象的模板進(jìn)行修補(bǔ)。如果你啟用動(dòng)態(tài)kubelet配置選項(xiàng),修改參數(shù)會(huì)自動(dòng)生效,不需要重啟服務(wù)。
- 【可配置性】許多類型的配置不能表示為key-value形式。
(2)計(jì)劃切換到kubeadm部署
- 使用kubeadm對(duì)k8s集群的生命周期管理,減少自身維護(hù)集群的成本。
- 使用kubeadm的證書管理,如證書上傳到secret里減少證書在主機(jī)拷貝的時(shí)間消耗和重新生成證書功能等。
- 使用kubeadm的kubeconfig生成admin kubeconfig文件。
- kubeadm其它功能如image管理、配置中心upload-config、自動(dòng)給控制節(jié)點(diǎn)打標(biāo)簽和污點(diǎn)等。
- 安裝coredns和kube-proxy addons。
(3)ansible使用規(guī)范
- 使用ansible自帶模塊處理部署邏輯。
- 避免使用hostvars。
- 避免使用delegate_to。
- 啟用–limit 模式。
- 等等。
2.2 CI 矩陣測試
部署出來的集群,需要進(jìn)行大量的場景測試和模擬。保證線上環(huán)境變更的可靠性和穩(wěn)定性。
CI矩陣部分測試案例如下。
(1)語法測試:
- ansible-lint
- shellcheck
- yamllint
- syntax-check
- pep8
(2)集群部署測試:
- 部署集群
- 擴(kuò)縮容控制節(jié)點(diǎn)、計(jì)算節(jié)點(diǎn)、etcd
- 升級(jí)集群
- etcd、docker、k8s和addons參數(shù)變更等
(3)性能和功能測試:
- 檢查kube-apiserver是否正常工作
- 檢查節(jié)點(diǎn)之間網(wǎng)絡(luò)是否正常
- 檢查計(jì)算節(jié)點(diǎn)是否正常
- k8s e2e測試
- k8s conformance 測試
- 其他測試
這里利用了GitLab、gitlab-runner[2]、ansible和kubevirt[3]等開源軟件構(gòu)建了CI流程。
詳細(xì)的部署步驟如下:
- 在k8s集群部署gitlab-runner,并對(duì)接GitLab倉庫。
- 在k8s集群部署Containerized-Data-Importer (CDI)[4]組件,用于創(chuàng)建pvc的存儲(chǔ)虛擬機(jī)的映像文件。
- 在k8s集群部署kubevirt,用于創(chuàng)建虛擬機(jī)。
- 在代碼倉庫編寫gitlab-ci.yaml[5], 規(guī)劃集群測試矩陣。
如上圖所示,當(dāng)開發(fā)人員在GitLab提交PR時(shí)會(huì)觸發(fā)一系列操作。這里主要展示了創(chuàng)建虛擬機(jī)和集群部署。其實(shí)在我們的集群還部署了語法檢查和性能測試gitlab-runner,通過這些gitlab-runner創(chuàng)建CI的job去執(zhí)行CI流程。
具體CI流程如下:
- 開發(fā)人員提交PR。
- 觸發(fā)CI自動(dòng)進(jìn)行ansible語法檢查。
- 執(zhí)行ansible腳本去創(chuàng)建namespace,pvc和kubevirt的虛擬機(jī)模板,最終虛擬機(jī)在k8s上運(yùn)行。這里主要用到ansible的k8s模塊[6]去管理這些資源的創(chuàng)建和銷毀。
- 調(diào)用ansible腳本去部署k8s集群。
- 集群部署完進(jìn)行功能驗(yàn)證和性能測試等。
- 銷毀kubevirt、pvc等資源。即刪除虛擬機(jī),釋放資源。
如上圖所示,當(dāng)開發(fā)人員提交多個(gè)PR時(shí),會(huì)在k8s集群中創(chuàng)建多個(gè)job,每個(gè)job都會(huì)執(zhí)行上述的CI測試,互相不會(huì)產(chǎn)生影響。這種主要使用kubevirt的能力,實(shí)現(xiàn)了k8s on k8s的架構(gòu)。
kubevirt主要能力如下:
- 提供標(biāo)準(zhǔn)的K8s API,通過ansible的k8s模塊就可以管理這些資源的生命周期。
- 復(fù)用了k8s的調(diào)度能力,對(duì)資源進(jìn)行了管控。
- 復(fù)用了k8s的網(wǎng)絡(luò)能力,以namespace隔離,每個(gè)集群網(wǎng)絡(luò)互相不影響。
三、Kubernetes-Operator 實(shí)踐
3.1 Operator 介紹
Operator是一種用于特定應(yīng)用的控制器,可以擴(kuò)展 K8s API的功能,來代表k8s的用戶創(chuàng)建、配置和管理復(fù)雜應(yīng)用的實(shí)例?;趉8s的資源和控制器概念構(gòu)建,又涵蓋了特定領(lǐng)域或應(yīng)用本身的知識(shí)。用于實(shí)現(xiàn)其所管理的應(yīng)用生命周期的自動(dòng)化。
總結(jié) Operator功能如下:
- kubernetes controller
- 部署或者管理一個(gè)應(yīng)用,如數(shù)據(jù)庫、etcd等
- 用戶自定義的應(yīng)用生命周期管理
- 部署
- 升級(jí)
- 擴(kuò)縮容
- 備份
- 自我修復(fù)
- 等等
3.2 Kubernetes-Operator CR 介紹
kubernetes-operator的使用很多自定義的CR資源和控制器,這里簡單的介紹功能和作用。
【ClusterDeployment】: 管理員配置的唯一的CR,其中MachineSet、Machine和Cluster它的子資源或者關(guān)聯(lián)資源。ClusterDeployment是所有的配置參數(shù)入口,定義了如etcd、k8s、lb、集群版本、網(wǎng)路和addons等所有配置。
【MachineSet】:集群角色的集合包括控制節(jié)點(diǎn)、計(jì)算節(jié)點(diǎn)和etcd的配置和執(zhí)行狀態(tài)。
【Machine】:每臺(tái)機(jī)器的具體信息,包括所屬的角色、節(jié)點(diǎn)本身信息和執(zhí)行的狀態(tài)。
【Cluster】:和ClusterDeployment對(duì)應(yīng),它的status定義為subresource,減少
clusterDeployment的觸發(fā)壓力。主要用于存儲(chǔ)ansible執(zhí)行器執(zhí)行腳本的狀態(tài)。
【ansible執(zhí)行器】:主要包括k8s自身的job、configMap、Secret和自研的job控制器。其中job主要用來執(zhí)行ansible的腳本,因?yàn)閗8s的job的狀態(tài)有成功和失敗,這樣job 控制器很好觀察到ansible執(zhí)行的成功或者失敗,同時(shí)也可以通過job對(duì)應(yīng)pod日志去查看ansible的執(zhí)行詳細(xì)流程。configmap主要用于存儲(chǔ)ansible執(zhí)行時(shí)依賴的inventory和變量,掛在到j(luò)ob上。secret主要存儲(chǔ)登陸主機(jī)的密鑰,也是掛載到j(luò)ob上。
【擴(kuò)展控制器】:主要用于擴(kuò)展集群管理的功能的附加控制器,在部署kubernetes-operator我們做了定制,可以選擇自己需要的擴(kuò)展控制器。比如addons控制器主要負(fù)責(zé)addon插件的安裝和管理。clusterinstall主要生成ansible執(zhí)行器。remoteMachineSet用于多集群管理,同步元數(shù)據(jù)集群和業(yè)務(wù)集群的machine狀態(tài)。還有其它的如對(duì)接公有云、dns、lb等控制器。
3.3 Kubernetes-Operator 架構(gòu)
vivo的應(yīng)用分布在數(shù)據(jù)中心的多個(gè)k8s集群上,提供了具有集中式多云管理、統(tǒng)一調(diào)度、高可用性、故障恢復(fù)等關(guān)鍵特性。主要搭建了一個(gè)元數(shù)據(jù)集群的pass平臺(tái)去管理多個(gè)業(yè)務(wù)k8s集群。在眾多關(guān)鍵組件中,其中kubernetes-operator就部署在元數(shù)據(jù)集群中,同時(shí)單獨(dú)運(yùn)行了machine控制器去管理物理資源。
下面舉例部分場景如下:
場景一:
當(dāng)大量應(yīng)用遷移到kubernets上,管理員評(píng)估需要擴(kuò)容集群。首先需要審批物理資源并通過pass平臺(tái)生成對(duì)應(yīng)machine的CR資源,此時(shí)的物理機(jī)處于備機(jī)池里,machine CR的狀態(tài)為空閑狀態(tài)。當(dāng)管理員創(chuàng)建ClusterDeploment時(shí)所屬的MachineSet會(huì)去關(guān)聯(lián)空閑狀態(tài)的machine,拿到空閑的machine資源,我們就可以觀測到當(dāng)前需要操作機(jī)器的IP地址生成對(duì)應(yīng)的inventory和變量,并創(chuàng)建configmap并掛載給job。執(zhí)行擴(kuò)容的ansible腳本,如果job成功執(zhí)行完會(huì)去更新machine的狀態(tài)為deployed。同時(shí)跨集群同步node的控制器會(huì)檢查當(dāng)前的擴(kuò)容的node是否為ready,如果為ready,會(huì)更新當(dāng)前的machine為Ready狀態(tài),才完成整個(gè)擴(kuò)容流程。
場景二:
當(dāng)其中一個(gè)業(yè)務(wù)集群出現(xiàn)故障,無法提供服務(wù),觸發(fā)故障恢復(fù)流程,走統(tǒng)一資源調(diào)度。同時(shí)業(yè)務(wù)的策略是分配在多個(gè)業(yè)務(wù)集群,同時(shí)配置了一個(gè)備用集群,并沒有在備用集群上分配實(shí)例,備用集群并不實(shí)際存在。
有如下2種情況:
- 其它的業(yè)務(wù)集群可以承載故障集群的業(yè)務(wù),kubernetes-operator不需要執(zhí)行任何操作。
- 如果其他業(yè)務(wù)集群不能承載故障集群的業(yè)務(wù)。容器平臺(tái)開始預(yù)估資源,調(diào)用kubernetes-operator創(chuàng)建集群,即創(chuàng)建clusterDeployment從備機(jī)池里選擇物理機(jī)器,觀測到當(dāng)前需要操作機(jī)器的IP地址生成對(duì)應(yīng)的inventory和變量,創(chuàng)建configmap并掛載給job。執(zhí)行集群安裝的ansible腳本, 集群正常部署完成后開始業(yè)務(wù)的遷移。
3.4 Kubernetes-Operator 執(zhí)行流程
- 集群管理員或者容器平臺(tái)觸發(fā)創(chuàng)建ClusterDeployment的CR,去定義當(dāng)前集群的操作。
- ClusterDeployment控制器感知到變化進(jìn)入控制器。
- 開始創(chuàng)建machineSet和關(guān)聯(lián)machine 資源。
- ClusterInstall 控制器感知ClusterDeployment和Machineset的變化,開始統(tǒng)計(jì)machine資源,創(chuàng)建configmap和job,參數(shù)指定操作的ansible yml入口,執(zhí)行擴(kuò)縮容、升級(jí)和安裝等操作。
- 調(diào)度器感知到j(luò)ob創(chuàng)建的pod資源,進(jìn)行調(diào)度。
- 調(diào)度器調(diào)用k8s客戶端更新pod的binding資源。
- kubelet感知到pod的調(diào)度結(jié)果,創(chuàng)建pod開始執(zhí)行ansible playbook。
- job controller感知job的執(zhí)行狀態(tài),更新ClusterDeployment狀態(tài)。一般策略下job controller會(huì)去清理configmap和job資源。
- NodeHealthy感知k8s的node是否為ready,并同步machine的狀態(tài)。
- addons 控制器感知集群是否ready,如果為ready去執(zhí)行相關(guān)的addons插件的安裝和升級(jí)。
四、總結(jié)
vivo大規(guī)模的K8s集群運(yùn)維實(shí)踐中,從底層的集群部署工具的優(yōu)化,到大量的CI矩陣測試保證了我們線上集群運(yùn)維的安全和穩(wěn)定性。采用了K8s托管K8s的方式來自動(dòng)化管理集群(K8s as a service),當(dāng)operator檢測當(dāng)前的集群狀態(tài),判斷是否與目標(biāo)一致,出現(xiàn)不一致時(shí),operator會(huì)發(fā)起具體的操作流程,驅(qū)動(dòng)整個(gè)集群達(dá)到目標(biāo)狀態(tài)。
當(dāng)前vivo的應(yīng)用主要分布在自建的數(shù)據(jù)中心的多個(gè)K8s集群中,隨著應(yīng)用的不斷的增長和復(fù)雜的業(yè)務(wù)場景,需要提供跨自建機(jī)房和云的多個(gè)K8s集群去運(yùn)行原云生的應(yīng)用程序。就需要Kubernetes-Operator提供對(duì)接公有云基礎(chǔ)設(shè)施、apiserver的負(fù)載均衡、網(wǎng)絡(luò)、dns和Cloud Provider 等。需要后續(xù)不斷完善,降低K8s集群的運(yùn)維難度。