Service 是為一組具有相同功能的Pod提供一個統(tǒng)一的入口地址遵班,并將請求進(jìn)行負(fù)載均衡地分發(fā)到各個Pod上擒抛。
Service 類型
ClusterIP
ClusterIP類型的Service是Kubernetes集群默認(rèn)的Service, 它只能用于集群內(nèi)部通信。不能用于外部通信帽揪。K8s會為每個Service分配一個虛擬IP,即ClusterIP。這個虛擬IP只能在集群內(nèi)部訪問疾掰。
NodePort
。NodePort類型的Service會在集群內(nèi)部的所有Node節(jié)點(diǎn)打開一個指定的端口徐紧。之后所有的流量直接發(fā)送到這個端口之后静檬,就會轉(zhuǎn)發(fā)的Service去對真實(shí)的服務(wù)進(jìn)行訪問。
LoadBalancer
LoadBalancer類型的Service通常和云廠商的LB結(jié)合一起使用并级,用于將集群內(nèi)部的服務(wù)暴露到外網(wǎng)拂檩,云廠商的LoadBalancer會給用戶分配一個IP,之后通過該IP的流量會轉(zhuǎn)發(fā)到你的Service。
Ingress
Ingress 其實(shí)不是Service的一個類型嘲碧,但是它可以作用于多個Service稻励,作為集群內(nèi)部服務(wù)的入口。Ingress 能做許多不同的事愈涩,比如根據(jù)不同的路由望抽,將請求轉(zhuǎn)發(fā)到不同的Service上等等。
Service 服務(wù)發(fā)現(xiàn)的方式
雖然Service解決了Pod的服務(wù)發(fā)現(xiàn)問題履婉,但不提前知道Service的IP煤篙,怎么發(fā)現(xiàn)service服務(wù)呢?k8s提供了兩種方式進(jìn)行服務(wù)發(fā)現(xiàn):
- 環(huán)境變量: 當(dāng)創(chuàng)建一個Pod的時候毁腿,kubelet會在該P(yáng)od中注入集群內(nèi)所有Service的相關(guān)環(huán)境變量辑奈。需要注意的是苛茂,要想一個Pod中注入某個Service的環(huán)境變量,則必須Service要先比該P(yáng)od創(chuàng)建身害。這一點(diǎn)味悄,幾乎使得這種方式進(jìn)行服務(wù)發(fā)現(xiàn)不可用。
- DNS:可以在集群中部署CoreDNS服務(wù)(舊版本的kubernetes集群使用的是kubeDNS)塌鸯, 來達(dá)到集群內(nèi)部的Pod通過DNS的方式進(jìn)行集群內(nèi)部各個服務(wù)之間的通訊侍瑟。
服務(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是k8s集群控制器的其中一個組件,其功能如下:
- 負(fù)責(zé)生成和維護(hù)所有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負(fù)責(zé)service的實(shí)現(xiàn)乍迄,即實(shí)現(xiàn)了k8s內(nèi)部從pod到service和外部從node port到service的訪問。
kube-proxy作為一個控制器士败,作為k8s和Linux kernel Netfilter交互的一個樞紐闯两。監(jiān)聽kubernetes集群Services和Endpoints對象的變化,并根據(jù)kube-proxy不同的模式(iptables or ipvs), 對內(nèi)核設(shè)置不同的規(guī)則拱烁,來實(shí)現(xiàn)路由轉(zhuǎn)發(fā)生蚁。
如果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掉。
iptable的使用
在Iptables模式下念脯,kube-proxy通過在目標(biāo)node節(jié)點(diǎn)上的Iptables中的NAT表的PREROUTIN和POSTROUTING鏈中創(chuàng)建一系列的自定義鏈(這些自定義鏈主要是”KUBE-SERVICE”鏈狞洋, “KUBE-POSTROUTING”鏈,每個服務(wù)對應(yīng)的”KUBE-SVC-XXXXXX”鏈和”KUBE-SEP-XXXX”鏈)绿店,然后通過這些自定義鏈對流經(jīng)到該Node的數(shù)據(jù)包做DNAT和SNAT操作從而實(shí)現(xiàn)路由吉懊,負(fù)載均衡和地址轉(zhuǎn)化。
NAT(Network Address Translation假勿,網(wǎng)絡(luò)地址轉(zhuǎn)換)是將IP 數(shù)據(jù)包頭中的IP 地址轉(zhuǎn)換為另一個IP 地址的過程借嗽。SNAT: Source Network Address Translation,是修改網(wǎng)絡(luò)包源ip地址的转培。DNAT: Destination Network Address Translation,是修改網(wǎng)絡(luò)包目的ip地址的恶导。
kube-proxy中,客戶端的請求數(shù)據(jù)包在Iptables規(guī)則中具體的匹配過程為:
PREROUTING鏈或者OUTPUT鏈(集群內(nèi)的Pod通過clusterIP訪問Service時經(jīng)過OUTPUT鏈浸须, 而當(dāng)集群外主機(jī)通過NodePort方式訪問Service時惨寿,通過PREROUTING鏈,兩個鏈都會跳轉(zhuǎn)到KUBE-SERVICE鏈)
KUBE-SERVICES鏈(每一個Service所暴露的每一個端口在KUBE-SERVICES鏈中都會對應(yīng)一條相應(yīng)的規(guī)則删窒,當(dāng)Service的數(shù)量達(dá)到一定規(guī)模時裂垦,KUBE-SERVICES鏈中的規(guī)則的數(shù)據(jù)將會非常的大,而Iptables在進(jìn)行查找匹配時是線性查找易稠,這將耗費(fèi)很長時間,時間復(fù)雜度O(n))缸废。
KUBE-SVC-XXXXX鏈 (在KUBE-SVC-XXXXX鏈中(后面那串 hash 值由 Service 的虛 IP 生成)包蓝,會以一定的概率匹配下面的某一條規(guī)則執(zhí)行驶社,通過statistic模塊為每個后端設(shè)置權(quán)重,已實(shí)現(xiàn)負(fù)載均衡的目的测萎,每個KUBE-SEP-XXXXX鏈代表Service后面的一個具體的Pod(后面那串 hash 值由后端 Pod 實(shí)際 IP 生成),這樣便實(shí)現(xiàn)了負(fù)載均衡的目的)
KUBE-SEP-XXXX鏈 (通過DNAT亡电,將數(shù)據(jù)包的目的IP修改為服務(wù)端的Pod IP)
通過上面的這個設(shè)置便實(shí)現(xiàn)了基于Iptables實(shí)現(xiàn)了負(fù)載均衡。但是Iptbles做負(fù)載均衡存在一些問題:
- 規(guī)則線性匹配時延: KUBE-SERVICES鏈掛了一長串KUBE-SVC-*鏈,訪問每個service,要遍歷每條鏈直到匹配硅瞧,時間復(fù)雜度O(N)
- 規(guī)則更新時延: 非增量式,需要先iptables-save拷貝Iptables狀態(tài)份乒,然后再更新部分規(guī)則,最后再通過 iptables-restore寫入到內(nèi)核腕唧。當(dāng)規(guī)則數(shù)到達(dá)一定程度時或辖,這個過程就會變得非常緩慢。
- 可擴(kuò)展性: 當(dāng)系統(tǒng)存在大量的Iptables規(guī)則鏈時枣接,增加/刪除規(guī)則會出現(xiàn)kernel lock,這時只能等待颂暇。
- 可用性: 服務(wù)擴(kuò)容/縮容時, Iptables規(guī)則的刷新會導(dǎo)致連接斷開但惶,服務(wù)不可用耳鸯。
更多iptable的實(shí)現(xiàn)湿蛔,請參考:https://xigang.github.io/2019/07/21/kubernetes-service/
為了解決Iptables當(dāng)前存在的這些問題,華為開源團(tuán)隊(duì)的同學(xué)為社區(qū)貢獻(xiàn)了IPVS模式县爬。請參考:https://xigang.github.io/2019/07/21/kubernetes-service/