淺談 K8S 下 gRPC 負(fù)載均衡問題
本文轉(zhuǎn)載自微信公眾號(hào)「架構(gòu)技術(shù)漫談」,作者LA0WAN9。轉(zhuǎn)載本文請(qǐng)聯(lián)系架構(gòu)技術(shù)漫談公眾號(hào)。
一般來說,在 K8S 下部署服務(wù)是很簡(jiǎn)單的事兒,但是如果部署的是一個(gè) gRPC 服務(wù)的話,那么稍不留神就可能掉坑里,個(gè)中緣由,且聽我慢慢道來。
在 K8S 下部署服務(wù),缺省會(huì)被分配一個(gè)地址(也就是 ClusterIP[1]),客戶端的請(qǐng)求會(huì)發(fā)送給它,然后再通過負(fù)載均衡轉(zhuǎn)發(fā)給后端某個(gè) pod:
ClusterIP
如果是 HTTP/1.1 之類的服務(wù),那么 ClusterIP 完全沒有問題;但是如果是 gRPC 服務(wù),那么 ClusterIP 會(huì)導(dǎo)致負(fù)載失衡,究其原因,是因?yàn)?gRPC 是基于 HTTP/2 的,多個(gè)請(qǐng)求在一個(gè) TCP 連接上多路復(fù)用,一旦 ClusterIP 和某個(gè) pod 建立了 gRPC 連接后,因?yàn)槎嗦窂?fù)用的緣故,所以后續(xù)其它請(qǐng)求也都會(huì)被轉(zhuǎn)發(fā)給此 pod,結(jié)果其它 pod 則完全被忽略了。
看到這里,有的讀者可能會(huì)有疑問:HTTP/1.1 不是實(shí)現(xiàn)了基于 KeepAlive 的連接復(fù)用么?為什么 HTTP/1.1 的復(fù)用沒問題,而 HTTP/2 的復(fù)用就有問題?答案是 HTTP/1.1 的 復(fù)用是串行的,當(dāng)請(qǐng)求到達(dá)的時(shí)候,如果沒有空閑連接那么就新創(chuàng)建一個(gè)連接,如果有空閑連接那么就可以復(fù)用,同一個(gè)時(shí)間點(diǎn),連接里最多只能承載有一個(gè)請(qǐng)求,結(jié)果是 HTTP/1.1 可以連接多個(gè) pod;而 HTTP/2 的復(fù)用是并行的,當(dāng)請(qǐng)求到達(dá)的時(shí)候,如果沒有連接那么就創(chuàng)建連接,如果有連接,那么不管其是否空閑都可以復(fù)用,同一個(gè)時(shí)間點(diǎn),連接里可以承載多個(gè)請(qǐng)求,結(jié)果是 HTTP/2 僅僅連接了一個(gè) pod。
了解了 K8S 下 gRPC 負(fù)載均衡問題的來龍去脈,我們不難得出解決方案:
在 Proxy 中實(shí)現(xiàn)負(fù)載均衡:采用 Envoy 做代理,和每臺(tái)后端服務(wù)器保持長(zhǎng)連接,當(dāng)客戶端請(qǐng)求到達(dá)時(shí),代理服務(wù)器依照規(guī)則轉(zhuǎn)發(fā)請(qǐng)求給后端服務(wù)器,從而實(shí)現(xiàn)負(fù)載均衡。
Proxy
在 Client 中實(shí)現(xiàn)負(fù)載均衡:把服務(wù)部署成 headless service[2],這樣服務(wù)就有了一個(gè)域名,然后客戶端通過域名訪問 gRPC 服務(wù),DNS resolver 會(huì)通過 DNS 查詢后端多個(gè)服務(wù)器地址,然后通過算法來實(shí)現(xiàn)負(fù)載均衡。
Client
兩種方案的優(yōu)缺點(diǎn)都很明顯:Proxy 方案結(jié)構(gòu)清晰,客戶端不需要了解后端服務(wù)器,對(duì)架構(gòu)沒有侵入性,但是性能會(huì)因?yàn)榇嬖谵D(zhuǎn)發(fā)而打折扣;Client 方案結(jié)構(gòu)復(fù)雜,客戶端需要了解后端服務(wù)器,對(duì)架構(gòu)有侵入性,但是性能更好。
參考資料
[1]ClusterIP: https://kubernetes.io/docs/concepts/services-networking/service/
[2]headless service: https://kubernetes.io/docs/concepts/services-networking/serv