一伐蒂、問題
首先,我們思考這樣一個問題:
訪問k8s集群中的pod肛鹏, 客戶端需要知道pod地址逸邦,需要感知pod的狀態(tài)恩沛。那如何獲取各個pod的地址?若某一node上的pod故障缕减,客戶端如何感知雷客?
二、k8s service
什么是service
是發(fā)現(xiàn)后端pod服務(wù)桥狡;
是為一組具有相同功能的容器應(yīng)用提供一個統(tǒng)一的入口地址搅裙;
是將請求進行負(fù)載分發(fā)到后端的各個容器應(yīng)用上的控制器。
對service的訪問來源
訪問service的請求來源有兩種:k8s集群內(nèi)部的程序(Pod)和 k8s集群外部的程序裹芝。
service類型
采用微服務(wù)架構(gòu)時部逮,作為服務(wù)所有者,除了實現(xiàn)業(yè)務(wù)邏輯以外局雄,還需要考慮如何把服務(wù)發(fā)布到k8s集群或者集群外部甥啄,使這些服務(wù)能夠被k8s集群內(nèi)的應(yīng)用存炮、其他k8s集群的應(yīng)用以及外部應(yīng)用使用炬搭。因此k8s提供了靈活的服務(wù)發(fā)布方式,用戶可以通過ServiceType來指定如何來發(fā)布服務(wù)穆桂,類型有以下幾種:
● ClusterIP:提供一個集群內(nèi)部的虛擬IP以供Pod訪問(service默認(rèn)類型)宫盔。
service 結(jié)構(gòu)如下:
● NodePort:在每個Node上打開一個端口以供外部訪問
Kubernetes將會在每個Node上打開一個端口并且每個Node的端口都是一樣的,通過:NodePort的方式Kubernetes集群外部的程序可以訪問Service享完。
service 定義如下:
● LoadBalancer:通過外部的負(fù)載均衡器來訪問
service selector
service通過selector和pod建立關(guān)聯(lián)灼芭。
k8s會根據(jù)service關(guān)聯(lián)到pod的podIP信息組合成一個endpoint。
若service定義中沒有selector字段般又,service被創(chuàng)建時彼绷,endpoint controller不會自動創(chuàng)建endpoint。
service負(fù)載分發(fā)策略
service 負(fù)載分發(fā)策略有兩種:
RoundRobin:輪詢模式茴迁,即輪詢將請求轉(zhuǎn)發(fā)到后端的各個pod上(默認(rèn)模式)寄悯;
SessionAffinity:基于客戶端IP地址進行會話保持的模式,第一次客戶端訪問后端某個pod堕义,之后的請求都轉(zhuǎn)發(fā)到這個pod上猜旬。
三、服務(wù)發(fā)現(xiàn)
k8s服務(wù)發(fā)現(xiàn)方式
雖然Service解決了Pod的服務(wù)發(fā)現(xiàn)問題倦卖,但不提前知道Service的IP洒擦,怎么發(fā)現(xiàn)service服務(wù)呢?
k8s提供了兩種方式進行服務(wù)發(fā)現(xiàn):
● 環(huán)境變量: 當(dāng)創(chuàng)建一個Pod的時候怕膛,kubelet會在該Pod中注入集群內(nèi)所有Service的相關(guān)環(huán)境變量熟嫩。需要注意的是,要想一個Pod中注入某個Service的環(huán)境變量褐捻,則必須Service要先比該Pod創(chuàng)建掸茅。這一點洋侨,幾乎使得這種方式進行服務(wù)發(fā)現(xiàn)不可用。
例如:
一個ServiceName為redis-master的Service倦蚪,對應(yīng)的ClusterIP:Port為10.0.0.11:6379希坚,則其在pod中對應(yīng)的環(huán)境變量為:
REDIS_MASTER_SERVICE_HOST=10.0.0.11 REDIS_MASTER_SERVICE_PORT=6379 REDIS_MASTER_PORT=tcp://10.0.0.11:6379 REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379 REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379 REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
● DNS:可以通過cluster add-on的方式輕松的創(chuàng)建KubeDNS來對集群內(nèi)的Service進行服務(wù)發(fā)現(xiàn)————這也是k8s官方強烈推薦的方式。為了讓Pod中的容器可以使用kube-dns來解析域名陵且,k8s會修改容器的/etc/resolv.conf配置裁僧。
k8s服務(wù)發(fā)現(xiàn)原理
● endpoint
endpoint是k8s集群中的一個資源對象,存儲在etcd中慕购,用來記錄一個service對應(yīng)的所有pod的訪問地址聊疲。
service配置selector,endpoint controller才會自動創(chuàng)建對應(yīng)的endpoint對象沪悲;否則获洲,不會生成endpoint對象.
例如,k8s集群中創(chuàng)建一個名為k8s-classic-1113-d3的service殿如,就會生成一個同名的endpoint對象贡珊,如下圖所示。其中ENDPOINTS就是service關(guān)聯(lián)的pod的ip地址和端口涉馁。
● endpoint controller
endpoint controller是k8s集群控制器的其中一個組件门岔,其功能如下:
負(fù)責(zé)生成和維護所有endpoint對象的控制器
負(fù)責(zé)監(jiān)聽service和對應(yīng)pod的變化
監(jiān)聽到service被刪除,則刪除和該service同名的endpoint對象
監(jiān)聽到新的service被創(chuàng)建烤送,則根據(jù)新建service信息獲取相關(guān)pod列表寒随,然后創(chuàng)建對應(yīng)endpoint對象
監(jiān)聽到service被更新,則根據(jù)更新后的service信息獲取相關(guān)pod列表帮坚,然后更新對應(yīng)endpoint對象
監(jiān)聽到pod事件妻往,則更新對應(yīng)的service的endpoint對象,將podIp記錄到endpoint中
四试和、負(fù)載均衡
kube-proxy
kube-proxy負(fù)責(zé)service的實現(xiàn)讯泣,即實現(xiàn)了k8s內(nèi)部從pod到service和外部從node port到service的訪問。
kube-proxy采用iptables的方式配置負(fù)載均衡灰署,基于iptables的kube-proxy的主要職責(zé)包括兩大塊:一塊是偵聽service更新事件判帮,并更新service相關(guān)的iptables規(guī)則,一塊是偵聽endpoint更新事件溉箕,更新endpoint相關(guān)的iptables規(guī)則(如 KUBE-SVC-鏈中的規(guī)則)晦墙,然后將包請求轉(zhuǎn)入endpoint對應(yīng)的Pod。如果某個service尚沒有Pod創(chuàng)建肴茄,那么針對此service的請求將會被drop掉晌畅。
kube-proxy的架構(gòu)如下:
kube-proxy iptables
kube-proxy監(jiān)聽service和endpoint的變化,將需要新增的規(guī)則添加到iptables中寡痰。
kube-proxy只是作為controller抗楔,而不是server棋凳,真正服務(wù)的是內(nèi)核的netfilter,體現(xiàn)在用戶態(tài)則是iptables连躏。
kube-proxy的iptables方式也支持RoundRobin(默認(rèn)模式)和SessionAffinity負(fù)載分發(fā)策略剩岳。
kubernetes只操作了filter和nat表。
Filter:在該表中入热,一個基本原則是只過濾數(shù)據(jù)包而不修改他們拍棕。filter table的優(yōu)勢是小而快,可以hook到input勺良,output和forward绰播。這意味著針對任何給定的數(shù)據(jù)包,只有可能有一個地方可以過濾它尚困。
NAT:此表的主要作用是在PREROUTING和POSTROUNTING的鉤子中蠢箩,修改目標(biāo)地址和原地址。與filter表稍有不同的是事甜,該表中只有新連接的第一個包會被修改谬泌,修改的結(jié)果會自動apply到同一連接的后續(xù)包中。
kube-proxy對iptables的鏈進行了擴充讳侨,自定義了KUBE-SERVICES呵萨,KUBE-NODEPORTS奏属,KUBE-POSTROUTING跨跨,KUBE-MARK-MASQ和KUBE-MARK-DROP五個鏈,并主要通過為KUBE-SERVICES chain增加rule來配制traffic routing 規(guī)則囱皿。我們可以看下自定義的這幾個鏈的作用:
KUBE-MARK-DROP - [0:0] /*對于未能匹配到跳轉(zhuǎn)規(guī)則的traffic set mark 0x8000勇婴,有此標(biāo)記的數(shù)據(jù)包會在filter表drop掉*/KUBE-MARK-MASQ - [0:0] /*對于符合條件的包 set mark 0x4000, 有此標(biāo)記的數(shù)據(jù)包會在KUBE-POSTROUTING chain中統(tǒng)一做MASQUERADE*/KUBE-NODEPORTS - [0:0] /*針對通過nodeport訪問的package做的操作*/KUBE-POSTROUTING - [0:0]KUBE-SERVICES - [0:0] /*操作跳轉(zhuǎn)規(guī)則的主要chain*/
同時,kube-proxy也為默認(rèn)的prerouting嘱腥、output和postrouting chain增加規(guī)則耕渴,使得數(shù)據(jù)包可以跳轉(zhuǎn)至k8s自定義的chain,規(guī)則如下:
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
如果service類型為nodePort齿兔,(從LB轉(zhuǎn)發(fā)至node的數(shù)據(jù)包均屬此類)那么將KUBE-NODEPORTS鏈中每個目的地址是NODE節(jié)點端口的數(shù)據(jù)包導(dǎo)入這個“KUBE-SVC-”鏈:
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/es1:http" -m tcp --dport 32135 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/es1:http" -m tcp --dport 32135 -j KUBE-SVC-LAS23QA33HXV7KBL
Iptables chain支持嵌套并因為依據(jù)不同的匹配條件可支持多種分支橱脸,比較難用標(biāo)準(zhǔn)的流程圖來體現(xiàn)調(diào)用關(guān)系,建單抽象為下圖:
舉個例子分苇,在k8s集群中創(chuàng)建了一個名為my-service的服務(wù)添诉,其中:
service vip:10.11.97.177
對應(yīng)的后端兩副本pod ip:10.244.1.10、10.244.2.10
容器端口為:80
服務(wù)端口為:80
則kube-proxy為該service生成的iptables規(guī)則主要有以下幾條:
-A KUBE-SERVICES -d 10.11.97.177/32 -p tcp -m comment --comment "default/my-service: cluster IP" -m tcp --dport 80 -j KUBE-SVC-BEPXDJBUHFCSYIC3
-A KUBE-SVC-BEPXDJBUHFCSYIC3 -m comment --comment “default/my-service:” -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-U4UWLP4OR3LOJBXU //50%的概率輪詢后端pod
-A KUBE-SVC-BEPXDJBUHFCSYIC3 -m comment --comment "default/my-service:" -j KUBE-SEP-QHRWSLKOO5YUPI7O
-A KUBE-SEP-U4UWLP4OR3LOJBXU -s 10.244.1.10/32 -m comment --comment "default/my-service:" -j KUBE-MARK-MASQ
-A KUBE-SEP-U4UWLP4OR3LOJBXU -p tcp -m comment --comment "default/my-service:" -m tcp -j DNAT --to-destination 10.244.1.10:80
-A KUBE-SEP-QHRWSLKOO5YUPI7O -s 10.244.2.10/32 -m comment --comment "default/my-service:" -j KUBE-MARK-MASQ
-A KUBE-SEP-QHRWSLKOO5YUPI7O -p tcp -m comment --comment "default/my-service:" -m tcp -j DNAT --to-destination 10.244.2.10:80
kube-proxy通過循環(huán)的方式創(chuàng)建后端endpoint的轉(zhuǎn)發(fā)医寿,概率是通過probability后的1.0/float64(n-i)計算出來的栏赴,譬如有兩個的場景,那么將會是一個0.5和1也就是第一個是50%概率第二個是100%概率靖秩,如果是三個的話類似须眷,33%竖瘾、50%、100%花颗。
kube-proxy iptables的性能缺陷
k8s集群創(chuàng)建大規(guī)模服務(wù)時捕传,會產(chǎn)生很多iptables規(guī)則,非增量式更新會引入一定的時延扩劝。iptables規(guī)則成倍增長乐横,也會導(dǎo)致路由延遲帶來訪問延遲。大規(guī)模場景下今野,k8s 控制器和負(fù)載均衡都面臨這挑戰(zhàn)葡公。例如,若集群中有N個節(jié)點条霜,每個節(jié)點每秒有M個pod被創(chuàng)建催什,則控制器每秒需要創(chuàng)建NM個endpoints,需要增加的iptables則是NM的數(shù)倍宰睡。以下是k8s不同規(guī)模下訪問service的時延:
從上圖中可以看出蒲凶,當(dāng)集群中服務(wù)數(shù)量增長時,因為 IPTables天生不是被設(shè)計用來作為 LB 來使用的拆内,IPTables 規(guī)則則會成倍增長旋圆,這樣帶來的路由延遲會導(dǎo)致的服務(wù)訪問延遲增加,直到無法忍受麸恍。
目前有以下幾種解決方案灵巧,但各有不足:
● 將endpoint對象拆分成多個對像
優(yōu)點:減小了單個endpoint大小
缺點:增加了對象的數(shù)量和請求量
● 使用集中式負(fù)載均衡器
優(yōu)點:減少了跟apiserver的連接和請求數(shù)
缺點:服務(wù)路由中又增加了一跳,并且需要集中式LB有很高的性能和高可用性
● 定期任務(wù)抹沪,批量創(chuàng)建/更新endpoint
優(yōu)點:減少了每秒的處理數(shù)
缺點:在定期任務(wù)執(zhí)行的間隔時間內(nèi)刻肄,端對端延遲明顯增加
五、K8s 1.8 新特性——ipvs
ipvs與iptables的性能差異
隨著服務(wù)的數(shù)量增長融欧,IPTables 規(guī)則則會成倍增長敏弃,這樣帶來的問題是路由延遲帶來的服務(wù)訪問延遲,同時添加或刪除一條規(guī)則也有較大延遲噪馏。不同規(guī)模下麦到,kube-proxy添加一條規(guī)則所需時間如下所示:
可以看出當(dāng)集群中服務(wù)數(shù)量達(dá)到5千個時,路由延遲成倍增加欠肾。添加 IPTables 規(guī)則的延遲瓶颠,有多種產(chǎn)生的原因,如:
添加規(guī)則不是增量的董济,而是先把當(dāng)前所有規(guī)則都拷貝出來步清,再做修改然后再把修改后的規(guī)則保存回去,這樣一個過程的結(jié)果就是 IPTables 在更新一條規(guī)則時會把 IPTables 鎖住,這樣的后果在服務(wù)數(shù)量達(dá)到一定量級的時候廓啊,性能基本不可接受:在有5千個服務(wù)(4萬條規(guī)則)時欢搜,添加一條規(guī)則耗時11分鐘;在右2萬個服務(wù)(16萬條規(guī)則)時谴轮,添加一條規(guī)則需要5個小時炒瘟。
這樣的延遲時間,對生產(chǎn)環(huán)境是不可以的第步,那該性能問題有哪些解決方案呢疮装?從根本上解決的話,可以使用 “IP Virtual Server”(IPVS )來替換當(dāng)前 kube-proxy 中的 IPTables 實現(xiàn)粘都,這樣能帶來顯著的性能提升以及更智能的負(fù)載均衡功能如支持權(quán)重廓推、支持重試等等。
那什么是 “IP Virtual Server”(IPVS ) 呢翩隧?
ipvs 簡介
k8s 1.8 版本中樊展,社區(qū) SIG Network 增強了 NetworkPolicy API,以支持 Pod 出口流量策略堆生,以及允許策略規(guī)則匹配源或目標(biāo) CIDR 的匹配條件专缠。這兩個增強特性都被設(shè)計為 beta 版本。 SIG Network 還專注于改進 kube-proxy淑仆,除了當(dāng)前的 iptables 和 userspace 模式涝婉,kube-proxy 還引入了一個 alpha 版本的 IPVS 模式。
作為 Linux Virtual Server(LVS) 項目的一部分蔗怠,IPVS 是建立于 Netfilter之上的高效四層負(fù)載均衡器墩弯,支持 TCP 和 UDP 協(xié)議,支持3種負(fù)載均衡模式:NAT蟀淮、直接路由(通過 MAC 重寫實現(xiàn)二層路由)和IP 隧道最住。ipvs(IP Virtual Server)安裝在LVS(Linux Virtual Server)集群作為負(fù)載均衡主節(jié)點上,通過虛擬出一個IP地址和端口對外提供服務(wù)怠惶。客戶端通過訪問虛擬IP+端口訪問該虛擬服務(wù)轧粟,之后訪問請求由負(fù)載均衡器調(diào)度到后端真實服務(wù)器上策治。
ipvs相當(dāng)于工作在netfilter中的input鏈。
配置方法:IPVS 負(fù)載均衡模式在 kube-proxy 處于測試階段還未正式發(fā)布兰吟,完全兼容當(dāng)前 Kubernetes 的行為通惫,通過修改 kube-proxy 啟動參數(shù),在 mode=userspace 和 mode=iptables 的基礎(chǔ)上混蔼,增加 mode=IPVS 即可啟用該功能履腋。
ipvs轉(zhuǎn)發(fā)模式
● DR模式(Direct Routing)
特點:
<1> 數(shù)據(jù)包在LB轉(zhuǎn)發(fā)過程中,源/目的IP和端口都不會變化。LB只修改數(shù)據(jù)包的MAC地址為RS的MAC地址
<2> RS須在環(huán)回網(wǎng)卡上綁定LB的虛擬機服務(wù)IP
<3> RS處理完請求后遵湖,響應(yīng)包直接回給客戶端悔政,不再經(jīng)過LB
缺點:
<1> LB和RS必須位于同一子網(wǎng)
● NAT模式(Network Address Translation)
特點:
<1> LB會修改數(shù)據(jù)包地址:對于請求包,進行DNAT延旧;對于響應(yīng)包谋国,進行SNAT
<2> 需要將RS的默認(rèn)網(wǎng)關(guān)地址配置為LB的虛擬IP地址
缺點:
<1> LB和RS必須位于同一子網(wǎng),且客戶端和LB不能位于同一子網(wǎng)
● FULLNAT模式
特點:
<1> LB會對請求包和響應(yīng)包都做SNAT+DNAT
<2> LB和RS對于組網(wǎng)結(jié)構(gòu)沒有要求
<3> LB和RS必須位于同一子網(wǎng)迁沫,且客戶端和LB不能位于同一子網(wǎng)
● 三種轉(zhuǎn)發(fā)模式性能從高到低:DR > NAT >FULLNAT
ipvs 負(fù)載均衡器常用調(diào)度算法
● 輪詢(Round Robin)
LB認(rèn)為集群內(nèi)每臺RS都是相同的芦瘾,會輪流進行調(diào)度分發(fā)。從數(shù)據(jù)統(tǒng)計上看集畅,RR模式是調(diào)度最均衡的近弟。
● 加權(quán)輪詢(Weighted Round Robin)
LB會根據(jù)RS上配置的權(quán)重,將消息按權(quán)重比分發(fā)到不同的RS上挺智∶晁保可以給性能更好的RS節(jié)點配置更高的權(quán)重,提升集群整體的性能逃贝。
● 最少連接調(diào)度
LB會根據(jù)和集群內(nèi)每臺RS的連接數(shù)統(tǒng)計情況谣辞,將消息調(diào)度到連接數(shù)最少的RS節(jié)點上。在長連接業(yè)務(wù)場景下沐扳,LC算法對于系統(tǒng)整體負(fù)載均衡的情況較好泥从;但是在短連接業(yè)務(wù)場景下,由于連接會迅速釋放沪摄,可能會導(dǎo)致消息每次都調(diào)度到同一個RS節(jié)點躯嫉,造成嚴(yán)重的負(fù)載不均衡。
● 加權(quán)最少連接調(diào)度
最小連接數(shù)算法的加權(quán)版杨拐。
● 原地址哈希祈餐,鎖定請求的用戶
根據(jù)請求的源IP,作為散列鍵(Hash Key)從靜態(tài)分配的散列表中找出對應(yīng)的服務(wù)器哄陶。若該服務(wù)器是可用的且未超載帆阳,將請求發(fā)送到該服務(wù)器。
原文首發(fā)于網(wǎng)易云屋吨。