更新時(shí)間:2020-04-04 來(lái)源:黑馬程序員 瀏覽量:
容器網(wǎng)絡(luò)發(fā)端于 Docker 的網(wǎng)絡(luò)。Docker 使用了一個(gè)比較簡(jiǎn)單的網(wǎng)絡(luò)模型,即內(nèi)部的網(wǎng)橋加內(nèi)部的保留 IP。這種設(shè)計(jì)的好處在于容器的網(wǎng)絡(luò)和外部世界是解耦的,無(wú)需占用宿主機(jī)的 IP 或者宿主機(jī)的資源,完全是虛擬的。
它的設(shè)計(jì)初衷是:當(dāng)需要訪問(wèn)外部世界時(shí),會(huì)采用 SNAT 這種方法來(lái)借用 Node 的 IP 去訪問(wèn)外面的服務(wù)。比如容器需要對(duì)外提供服務(wù)的時(shí)候,所用的是 DNAT 技術(shù),也就是在 Node 上開(kāi)一個(gè)端口,然后通過(guò) iptable 或者別的某些機(jī)制,把流導(dǎo)入到容器的進(jìn)程上以達(dá)到目的。
該模型的問(wèn)題在于,外部網(wǎng)絡(luò)無(wú)法區(qū)分哪些是容器的網(wǎng)絡(luò)與流量、哪些是宿主機(jī)的網(wǎng)絡(luò)與流量。比如,如果要做一個(gè)高可用的時(shí)候,172.16.1.1 和 172.16.1.2 是擁有同樣功能的兩個(gè)容器,此時(shí)我們需要將兩者綁成一個(gè) Group 對(duì)外提供服務(wù),而這個(gè)時(shí)候我們發(fā)現(xiàn)從外部看來(lái)兩者沒(méi)有相同之處,它們的 IP 都是借用宿主機(jī)的端口,因此很難將兩者歸攏到一起。推薦了解傳智播客linux云計(jì)算+運(yùn)維開(kāi)發(fā)課程。
在此基礎(chǔ)上,Kubernetes 提出了這樣一種機(jī)制:即每一個(gè) Pod,也就是一個(gè)功能聚集小團(tuán)伙應(yīng)有自己的“身份證”,或者說(shuō) ID。在 TCP 協(xié)議棧上,這個(gè) ID 就是 IP。
這個(gè) IP 是真正屬于該 Pod 的,外部世界不管通過(guò)什么方法一定要給它。對(duì)這個(gè) Pod IP 的訪問(wèn)就是真正對(duì)它的服務(wù)的訪問(wèn),中間拒絕任何的變?cè)?。比如?10.1.1.1 的 IP 去訪問(wèn) 10.1.2.1 的 Pod,結(jié)果到了 10.1.2.1 上發(fā)現(xiàn),它實(shí)際上借用的是宿主機(jī)的 IP,而不是源 IP,這樣是不被允許的。Pod 內(nèi)部會(huì)要求共享這個(gè) IP,從而解決了一些功能內(nèi)聚的容器如何變成一個(gè)部署的原子的問(wèn)題。
剩下的問(wèn)題是我們的部署手段。Kubernetes 對(duì)怎么實(shí)現(xiàn)這個(gè)模型其實(shí)是沒(méi)有什么限制的,用 underlay 網(wǎng)絡(luò)來(lái)控制外部路由器進(jìn)行導(dǎo)流是可以的;如果希望解耦,用 overlay 網(wǎng)絡(luò)在底層網(wǎng)絡(luò)之上再加一層疊加網(wǎng),這樣也是可以的??傊灰_(dá)到模型所要求的目的即可。
Pod 究竟如何上網(wǎng)
容器網(wǎng)絡(luò)的網(wǎng)絡(luò)包究竟是怎么傳送的?
我們可以從以下兩個(gè)維度來(lái)看:
·協(xié)議層次
·網(wǎng)絡(luò)拓?fù)?br/>
1. 協(xié)議層次
它和 TCP 協(xié)議棧的概念是相同的,需要從兩層、三層、四層一層層地摞上去,發(fā)包的時(shí)候從右往左,即先有應(yīng)用數(shù)據(jù),然后發(fā)到了 TCP 或者 UDP 的四層協(xié)議,繼續(xù)向下傳送,加上 IP 頭,再加上 MAC 頭就可以送出去了。收包的時(shí)候則按照相反的順序,首先剝離 MAC 的頭,再剝離 IP 的頭,最后通過(guò)協(xié)議號(hào)在端口找到需要接收的進(jìn)程。
2. 網(wǎng)絡(luò)拓?fù)?/span>
一個(gè)容器的包所要解決的問(wèn)題分為兩步:第一步,如何從容器的空間 (c1) 跳到宿主機(jī)的空間 (infra);第二步,如何從宿主機(jī)空間到達(dá)遠(yuǎn)端。
我個(gè)人的理解是,容器網(wǎng)絡(luò)的方案可以通過(guò)接入、流控、通道這三個(gè)層面來(lái)考慮。
·第一個(gè)是接入,就是說(shuō)我們的容器和宿主機(jī)之間是使用哪一種機(jī)制做連接,比如 Veth + bridge、Veth + pair 這樣的經(jīng)典方式,也有利用高版本內(nèi)核的新機(jī)制等其他方式(如 mac/IPvlan 等),來(lái)把包送入到宿主機(jī)空間;
·第二個(gè)是流控,就是說(shuō)我的這個(gè)方案要不要支持 Network Policy,如果支持的話又要用何種方式去實(shí)現(xiàn)。這里需要注意的是,我們的實(shí)現(xiàn)方式一定需要在數(shù)據(jù)路徑必經(jīng)的一個(gè)關(guān)節(jié)點(diǎn)上。如果數(shù)據(jù)路徑不通過(guò)該 Hook 點(diǎn),那就不會(huì)起作用;
·第三個(gè)是通道,即兩個(gè)主機(jī)之間通過(guò)什么方式完成包的傳輸。我們有很多種方式,比如以路由的方式,具體又可分為 BGP 路由或者直接路由。還有各種各樣的隧道技術(shù)等等。最終我們實(shí)現(xiàn)的目的就是一個(gè)容器內(nèi)的包通過(guò)容器,經(jīng)過(guò)接入層傳到宿主機(jī),再穿越宿主機(jī)的流控模塊(如果有)到達(dá)通道送到對(duì)端。
3. 一個(gè)最簡(jiǎn)單的路由方案:Flannel-host-gw
這個(gè)方案采用的是每個(gè) Node 獨(dú)占網(wǎng)段,每個(gè) Subnet 會(huì)綁定在一個(gè) Node 上,網(wǎng)關(guān)也設(shè)置在本地,或者說(shuō)直接設(shè)在 cni0 這個(gè)網(wǎng)橋的內(nèi)部端口上。該方案的好處是管理簡(jiǎn)單,壞處就是無(wú)法跨 Node 遷移 Pod。就是說(shuō)這個(gè) IP、網(wǎng)段已經(jīng)是屬于這個(gè) Node 之后就無(wú)法遷移到別的 Node 上。
這個(gè)方案的精髓在于 route 表的設(shè)置,如上圖所示。接下來(lái)為大家一一解讀一下。
·第一條很簡(jiǎn)單,我們?cè)谠O(shè)置網(wǎng)卡的時(shí)候都會(huì)加上這一行。就是指定我的默認(rèn)路由是通過(guò)哪個(gè) IP 走掉,默認(rèn)設(shè)備又是什么;
·第二條是對(duì) Subnet 的一個(gè)規(guī)則反饋。就是說(shuō)我的這個(gè)網(wǎng)段是 10.244.0.0,掩碼是 24 位,它的網(wǎng)關(guān)地址就在網(wǎng)橋上,也就是 10.244.0.1。這就是說(shuō)這個(gè)網(wǎng)段的每一個(gè)包都發(fā)到這個(gè)網(wǎng)橋的 IP 上;
·第三條是對(duì)對(duì)端的一個(gè)反饋。如果你的網(wǎng)段是 10.244.1.0(上圖右邊的 Subnet),我們就把它的 Host 的網(wǎng)卡上的 IP (10.168.0.3) 作為網(wǎng)關(guān)。也就是說(shuō),如果數(shù)據(jù)包是往 10.244.1.0 這個(gè)網(wǎng)段發(fā)的,就請(qǐng)以 10.168.0.3 作為網(wǎng)關(guān)。
再來(lái)看一下這個(gè)數(shù)據(jù)包到底是如何跑起來(lái)的?
假設(shè)容器 (10.244.0.2) 想要發(fā)一個(gè)包給 10.244.1.3,那么它在本地產(chǎn)生了 TCP 或者 UDP 包之后,再依次填好對(duì)端 IP 地址、本地以太網(wǎng)的 MAC 地址作為源 MAC 以及對(duì)端 MAC。一般來(lái)說(shuō)本地會(huì)設(shè)定一條默認(rèn)路由,默認(rèn)路由會(huì)把 cni0 上的 IP 作為它的默認(rèn)網(wǎng)關(guān),對(duì)端的 MAC 就是這個(gè)網(wǎng)關(guān)的 MAC 地址。然后這個(gè)包就可以發(fā)到橋上去了。如果網(wǎng)段在本橋上,那么通過(guò) MAC 層的交換即可解決。
這個(gè)例子中我們的 IP 并不屬于本網(wǎng)段,因此網(wǎng)橋會(huì)將其上送到主機(jī)的協(xié)議棧去處理。主機(jī)協(xié)議棧恰好找到了對(duì)端的 MAC 地址。使用 10.168.0.3 作為它的網(wǎng)關(guān),通過(guò)本地 ARP 探查后,我們得到了 10.168.0.3 的 MAC 地址。即通過(guò)協(xié)議棧層層組裝,我們達(dá)到了目的,將 Dst-MAC 填為右圖主機(jī)網(wǎng)卡的 MAC 地址,從而將包從主機(jī)的 eth0 發(fā)到對(duì)端的 eth0 上去。
所以大家可以發(fā)現(xiàn),這里有一個(gè)隱含的限制,上圖中的 MAC 地址填好之后一定是能到達(dá)對(duì)端的,但如果這兩個(gè)宿主機(jī)之間不是二層連接的,中間經(jīng)過(guò)了一些網(wǎng)關(guān)、一些復(fù)雜的路由,那么這個(gè) MAC 就不能直達(dá),這種方案就是不能用的。當(dāng)包到達(dá)了對(duì)端的 MAC 地址之后,發(fā)現(xiàn)這個(gè)包確實(shí)是給它的,但是 IP 又不是它自己的,就開(kāi)始 Forward 流程,包上送到協(xié)議棧,之后再走一遍路由,剛好會(huì)發(fā)現(xiàn) 10.244.1.0/24 需要發(fā)到 10.244.1.1 這個(gè)網(wǎng)關(guān)上,從而到達(dá)了 cni0 網(wǎng)橋,它會(huì)找到 10.244.1.3 對(duì)應(yīng)的 MAC 地址,再通過(guò)橋接機(jī)制,這個(gè)包就到達(dá)了對(duì)端容器。
大家可以看到,整個(gè)過(guò)程總是二層、三層,發(fā)的時(shí)候又變成二層,再做路由,就是一個(gè)大環(huán)套小環(huán)。這是一個(gè)比較簡(jiǎn)單的方案,如果中間要走隧道,則可能會(huì)有一條 vxlan tunnel 的設(shè)備,此時(shí)就不填直接的路由,而填成對(duì)端的隧道號(hào)。
Service 究竟如何工作
Service 其實(shí)是一種負(fù)載均衡 (Load Balance) 的機(jī)制。
我們認(rèn)為它是一種用戶側(cè)(Client Side) 的負(fù)載均衡,也就是說(shuō) VIP 到 RIP 的轉(zhuǎn)換在用戶側(cè)就已經(jīng)完成了,并不需要集中式地到達(dá)某一個(gè) NGINX 或者是一個(gè) ELB 這樣的組件來(lái)進(jìn)行決策。
它的實(shí)現(xiàn)是這樣的:首先是由一群 Pod 組成一組功能后端,再在前端上定義一個(gè)虛 IP 作為訪問(wèn)入口。一般來(lái)說(shuō),由于 IP 不太好記,我們還會(huì)附贈(zèng)一個(gè) DNS 的域名,Client 先訪問(wèn)域名得到虛 IP 之后再轉(zhuǎn)成實(shí) IP。Kube-proxy 則是整個(gè)機(jī)制的實(shí)現(xiàn)核心,它隱藏了大量的復(fù)雜性。它的工作機(jī)制是通過(guò) apiserver 監(jiān)控 Pod/Service 的變化(比如是不是新增了 Service、Pod)并將其反饋到本地的規(guī)則或者是用戶態(tài)進(jìn)程。
一個(gè) LVS 版的 Service
我們來(lái)實(shí)際做一個(gè) LVS 版的 Service。LVS 是一個(gè)專(zhuān)門(mén)用于負(fù)載均衡的內(nèi)核機(jī)制。它工作在第四層,性能會(huì)比用 iptable 實(shí)現(xiàn)好一些。
假設(shè)我們是一個(gè) Kube-proxy,拿到了一個(gè) Service 的配置,如下圖所示:它有一個(gè) Cluster IP,在該 IP 上的端口是 9376,需要反饋到容器上的是 80 端口,還有三個(gè)可工作的 Pod,它們的 IP 分別是 10.1.2.3, 10.1.14.5, 10.1.3.8。
它要做的事情就是:
第 1 步,綁定 VIP 到本地(欺騙內(nèi)核);
首先需要讓內(nèi)核相信它擁有這樣的一個(gè)虛 IP,這是 LVS 的工作機(jī)制所決定的,因?yàn)樗ぷ髟诘谒膶樱⒉魂P(guān)心 IP 轉(zhuǎn)發(fā),只有它認(rèn)為這個(gè) IP 是自己的才會(huì)拆到 TCP 或 UDP 這一層。在第一步中,我們將該 IP 設(shè)到內(nèi)核中,告訴內(nèi)核它確實(shí)有這么一個(gè) IP。實(shí)現(xiàn)的方法有很多,我們這里用的是 ip route 直接加 local 的方式,用 Dummy 啞設(shè)備上加 IP 的方式也是可以的。
第 2 步,為這個(gè)虛 IP 創(chuàng)建一個(gè) IPVS 的 virtual server;
告訴它我需要為這個(gè) IP 進(jìn)行負(fù)載均衡分發(fā),后面的參數(shù)就是一些分發(fā)策略等等。virtual server 的 IP 其實(shí)就是我們的 Cluster IP。
第 3 步,為這個(gè) IPVS service 創(chuàng)建相應(yīng)的 real server。
我們需要為 virtual server 配置相應(yīng)的 real server,就是真正提供服務(wù)的后端是什么。比如說(shuō)我們剛才看到有三個(gè) Pod,于是就把這三個(gè)的 IP 配到 virtual server 上,完全一一對(duì)應(yīng)過(guò)來(lái)就可以了。Kube-proxy 工作跟這個(gè)也是類(lèi)似的。只是它還需要去監(jiān)控一些 Pod 的變化,比如 Pod 的數(shù)量變成 5 個(gè)了,那么規(guī)則就應(yīng)變成 5 條。如果這里面某一個(gè) Pod 死掉了或者被殺死了,那么就要相應(yīng)地減掉一條。又或者整個(gè) Service 被撤銷(xiāo)了,那么這些規(guī)則就要全部刪掉。所以它其實(shí)做的是一些管理層面的工作。
啥?負(fù)載均衡還分內(nèi)部外部
最后我們介紹一下 Service 的類(lèi)型,可以分為以下 4 類(lèi)。
1. ClusterIP
集群內(nèi)部的一個(gè)虛擬 IP,這個(gè) IP 會(huì)綁定到一堆服務(wù)的 Group Pod 上面,這也是默認(rèn)的服務(wù)方式。它的缺點(diǎn)是這種方式只能在 Node 內(nèi)部也就是集群內(nèi)部使用。
2. NodePort
供集群外部調(diào)用。將 Service 承載在 Node 的靜態(tài)端口上,端口號(hào)和 Service 一一對(duì)應(yīng),那么集群外的用戶就可以通過(guò) : 的方式調(diào)用到 Service。
3. LoadBalancer
給云廠商的擴(kuò)展接口。像阿里云、亞馬遜這樣的云廠商都是有成熟的 LB 機(jī)制的,這些機(jī)制可能是由一個(gè)很大的集群實(shí)現(xiàn)的,為了不浪費(fèi)這種能力,云廠商可通過(guò)這個(gè)接口進(jìn)行擴(kuò)展。它首先會(huì)自動(dòng)創(chuàng)建 NodePort 和 ClusterIP 這兩種機(jī)制,云廠商可以選擇直接將 LB 掛到這兩種機(jī)制上,或者兩種都不用,直接把 Pod 的 RIP 掛到云廠商的 ELB 的后端也是可以的。
4. ExternalName
擯棄內(nèi)部機(jī)制,依賴(lài)外部設(shè)施,比如某個(gè)用戶特別強(qiáng),他覺(jué)得我們提供的都沒(méi)什么用,就是要自己實(shí)現(xiàn),此時(shí)一個(gè) Service 會(huì)和一個(gè)域名一一對(duì)應(yīng)起來(lái),整個(gè)負(fù)載均衡的工作都是外部實(shí)現(xiàn)的。
下圖是一個(gè)實(shí)例。它靈活地應(yīng)用了 ClusterIP、NodePort 等多種服務(wù)方式,又結(jié)合了云廠商的 ELB,變成了一個(gè)很靈活、極度伸縮、生產(chǎn)上真正可用的一套系統(tǒng)。
首先我們用 ClusterIP 來(lái)做功能 Pod 的服務(wù)入口。大家可以看到,如果有三種 Pod 的話,就有三個(gè) Service Cluster IP 作為它們的服務(wù)入口。這些方式都是 Client 端的,如何在 Server 端做一些控制呢?
首先會(huì)起一些 Ingress 的 Pod(Ingress 是 K8s 后來(lái)新增的一種服務(wù),本質(zhì)上還是一堆同質(zhì)的 Pod),然后將這些 Pod 組織起來(lái),暴露到一個(gè) NodePort 的 IP,K8s 的工作到此就結(jié)束了。
任何一個(gè)用戶訪問(wèn) 23456 端口的 Pod 就會(huì)訪問(wèn)到 Ingress 的服務(wù),它的后面有一個(gè) Controller,會(huì)把 Service IP 和 Ingress 的后端進(jìn)行管理,最后會(huì)調(diào)到 ClusterIP,再調(diào)到我們的功能 Pod。前面提到我們?nèi)?duì)接云廠商的 ELB,我們可以讓 ELB 去監(jiān)聽(tīng)所有集群節(jié)點(diǎn)上的 23456 端口,只要在 23456 端口上有服務(wù)的,就認(rèn)為有一個(gè) Ingress 的實(shí)例在跑。
整個(gè)的流量經(jīng)過(guò)外部域名的一個(gè)解析跟分流到達(dá)了云廠商的 ELB,ELB 經(jīng)過(guò)負(fù)載均衡并通過(guò) NodePort 的方式到達(dá) Ingress,Ingress 再通過(guò) ClusterIP 調(diào)用到后臺(tái)真正的 Pod。這種系統(tǒng)看起來(lái)比較豐富,健壯性也比較好。任何一個(gè)環(huán)節(jié)都不存在單點(diǎn)的問(wèn)題,任何一個(gè)環(huán)節(jié)也都有管理與反饋。
本文總結(jié)
本節(jié)課的主要內(nèi)容就到此為止了,這里為大家簡(jiǎn)單總結(jié)一下:
·大家要從根本上理解 Kubernetes 網(wǎng)絡(luò)模型的演化來(lái)歷,理解 PerPodPerIP 的用心在哪里;
·網(wǎng)絡(luò)的事情萬(wàn)變不離其宗,按照模型從 4 層向下就是發(fā)包過(guò)程,反正層層剝離就是收包過(guò)程,容器網(wǎng)絡(luò)也是如此;
·Ingress 等機(jī)制是在更高的層次上(服務(wù)<->端口)方便大家部署集群對(duì)外服務(wù),通過(guò)一個(gè)真正可用的部署實(shí)例,希望大家把 Ingress+Cluster IP + PodIP 等概念聯(lián)合來(lái)看,理解社區(qū)出臺(tái)新機(jī)制、新資源對(duì)象的思考。
猜你喜歡: