Kubernetes使用OkHttp客戶端進行網(wǎng)絡(luò)負(fù)載均衡
在一次內(nèi)部Java服務(wù)審計中,我們發(fā)現(xiàn)一些請求沒有在Kubernetes(K8s)網(wǎng)絡(luò)上正確地實現(xiàn)負(fù)載均衡。導(dǎo)致我們深入研究的問題是HTTP 5xx錯誤率的急劇上升,由于CPU使用率非常高,垃圾收集事件的數(shù)量很多以及超時,但這僅發(fā)生在一些特定的Pod中。
這種情況并不在所有情況下都可見,因為它影響到多Pod服務(wù),源Pod和目標(biāo)Pod的數(shù)量不同。在本博文中,我將討論我們采取的措施來負(fù)載均衡這組服務(wù)和Pod。
在我們的部署中,請求在Pod之間是如何均衡的?
兩個源Pod向六個目標(biāo)Pod發(fā)送請求。
可以清楚地看到請求分布在目標(biāo)Pod之間存在不均衡。
但為什么會這樣?
K8s負(fù)載均衡器(IPVS代理模式)的默認(rèn)負(fù)載均衡調(diào)度程序設(shè)置為輪詢(round robin)。IPVS提供了更多的選項來均衡流量到Pod后端。在測試這些選項時,我們發(fā)現(xiàn)當(dāng)涉及到我們的服務(wù)時,不管配置如何,行為都相同,這些服務(wù)之間使用內(nèi)部路由進行通信。
到底發(fā)生了什么?K8s中的IPVS根據(jù)連接來平衡流量,這在大多數(shù)情況下都表現(xiàn)得相當(dāng)不錯。我們的服務(wù)使用OkHttp作為相互通信的HTTP客戶端。我們的問題與這個HTTP客戶端的行為方式有關(guān)。使用默認(rèn)配置,它會創(chuàng)建到服務(wù)器的連接,如果您不想在代碼中顯式關(guān)閉連接,因為這太昂貴,那么它會保持并重新建立到先前合作伙伴的連接。這意味著客戶端嘗試保持與目標(biāo)的連接,并通過該特定連接發(fā)送請求。通常情況下,它會創(chuàng)建1:1的連接,這在K8s方面沒有均衡。
該怎么辦?
如果您需要擴展或希望使您的服務(wù)得到適當(dāng)?shù)呢?fù)載均衡,您需要在客戶端端更新配置。OkHttp提供了ConnectionPool功能。當(dāng)使用ConnectionPool選項時,連接將在有限的時間段內(nèi)建立,然后重復(fù)設(shè)置一個新的連接,因此IPVS可以進行負(fù)載均衡,因為它有大量的新連接,應(yīng)該根據(jù)IPVS調(diào)度程序路由到目標(biāo)。基本上,它的工作方式類似于機關(guān)槍而不是激光束。
我們在發(fā)布此更新后的效果如何?
使用更新的HTTP客戶端和默認(rèn)IPVS調(diào)度程序在多Pod服務(wù)之間實現(xiàn)了負(fù)載均衡的連接。
到底做了什么改變?
我們進行了大量的測試,使用各種配置來測量響應(yīng)時間和性能開銷,以確保負(fù)載均衡。下面是主要的代碼更改,看起來沒有明顯的性能開銷。
代碼更改示例
有一個選項可以設(shè)置調(diào)度程序,以便能夠并行發(fā)送更多的請求。在我們的情況下,這最終會建立一組最近關(guān)閉的連接,然后繼續(xù)只使用一個連接。此外,我們試圖防止過于頻繁地打開新連接,因為執(zhí)行請求比打開新連接要少要求得多。
結(jié)果如何?
網(wǎng)絡(luò)和資源的使用現(xiàn)在比以前更加平衡 - 沒有巨大或持續(xù)很長時間的峰值,也沒有出現(xiàn)只影響部署中某些Pod的“嘈雜鄰居”效應(yīng)?,F(xiàn)在幾乎所有的Pod都以幾乎相同的方式被利用,因此我們能夠減少我們的部署中的Pod數(shù)量。我們知道這并不完美,但對于我們的用例來說已經(jīng)足夠好,因為它不會給服務(wù)或IPVS負(fù)載均衡器帶來明顯的性能開銷。
現(xiàn)在的Pod上的請求負(fù)載均衡
結(jié)論
定期進行徹底的服務(wù)審計是有益的,因為它可以揭示出未來對所有服務(wù)有益的優(yōu)化點,并在解決那些本應(yīng)該立即運行的功能的奇怪癥狀時為您節(jié)省時間。此外,花些時間查看文檔,測試,討論并了解在使用客戶端庫時關(guān)于連接設(shè)置和處理的默認(rèn)設(shè)置的影響,以確保它們將按照您的預(yù)期行事。