使用Kubernetes三年,我們從中學到了什么?
2017年.我們構(gòu)建了第一個kubernetes集群,版本是1.9.4。有兩個集群,一個用裸機RHEL VMs運行,另一個用的是AWS EC2。時至今日,我們的Kubernetes基礎(chǔ)架構(gòu)團隊有超過400個虛擬機,遍布多個數(shù)據(jù)中心。這個平臺有高度可用且關(guān)鍵的軟件應(yīng)用和系統(tǒng),能管理運行近四百萬個活躍機器的大型實時網(wǎng)絡(luò)。
雖然kubernetes使我們的生活更輕松了,但過程會蠻艱辛,要經(jīng)過范式的改變。不僅要完全更迭我們的技能和工具,還有設(shè)計和思想。我們必須掌握許多新的科技,大幅擴充提升團隊和基礎(chǔ)設(shè)施。
回首用kubernetes產(chǎn)出的這三年,我們得出以下關(guān)鍵經(jīng)驗。
1. 應(yīng)用的奇怪案例
涉及到微服務(wù)和容器化,工程師們傾向于不使用Java,主要是因為它糟糕的內(nèi)存管理。然而Java已不同往日,它的容器適配性在幾年里已有提高。畢竟大多數(shù)系統(tǒng)都用Java運行,如Apache Kafka和Elasticsearch。
在2017-2018年,只有一些應(yīng)用在Java8運行。這些應(yīng)用通常很難適應(yīng)Docker內(nèi)存系統(tǒng),并且很容易因為堆內(nèi)存問題和異常的垃圾收集趨勢而崩潰。這是由于Java虛擬機不能遵守Linux cgroup和namespace造成的,而他們是容器化技術(shù)的核心。
然而甲骨文公司在這之后不斷提升Java容器化的適配性。Java8的后續(xù)補丁也引入了實驗性的Java虛擬機flag標示來解決這些問題:
- XX:+UnlockExperimentalVMOptions
- XX:+UseCGroupMemoryLimitForHeap
但Java仍名聲不佳,對比同行的python和Go,Java占用內(nèi)存且啟動速度慢。主要是由Java虛擬機的內(nèi)存管理及類加載器造成的。
現(xiàn)在如果必須選擇Java,我們會確保版本為11及以上,并且我們的Kubernetes內(nèi)存設(shè)定為Java虛擬機最大堆內(nèi)存( -Xmx )之上的1GB,以提供余量。也就是說,如果JVM使用8GB的堆內(nèi)存,則該應(yīng)用的Kubernetes資源限制就是9GB。有了它生活會更好。
圖源:unsplash
2. Kubernetes的生命周期升級
Kubernetes的生命周期管理很繁雜,比如它的升級和加強,特別體現(xiàn)在用裸機或者VM搭建的集群。在升級時,我們發(fā)現(xiàn)搭建新集群最簡單的方式就是用最新的版本并將工作負載從舊版本轉(zhuǎn)移到新版本。努力和計劃在模型內(nèi)進行升級是不值得的。
Kubernetes有多個運行插件,需要與升級同步。Docker、Calico或Flannel之類的CNI插件都需要仔細地將它們組合在一起才能正常工作。雖然一些項目可以使它變得更容易運行,如Kubespray、Kubeone、Kops和Kubeaws,但它們都有缺點。
我們在RHEL VM上使用Kubespray搭建了集群。Kubespray有關(guān)于搭建、添加、刪除新節(jié)點、版本升級的指導手冊,基本覆蓋了我們使用Kubernetes產(chǎn)出需要的所有內(nèi)容。但升級手冊包含了免責聲明,提醒我們即使變更很小也不要忽略任何版本,也就是說要更新所有中間版本才能使用目標版本。
訣竅就是,當你計劃使用或已經(jīng)使用了Kubernetes,想想生命周期活動以及你的方案如何這些問題。相對來說用它來搭建和運行集群是比較簡單的,但生命周期的維護仍是有著諸多活動部件的全新領(lǐng)域。
3. 構(gòu)建和部署
圖源:unsplash
要做好重新設(shè)計整個搭建和部署管道的準備。我們的搭建過程和部署必須經(jīng)歷Kubernetes的完全轉(zhuǎn)型,不僅對Jenkins管道進行了大量的重組,還使用了Helm等新工具,策劃了新的git流程和構(gòu)建,標記了docker鏡像,并對helm部署圖表進行了版本控制。
不僅需要維護代碼,還需要策略來維護Kubernetes部署文件、Docker文件、Docker鏡像和Helm圖表,并設(shè)計一種將它們鏈接起來的方法。
在幾次迭代后我們有了如下設(shè)計:放置應(yīng)用程序代碼及其Helm圖表于單獨的git存儲庫中,這使我們可以分別對它們進行版本控制。(語義版本號)
然后,我們將圖表版本的地圖與應(yīng)用程序版本一起保存,并使用它來跟蹤發(fā)布。例如,將app-1.2.0與charts-1.1.0一起部署。如果僅更改Helm值文件,則僅更改圖表的補丁程序版本(例如,從1.1.0到1.1.1)。所有這些版本均由每個存儲庫RELEASE.txt發(fā)行說明。
Apache Kafka或Redis的代碼之類的系統(tǒng)應(yīng)用程序的工作方式有所不同,我們未構(gòu)建或修改其代碼。也就是說,由于Docker標簽只是Helm chart版本控制的一部分,我們沒有兩個git存儲庫。如果我們曾經(jīng)更改了docker標簽進行升級,我們將在圖表標簽中增加主要版本。
4. 存活和就緒探針(雙刃劍)
Kubernetes的存活和就緒探針是能自動解決系統(tǒng)問題的出色功能點??梢栽诎l(fā)生故障時重新啟動容器,并將流量從異常事故中轉(zhuǎn)移。但在某些故障情況下,探針可能是雙刃劍,并影響應(yīng)用程序的啟動和恢復,尤其是有狀態(tài)應(yīng)用程序,例如消息平臺或數(shù)據(jù)庫。
Kafka系統(tǒng)就是受害者。我們運行了一個有replicationFactor of 3 和minInSyncReplica of 2的 3 Broker 3 Zookeeper狀態(tài)集,當系統(tǒng)意外故障或崩潰導致Kafka啟動時會發(fā)生此問題。這導致它在啟動期間運行其他腳本來修復損壞的索引,根據(jù)嚴重程度不同,該過程可能需要10到30分鐘。
由于時間耗費增加,存活探針將不斷失敗,從而向Kafka發(fā)出終止信號以重新啟動,這一并阻止了Kafka系統(tǒng)修改索引和完全啟動。
唯一的解決方案是在存活探針設(shè)置中設(shè)置 initialDelaySeconds,從而在容器啟動后延遲探針評估。但是問題在于當然很難給出具體時間,有些恢復甚至需要一個小時,因此我們需要留足空間來計算時間。但是initialDelaySeconds增加的時間越多,速度就越慢,因為啟動失敗時Kubernetes需要更長的時間來重新啟動容器。
最新的幾個版本中,Kubernetes引入了第三種探針“啟動探針”來解決這個問題。在alpha from 1.16 和 beta from 1.18 中都可用。啟動探針可以使存活和就緒探針在容器啟動前停止運行,保證應(yīng)用程序的啟動不受干擾。
5. 公開外部IP
圖源:unsplash
我們了解到,使用靜態(tài)外部IP公開服務(wù)會極大損害內(nèi)核的連接跟蹤機制。除非詳細計劃,否則它只會大規(guī)模崩潰。
集群在Calico for CNI和BGP運行,作為Kubernetes內(nèi)部路由協(xié)議,并與邊緣路由器搭配。而Kubeproxy,我們使用IP Tables模式。我們在Kubernetes有著龐大的服務(wù),該服務(wù)通過外部IP公開,每天處理數(shù)百萬個連接。
所有的源網(wǎng)絡(luò)地址轉(zhuǎn)換(NAT)和偽裝來自軟件定義網(wǎng)絡(luò),Kubernetes需要一個機制來跟蹤所有邏輯流。為此,它使用內(nèi)核的Conntrack and netfilter工具來管理與靜態(tài)IP的外部連接,接著將其轉(zhuǎn)換為內(nèi)部服務(wù)IP,再轉(zhuǎn)為pod IP。這些都通過conntrack表和IP表完成。
但這個conntrack表有限制。當達到極限時,Kubernetes集群(下有OS內(nèi)核)將不能接受新連接。在RHEL上,可以通過這種方式進行檢查:
- $ sysctlnet.netfilter.nf_conntrack_countnet.netfilter.nf_conntrack_maxnet.netfilter.nf_conntrack_count = 167012
- net.netfilter.nf_conntrack_max = 262144
解決該問題的方法是匹配帶有邊緣路由器的多個節(jié)點,使你的靜態(tài)IP的傳入連接遍及整個群集。因此,如果集群中有大量虛擬機,累積起來就可以擁有一個大的conntrack表來處理大量的傳入連接。
在2017年剛開始使用的時候,這個問題完全難住了我們。但2019年Calico發(fā)布了一份相關(guān)的詳盡研究,題目就是《為什么conntrack不再友好》。
我們是否一定需要Kubernetes?
圖源:debian
三年來,我們?nèi)栽诿刻炖^續(xù)探索和學習新東西。這個復雜的平臺中存在著一系列挑戰(zhàn),尤其是構(gòu)建和維護環(huán)境的開銷很大。這會改變你設(shè)計、思考、構(gòu)建的方式,并且會需要團隊不斷提升擴充來契合轉(zhuǎn)變。
然而,如果你在云端并且使用Kubernetes作為“服務(wù)”,那么大部分平臺維護的開銷都可以下降,例如“如何擴展內(nèi)部網(wǎng)絡(luò)CIDR?”或“如何升級我的Kubernetes版本?”
應(yīng)該問的第一個問題是:“是否一定需要Kubernetes?”這可以幫助你評估問題,判斷用Kubernetes來解決它們是否必要。Kubernetes的升級轉(zhuǎn)換等并不便宜。你的用例一定要真正配得上這筆支出并且值得使用這個平臺。如果值得,那么Kubernetes可以極大地提高生產(chǎn)力。
記住,為了使用科技而使用是沒有意義的。
本文轉(zhuǎn)載自微信公眾號「讀芯術(shù)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系讀芯術(shù)公眾號。