一禽绪、前言
k8s對Pods之間如何進(jìn)行組網(wǎng)通信提出了要求合瓢,k8s對集群的網(wǎng)絡(luò)有以下要求:
所有的Pods之間可以在不使用NAT網(wǎng)絡(luò)地址轉(zhuǎn)換的情況下相互通信
所有的Nodes之間可以在不使用NAT網(wǎng)絡(luò)地址轉(zhuǎn)換的情況下相互通信
每個Pod自己看到的自己的ip和其他Pod看到的一致
k8s網(wǎng)絡(luò)模型設(shè)計基礎(chǔ)原則:每個Pod都擁有一個獨(dú)立的 IP地址退个,而且 假定所有 Pod 都在一個可以直接連通的赠法、扁平的網(wǎng)絡(luò)空間中 勋功。 所以不管它們是否運(yùn)行在同 一 個 Node (宿主機(jī))中递鹉,都要求它們可以直接通過對方的 IP 進(jìn)行訪問弓候。設(shè)計這個原則的原因 是郎哭,用戶不需要額外考慮如何建立 Pod 之間的連接他匪,也不需要考慮將容器端口映射到主機(jī)端口等問題。
由于 Kubemetes 的網(wǎng)絡(luò)模型假設(shè) Pod 之間訪問時使用的是對方 Pod 的實(shí)際地址夸研,所以一個
Pod 內(nèi)部的應(yīng)用程序看到的自己的 IP 地址和端口與集群內(nèi)其他 Pod 看到的一樣邦蜜。它們都是 Pod 實(shí)際分配的IP地址。將IP地址和端口在Pod內(nèi)部和外部都保持一致亥至, 我們可以不使用 NAT 來進(jìn)行轉(zhuǎn)換悼沈,地址空間也自然是平的。
鑒于上面這些要求姐扮,我們需要解決四個不同的網(wǎng)絡(luò)問題:
Docker容器和Docker容器之間的網(wǎng)絡(luò)
Pod與Pod之間的網(wǎng)絡(luò)
Pod與Service之間的網(wǎng)絡(luò)
Internet與Service之間的網(wǎng)絡(luò)
下面我們一一進(jìn)行討論每種網(wǎng)絡(luò)問題絮供,以及如何解決。
在k8s中每個Pod中管理著一組Docker容器壤靶,這些Docker容器共享同一個網(wǎng)絡(luò)命名空間。
Pod中的每個Docker容器擁有與Pod相同的IP和port地址空間惊搏,并且由于他們在同一個網(wǎng)絡(luò)命名空間贮乳,他們之間可以通過localhost相互訪問。
什么機(jī)制讓同一個Pod內(nèi)的多個docker容器能夠相互通信那恬惯?其實(shí)是使用Docker的一種網(wǎng)絡(luò)模型:–net=container
container模式指定新創(chuàng)建的Docker容器和已經(jīng)存在的一個容器共享一個網(wǎng)絡(luò)命名空間向拆,而不是和宿主機(jī)共享。新創(chuàng)建的Docker容器不會創(chuàng)建自己的網(wǎng)卡酪耳,配置自己的 IP浓恳,而是和一個指定的容器共享 IP、端口范圍等
docker run -d --net=container:6c5da4316866(已有容器id) e66c955efe8d(鏡像id)
每個Pod容器有一個pause容器,其有獨(dú)立的網(wǎng)絡(luò)命名空間碗暗,在Pod內(nèi)啟動Docker容器時候使用?–net=container就可以讓當(dāng)前Docker容器加入到Pod容器擁有的網(wǎng)絡(luò)命名空間(pause容器的)
?pause跟容器
k8s中,每個Pod擁有一個ip地址讹堤,不同的Pod之間可以直接使用該ip與彼此進(jìn)行通訊
在同一個Node上吆鹤,從Pod的視角看,它存在于自己的網(wǎng)絡(luò)命名空間中洲守,并且需要與該Node上的其他網(wǎng)絡(luò)命名空間上的Pod進(jìn)行通信疑务。
那么是如何做到的?這多虧了使用linux虛擬以太網(wǎng)設(shè)備或者說是由兩個虛擬接口組成的veth(virtual-ethernet)對使不同的網(wǎng)絡(luò)命名空間鏈接起來梗醇,這些虛擬接口分布在多個網(wǎng)絡(luò)命名空間上(這里是指多個Pod上)知允。
為了讓多個Pod的網(wǎng)絡(luò)命名空間鏈接起來,我們可以讓veth對的一端鏈接到root網(wǎng)絡(luò)命名空間(宿主機(jī)的)叙谨,另一端鏈接到Pod的網(wǎng)絡(luò)命名空間温鸽。
每對Veth就像一根接插電纜,連接兩側(cè)并允許流量在它們之間流動;這種veth對可以推廣到同一個Node上任意多的Pod上涤垫,如上圖這里展示使用veth對鏈接每個Pod到虛擬機(jī)的root網(wǎng)絡(luò)命名空間姑尺。
下面我們看如何使用網(wǎng)橋設(shè)備來讓通過veth對鏈接到root命名空間的多個Pod進(jìn)行通信。
linux以太網(wǎng)橋(Linux Ethernet bridge)是一個虛擬的2層網(wǎng)絡(luò)設(shè)備蝠猬,目的是把多個以太網(wǎng)段鏈接起來切蟋,網(wǎng)橋維護(hù)了一個轉(zhuǎn)發(fā)表,通過檢查轉(zhuǎn)發(fā)表通過它傳輸?shù)臄?shù)據(jù)包的目的地并決定是否將數(shù)據(jù)包傳遞到連接到網(wǎng)橋的其他網(wǎng)段榆芦,網(wǎng)橋代碼通過查看網(wǎng)絡(luò)中每個以太網(wǎng)設(shè)備特有的MAC地址來決定是傳輸數(shù)據(jù)還是丟棄數(shù)據(jù)柄粹。
網(wǎng)橋?qū)崿F(xiàn)了ARP協(xié)議用來根據(jù)給定的ip地址找到對應(yīng)機(jī)器的數(shù)據(jù)鏈路層的mac地址,一開始轉(zhuǎn)發(fā)表為空匆绣,當(dāng)一個數(shù)據(jù)幀被網(wǎng)橋接受后驻右,網(wǎng)橋會廣播該幀到所有的鏈接設(shè)備(除了發(fā)送方設(shè)備),并且把響應(yīng)這個廣播的設(shè)備記錄到轉(zhuǎn)發(fā)表崎淳;隨后發(fā)往相同ip地址的流量會直接從轉(zhuǎn)發(fā)表查找正確的mac地址旺入,然后轉(zhuǎn)發(fā)包到對應(yīng)的設(shè)備。
如上圖顯示了兩個Pod通過veth對鏈接到root網(wǎng)絡(luò)命名空間凯力,并且通過網(wǎng)橋進(jìn)行通信
鑒于每個Pod有自己獨(dú)立的網(wǎng)絡(luò)命名空間,我們使用虛擬以太網(wǎng)設(shè)備把多個Pod的命名空間鏈接到了root命名空間礼华,并且使用網(wǎng)橋讓多個Pod之間進(jìn)行通信咐鹤,下面我們看如何在兩個pod之間進(jìn)行通信:
通過網(wǎng)橋這里把veth0和veth1組成為一個以太網(wǎng),他們直接是可以直接通信的圣絮,另外這里通過veth對讓pod1的eth0和veth0祈惶、pod2的eth0和veth1關(guān)聯(lián)起來,從而讓pod1和pod2相互通信扮匠。
Pod 1通過自己默認(rèn)的以太網(wǎng)設(shè)備eth0發(fā)送一個數(shù)據(jù)包捧请,eth0把數(shù)據(jù)傳遞給veth0,數(shù)據(jù)包到達(dá)網(wǎng)橋后,網(wǎng)橋通過轉(zhuǎn)發(fā)表把數(shù)據(jù)傳遞給veth1棒搜,然后虛擬設(shè)備veth1直接把包傳遞給Pod2網(wǎng)絡(luò)命名空間中的虛擬設(shè)備eth0.
k8s網(wǎng)絡(luò)模型需要每個pod必須通過ip地址可以進(jìn)行訪問疹蛉,每個pod的ip地址總是對網(wǎng)絡(luò)中的其他pod可見,并且每個pod看待自己的ip與別的pod看待的是一樣的(雖然他沒規(guī)定如何實(shí)現(xiàn))力麸,下面我們看不同Node間Pod如何交互
k8s中每個集群中的每個Node都會被分配了一個CIDR塊(無類別域間路由選擇可款,把網(wǎng)絡(luò)前綴都相同的連續(xù)地址組成的地址組稱為CIDR地址塊)用來給該Node上的Pod分配IP地址。(保證pod的ip不會沖突)
另外還需要把pod的ip與所在的nodeip關(guān)聯(lián)起來()
如上圖Node1(vm1)上的Pod1與Node2(vm2)上Pod4之間進(jìn)行交互克蚂。
首先pod1通過自己的以太網(wǎng)設(shè)備eth0把數(shù)據(jù)包發(fā)送到關(guān)聯(lián)到root命名空間的veth0上闺鲸,然后數(shù)據(jù)包被Node1上的網(wǎng)橋設(shè)備cbr0接受到,網(wǎng)橋查找轉(zhuǎn)發(fā)表發(fā)現(xiàn)找不到pod4的Mac地址埃叭,則會把包轉(zhuǎn)發(fā)到默認(rèn)路由(root命名空間的eth0設(shè)備)摸恍,然后數(shù)據(jù)包經(jīng)過eth0就離開了Node1,被發(fā)送到網(wǎng)絡(luò)赤屋。
數(shù)據(jù)包到達(dá)Node2后立镶,首先會被root命名空間的eth0設(shè)備壁袄,然后通過網(wǎng)橋cbr0把數(shù)據(jù)路由到虛擬設(shè)備veth1,最終數(shù)據(jù)表會被流轉(zhuǎn)到與veth1配對的另外一端(pod4的eth0)
每個Node都知道如何把數(shù)據(jù)包轉(zhuǎn)發(fā)到其內(nèi)部運(yùn)行的Pod,當(dāng)一個數(shù)據(jù)包到達(dá)Node后谜慌,其內(nèi)部數(shù)據(jù)流就和Node內(nèi)Pod之間的流轉(zhuǎn)類似了然想。
對于如何來配置網(wǎng)絡(luò),k8s在網(wǎng)絡(luò)這塊自身并沒有實(shí)現(xiàn)網(wǎng)絡(luò)規(guī)劃的具體邏輯欣范,而是制定了一套CNI(Container Network Interface)接口規(guī)范变泄,開放給社區(qū)來實(shí)現(xiàn)。
例如AWS恼琼,亞馬遜為k8s維護(hù)了一個容器網(wǎng)絡(luò)插件,使用CNI插件來讓亞馬遜VPC(虛擬私有云)環(huán)境中的Node與Node直接進(jìn)行交互.
CoreOS的Flannel是k8s中實(shí)現(xiàn)CNI規(guī)范較為出名的一種實(shí)現(xiàn)妨蛹。
UDP幀格式
vlan?
3,1晴竞,12 vid
vxlan協(xié)議格式 VXLAN 全稱是 Virtual eXtensible Local Area Network蛙卤,虛擬可擴(kuò)展的局域網(wǎng)。它是一種 overlay 技術(shù)(覆蓋網(wǎng)絡(luò)通過將一個分組封裝在另一個分組內(nèi)來將網(wǎng)絡(luò)服務(wù)與底層基礎(chǔ)設(shè)施分離噩死。在將封裝的數(shù)據(jù)包轉(zhuǎn)發(fā)到端點(diǎn)后颤难,將其解封裝。)已维,通過三層的網(wǎng)絡(luò)來搭建虛擬的二層網(wǎng)絡(luò)行嗤,其幀格式:
從這個報文中可以看到三個部分:
1.最外層的 UDP 協(xié)議報文用來在底層網(wǎng)絡(luò)上傳輸,也就是 vtep 之間互相通信的基礎(chǔ)
2.中間是 VXLAN 頭部垛耳,vtep 接受到報文之后栅屏,去除前面的 UDP 協(xié)議部分,根據(jù)這部分來處理 vxlan 的邏輯堂鲜,主要是根據(jù) VNI 發(fā)送到最終的虛擬機(jī)
3.最里面是原始的報文栈雳,也就是虛擬機(jī)看到的報文內(nèi)容
VTEP(VXLAN Tunnel Endpoints):vxlan 網(wǎng)絡(luò)的邊緣設(shè)備,用來進(jìn)行 vxlan 報文的處理(封包和解包)缔莲。vtep 可以是網(wǎng)絡(luò)設(shè)備(比如交換機(jī))哥纫,也可以是一臺機(jī)器(比如虛擬化集群中的宿主機(jī))
VNI(VXLAN Network Identifier):VNI 是每個 vxlan 的標(biāo)識,是個 24 位整數(shù)痴奏,一共有 2^24 = 16,777,216(一千多萬)磺箕,一般每個 VNI 對應(yīng)一個租戶,也就是說使用 vxlan 搭建的公有云可以理論上可以支撐千萬級別的租戶
Tunnel:隧道是一個邏輯上的概念抛虫,在 vxlan 模型中并沒有具體的物理實(shí)體想對應(yīng)松靡。隧道可以看做是一種虛擬通道,vxlan 通信雙方(圖中的虛擬機(jī))認(rèn)為自己是在直接通信建椰,并不知道底層網(wǎng)絡(luò)的存在雕欺。從整體來說,每個 vxlan 網(wǎng)絡(luò)像是為通信的虛擬機(jī)搭建了一個單獨(dú)的通信通道,也就是隧道
Flannel是CoreOS團(tuán)隊針對Kubernetes設(shè)計的一個網(wǎng)絡(luò)規(guī)劃實(shí)現(xiàn)屠列,簡單來說啦逆,它的功能有以下幾點(diǎn):
使集群中的不同Node主機(jī)創(chuàng)建的Docker容器都具有全集群唯一的虛擬IP地址。
建立一個覆蓋網(wǎng)絡(luò)(overlay network)笛洛,通過這個覆蓋網(wǎng)絡(luò)夏志,將數(shù)據(jù)包原封不動的傳遞到目標(biāo)容器。覆蓋網(wǎng)絡(luò)是建立在另一個網(wǎng)絡(luò)之上并由其基礎(chǔ)設(shè)施支持的虛擬網(wǎng)絡(luò)苛让。覆蓋網(wǎng)絡(luò)通過將一個分組封裝在另一個分組內(nèi)來將網(wǎng)絡(luò)服務(wù)與底層基礎(chǔ)設(shè)施分離沟蔑。在將封裝的數(shù)據(jù)包轉(zhuǎn)發(fā)到端點(diǎn)后,將其解封裝狱杰。
創(chuàng)建一個新的虛擬網(wǎng)卡flannel0接收docker網(wǎng)橋的數(shù)據(jù)瘦材,通過維護(hù)路由表,對接收到的數(shù)據(jù)進(jìn)行封包和轉(zhuǎn)發(fā)(vxlan)仿畸。
路由信息一般存放到etcd:多個node上的Flanneld依賴一個etcd cluster來做集中配置服務(wù)食棕,etcd保證了所有node上flanned所看到的配置是一致的。同時每個node上的flanned監(jiān)聽etcd上的數(shù)據(jù)變化错沽,實(shí)時感知集群中node的變化
Flannel首先會在Node上創(chuàng)建一個名為flannel0的網(wǎng)橋(vxlan類型的設(shè)備)簿晓,并且在每個Node上運(yùn)行一個名為flanneld的代理.每個node上的flannel代理會從etcd上為當(dāng)前node申請一個CIDR地址塊用來給該node上的pod分配地址。
Flannel致力于給k8s集群中的nodes提供一個3層網(wǎng)絡(luò)千埃,他并不控制node中的容器是如何進(jìn)行組網(wǎng)的憔儿,僅僅關(guān)心流量如何在node之間流轉(zhuǎn)。
如上圖ip為10.1.15.2的pod1與另外一個Node上的10.1.20.3的pod2進(jìn)行通信镰禾。
首先pod1通過veth對把數(shù)據(jù)包發(fā)送到docker0虛擬網(wǎng)橋,網(wǎng)橋通過查找轉(zhuǎn)發(fā)表發(fā)現(xiàn)10.1.20.3不在自己管理的網(wǎng)段唱逢,就會把數(shù)據(jù)包 轉(zhuǎn)發(fā)給默認(rèn)路由(這里為flannel0網(wǎng)橋)
flannel.0網(wǎng)橋是一個vxlan設(shè)備吴侦,flannel.0收到數(shù)據(jù)包后,由于自己不是目的地10.1.20.3坞古,也要嘗試將數(shù)據(jù)包重新發(fā)送出去备韧。數(shù)據(jù)包沿著網(wǎng)絡(luò)協(xié)議棧向下流動,在二層時需要封二層以太包痪枫,填寫目的mac地址织堂,這時一般應(yīng)該發(fā)出arp:”who is 10.1.20.3″。但vxlan設(shè)備的特殊性就在于它并沒有真正在二層發(fā)出這個arp包奶陈,而是由linux kernel引發(fā)一個”L3 MISS”事件并將arp請求發(fā)到用戶空間的flanned程序易阳。
flanned程序收到”L3 MISS”內(nèi)核事件以及arp請求(who is 10.1.20.3)后,并不會向外網(wǎng)發(fā)送arp request吃粒,而是嘗試從etcd查找該地址匹配的子網(wǎng)的vtep信息潦俺,也就是會找到node2上的flanel.0的mac地址信息,flanned將查詢到的信息放入node1 host的arp cache表中,flanneel0完成這項工作后事示,linux kernel就可以在arp table中找到 10.1.20.3對應(yīng)的mac地址并封裝二層以太包了:
由于是Vlanx設(shè)備早像,flannel0還會對上面的包進(jìn)行二次封裝,封裝新的以太網(wǎng)mac幀:
node上2的eth0接收到上述vxlan包肖爵,kernel將識別出這是一個vxlan包卢鹦,于是拆包后將packet轉(zhuǎn)給node上2的flannel.0。flannel.0再將這個數(shù)據(jù)包轉(zhuǎn)到docker0劝堪,繼而由docker0傳輸?shù)絇od2的某個容器里冀自。
如上圖,總的來說就是建立VXLAN 隧道幅聘,通過UDP把IP封裝一層直接送到對應(yīng)的節(jié)點(diǎn)凡纳,實(shí)現(xiàn)了一個大的 VLAN。
四帝蒿、.Pod與Service之間的網(wǎng)絡(luò)
上面展示了Pod之間如何通過他們自己的ip地址進(jìn)行通信荐糜,但是pod的ip地址是不持久的,當(dāng)集群中pod的規(guī)母鸪縮減或者pod故障或者node故障重啟后暴氏,新的pod的ip就可能與之前的不一樣的。所以k8s中衍生出來Service來解決這個問題绣张。
k8s中 Service管理了一系列的Pods答渔,每個Service有一個虛擬的ip,要訪問service管理的Pod上的服務(wù)只需要訪問你這個虛擬ip就可以了,這個虛擬ip是固定的侥涵,當(dāng)service下的pod規(guī)模改變沼撕、故障重啟、node重啟時候芜飘,對使用service的用戶來說是無感知的敬鬓,因?yàn)樗麄兪褂玫膕ervice的ip沒有變驻售。
當(dāng)數(shù)據(jù)包到達(dá)Service虛擬ip后皮壁,數(shù)據(jù)包會被通過k8s給該servcie自動創(chuàng)建的負(fù)載均衡器路由到背后的pod容器凯楔。下面我們看看具體是如何做到的
為了實(shí)現(xiàn)負(fù)載均衡,k8s依賴linux內(nèi)建的網(wǎng)絡(luò)框架-netfilter娶牌。Netfilter是Linux提供的內(nèi)核態(tài)框架奔浅,允許使用者自定義處理接口實(shí)現(xiàn)各種與網(wǎng)絡(luò)相關(guān)的操作。 Netfilter為包過濾诗良,網(wǎng)絡(luò)地址轉(zhuǎn)換和端口轉(zhuǎn)換提供各種功能和操作汹桦,以及提供禁止數(shù)據(jù)包到達(dá)計算機(jī)網(wǎng)絡(luò)內(nèi)敏感位置的功能。在linux內(nèi)核協(xié)議棧中鉴裹,有5個跟netfilter有關(guān)的鉤子函數(shù)营勤,數(shù)據(jù)包經(jīng)過每個鉤子時灵嫌,都會檢查上面是否注冊有函數(shù),如果有的話葛作,就會調(diào)用相應(yīng)的函數(shù)處理該數(shù)據(jù)包
NF_IP_PRE_ROUTING: 接收的數(shù)據(jù)包剛進(jìn)來寿羞,還沒有經(jīng)過路由選擇,即還不知道數(shù)據(jù)包是要發(fā)給本機(jī)還是其它機(jī)器赂蠢。
NF_IP_LOCAL_IN: 已經(jīng)經(jīng)過路由選擇绪穆,并且該數(shù)據(jù)包的目的IP是本機(jī),進(jìn)入本地數(shù)據(jù)包處理流程虱岂。
NF_IP_FORWARD: 已經(jīng)經(jīng)過路由選擇玖院,但該數(shù)據(jù)包的目的IP不是本機(jī),而是其它機(jī)器第岖,進(jìn)入forward流程难菌。
NF_IP_LOCAL_OUT: 本地程序要發(fā)出去的數(shù)據(jù)包剛到IP層,還沒進(jìn)行路由選擇蔑滓。
NF_IP_POST_ROUTING: 本地程序發(fā)出去的數(shù)據(jù)包郊酒,或者轉(zhuǎn)發(fā)(forward)的數(shù)據(jù)包已經(jīng)經(jīng)過了路由選擇,即將交由下層發(fā)送出去键袱。
netfilter是工作在那一層燎窘?
iptables是運(yùn)行在用戶態(tài)的用戶程序,其基于表來管理規(guī)則蹄咖,用于定義使用netfilter框架操作和轉(zhuǎn)換數(shù)據(jù)包的規(guī)則褐健。根據(jù)rule的作用分成了好幾個表,比如用來過濾數(shù)據(jù)包的rule就會放到filter表中澜汤,用于處理地址轉(zhuǎn)換的rule就會放到nat表中蚜迅,其中rule就是應(yīng)用在netfilter鉤子上的函數(shù),用來修改數(shù)據(jù)包的內(nèi)容或過濾數(shù)據(jù)包俊抵。目前iptables支持的表有下面這些:
Filter:從名字就可以看出谁不,這個表里面的rule主要用來過濾數(shù)據(jù),用來控制讓哪些數(shù)據(jù)可以通過务蝠,哪些數(shù)據(jù)不能通過拍谐,它是最常用的表烛缔。
NAT(*):里面的rule都是用來處理網(wǎng)絡(luò)地址轉(zhuǎn)換的馏段,控制要不要進(jìn)行地址轉(zhuǎn)換,以及怎樣修改源地址或目的地址践瓷,從而影響數(shù)據(jù)包的路由院喜,達(dá)到連通的目的,這是家用路由器必備的功能晕翠。
Mangle:里面的rule主要用來修改IP數(shù)據(jù)包頭喷舀,比如修改TTL值砍濒,同時也用于給數(shù)據(jù)包添加一些標(biāo)記,從而便于后續(xù)其它模塊對數(shù)據(jù)包進(jìn)行處理(這里的添加標(biāo)記是指往內(nèi)核skb結(jié)構(gòu)中添加標(biāo)記硫麻,而不是往真正的IP數(shù)據(jù)包上加?xùn)|西)爸邢。
Raw:在netfilter里面有一個叫做connection tracking的功能,主要用來追蹤所有的連接拿愧,而raw表里的rule的功能是給數(shù)據(jù)包打標(biāo)記杠河,從而控制哪些數(shù)據(jù)包不被connection tracking所追蹤。
Security:里面的rule跟SELinux有關(guān)浇辜,主要是在數(shù)據(jù)包上設(shè)置一些SELinux的標(biāo)記券敌,便于跟SELinux相關(guān)的模塊來處理該數(shù)據(jù)包。
在k8s中柳洋,iptables規(guī)則由kube-proxy控制器配置待诅,該控制器監(jiān)視K8s API服務(wù)器的更改。當(dāng)對Service或Pod的虛擬IP地址進(jìn)行修改時熊镣,iptables規(guī)則也會更新以便讓service能夠正確的把數(shù)據(jù)包路由到后端Pod卑雁。
iptables規(guī)則監(jiān)視發(fā)往Service虛擬IP的流量,并且在匹配時轧钓,從可用Pod集合中選擇隨機(jī)Pod IP地址,iptables規(guī)則將數(shù)據(jù)包的目標(biāo)IP地址從Service的虛擬IP更改為選定的Pod的ip序厉。總的來說iptables已在機(jī)器上完成負(fù)載平衡毕箍,并將指向Servcie的虛擬IP的流量轉(zhuǎn)移到實(shí)際的pod的IP弛房。
在從service到pod的路徑上,IP地址來自目標(biāo)Pod而柑。 在這種情況下文捶,iptables再次重寫IP頭以將Pod IP替換為Service的IP,以便Pod認(rèn)為它一直與Service的虛擬IP通信媒咳。
如上圖當(dāng)從一個Pod發(fā)送數(shù)據(jù)包到Service時候粹排,數(shù)據(jù)包先從Pod1所在的虛擬設(shè)備eth0離開pod1,并通過veth對的另外一端veth0傳遞給網(wǎng)橋cbr0,網(wǎng)橋找不到service對應(yīng)ip的mac地址,所以把包轉(zhuǎn)發(fā)給默認(rèn)路由涩澡,也就是root命名空間的eth0
在root命名空間的設(shè)備eth0接受到數(shù)據(jù)包前顽耳,數(shù)據(jù)包會經(jīng)過iptables進(jìn)行過濾,iptables接受數(shù)據(jù)包后會使用kube-proxy在Node上安裝的規(guī)則來響應(yīng)Service或Pod的事件妙同,將數(shù)據(jù)包的目的地址從Service的IP重寫為Service后端特定的Pod IP(本例子中是pod4).
現(xiàn)在數(shù)據(jù)包的目的ip就不再是service的ip地址了射富,而是pod4的ip地址。
iptables利用Linux內(nèi)核的conntrack來記住所做的Pod選擇粥帚,以便將來的流量路由到同一個Pod(禁止任何擴(kuò)展事件)胰耗。 從本質(zhì)上講,iptables直接在Node上進(jìn)行了集群內(nèi)負(fù)載均衡芒涡,然后流量使用我們已經(jīng)檢查過的Pod-to-Pod路由流到Pod柴灯。
收到此數(shù)據(jù)包的Pod將會回發(fā)包到源Pod卖漫,回包的源IP識別為自己的IP(比如這里為Pod4的ip),將目標(biāo)IP設(shè)置為最初發(fā)送數(shù)據(jù)包的Pod(這里為pod1的ip)赠群。
數(shù)據(jù)包進(jìn)入目標(biāo)Pod(這里為Pod1)所在節(jié)點(diǎn)后羊始,數(shù)據(jù)包流經(jīng)iptables,它使用conntrack記住它之前做出的選擇查描,并將數(shù)據(jù)包的源IP重寫為Service的IP店枣。 從這里開始,數(shù)據(jù)包通過網(wǎng)橋流向與Pod1的命名空間配對的虛擬以太網(wǎng)設(shè)備叹誉,并流向我們之前看到的Pod1的以太網(wǎng)設(shè)備鸯两。
五.Internet與Service之間的網(wǎng)絡(luò)
到目前為止,我們已經(jīng)了解了如何在Kubernetes集群中路由流量长豁。下面我們希望將服務(wù)暴露給外部使用(互聯(lián)網(wǎng))钧唐。 這需要強(qiáng)調(diào)兩個相關(guān)的問題:(1)從k8s的service訪問Internet,以及(2)從Internet訪問k8s的service.
從Node到公共Internet的路由流量是特定于網(wǎng)絡(luò)的匠襟,實(shí)際上取決于網(wǎng)絡(luò)配置钝侠。為了使本節(jié)更具體,下面使用AWS VPC討論具體細(xì)節(jié)酸舍。
在AWS中帅韧,k8s集群在VPC內(nèi)運(yùn)行,其中每個Node都分配了一個可從k8s集群內(nèi)訪問的私有IP地址啃勉。要使群集外部的流量可訪問忽舟,需要將Internet網(wǎng)關(guān)連接到VPC。 Internet網(wǎng)關(guān)有兩個目的:在VPC路由表中提供可以路由到Internet的流量的目標(biāo)淮阐,以及為已分配公共IP地址的任何實(shí)例執(zhí)行網(wǎng)絡(luò)地址轉(zhuǎn)換(NAT)叮阅。 NAT轉(zhuǎn)換負(fù)責(zé)將群集專用的節(jié)點(diǎn)內(nèi)部IP地址更改為公共Internet中可用的外部IP地址。
通過Internet網(wǎng)關(guān)泣特,Node可以將流量路由到Internet浩姥。不幸的是,有一個小問題状您。 Pod具有自己的IP地址勒叠,該IP地址與承載Pod的Node的IP地址不同,并且Internet網(wǎng)關(guān)上的NAT轉(zhuǎn)換僅適用于Node的 IP地址膏孟,因?yàn)樗恢繬ode上正在運(yùn)行那些Pod - Internet網(wǎng)關(guān)不是容器感知的眯分。讓我們再次看看k8s如何使用iptables解決這個問題。
本質(zhì)都是使用NAT來做
如上圖中骆莹,數(shù)據(jù)包源自Pod1的網(wǎng)絡(luò)命名空間颗搂,并通過veth對連接到root命名空間担猛。
一旦數(shù)據(jù)包到達(dá)root命名空間幕垦,數(shù)據(jù)包就會從網(wǎng)橋cbr0流傳到到默認(rèn)設(shè)備eth0丢氢,因?yàn)閿?shù)據(jù)包上的目的IP與連接到網(wǎng)橋的任何網(wǎng)段都不匹配,在到達(dá)root命名空間的以太網(wǎng)設(shè)備eth0之前先改,iptables會修改數(shù)據(jù)包疚察。
在這種情況下,數(shù)據(jù)包的源IP地址是Pod1的ip地址仇奶,如果我們將源保持為Pod1貌嫡,則Internet網(wǎng)關(guān)將拒絕它,因?yàn)榫W(wǎng)關(guān)NAT僅了解連接到vm的IP地址该溯。解決方案是讓iptables執(zhí)行源NAT - 更改數(shù)據(jù)包源 - 以便數(shù)據(jù)包看起來來自VM而不是Pod岛抄。
有了正確的源IP,數(shù)據(jù)包現(xiàn)在可以離開VM,并到達(dá)Internet網(wǎng)關(guān)狈茉。 Internet網(wǎng)關(guān)將執(zhí)行另一個NAT夫椭,將源IP從VM內(nèi)部IP重寫為Internet IP。最后氯庆,數(shù)據(jù)包將到達(dá)公共互聯(lián)網(wǎng)蹭秋。在回來數(shù)據(jù)包的路上,數(shù)據(jù)包遵循相同的路徑堤撵,任何源IP都會與發(fā)送時候做相同的修改操作仁讨,以便系統(tǒng)的每一層都接收它理解的IP地址:Node,以及Pod命名空間中中的Pod IP实昨。
讓Internet流量進(jìn)入k8s集群洞豁,這特定于配置的網(wǎng)絡(luò),可以在網(wǎng)絡(luò)堆棧的不同層來實(shí)現(xiàn):
(1) NodePort
(2)Service LoadBalancer
(3)Ingress控制器荒给。
讓外網(wǎng)訪問k8s內(nèi)部的服務(wù)的第一個方法是創(chuàng)建一個NodePort類型的Service族跛,
對于NodePort類型的Service,k8s集群中的每個Node都會打開一個端口(所有Node上的端口相同)锐墙,并將該端口上收到的流量重定向到具體的Service礁哄。
對于NodePort類型的Service,我們可以通過任何Node的ip和端口號來訪問NodePort服務(wù)溪北。
創(chuàng)建NodePort類型的服務(wù):
如下圖桐绒,服務(wù)暴露在兩個節(jié)點(diǎn)的端口30123上,到達(dá)任何一個端口的鏈接會被重定向到一個隨機(jī)選擇的Pod之拨。
如何做到的茉继?
NodePort是靠kube-proxy服務(wù)通過iptables的nat轉(zhuǎn)換功能實(shí)現(xiàn)的,kube-proxy會在運(yùn)行過程中動態(tài)創(chuàng)建與Service相關(guān)的iptables規(guī)則蚀乔,這些規(guī)則實(shí)現(xiàn)了NodePort的請求流量重定向到kube-proxy進(jìn)程上對應(yīng)的Service的代理端口上烁竭。
kube-proxy接受到Service的請求訪問后,會從service對應(yīng)的后端Pod中選擇一個進(jìn)行訪問(RR)
但 NodePort 還沒有完全解決外部訪問 Service 的所有問題吉挣,比如負(fù)載均衡問題派撕,假如我們 的集群中有 10 個 Node婉弹,則此時最好有一個負(fù)載均衡器,外部的請求只需訪問此負(fù)載均衡器的 IP 地址终吼,由負(fù)載均衡器負(fù) 責(zé)轉(zhuǎn)發(fā)流量到后面某個 Node 的 NodePort 上
該方式是NodePort方式的擴(kuò)展镀赌,這使得Service可以通過一個專用的負(fù)載均衡器來訪問,這個是由具體云服務(wù)提供商來提供的际跪,負(fù)載均衡器將流量重定向到所有節(jié)點(diǎn)的端口上商佛,如果云提供商不支持負(fù)載均衡,則退化為NodePort類型.本質(zhì)是所有NodeIp:nodePort的負(fù)載均衡器
創(chuàng)建k8s service時姆打,可以選擇指定LoadBalancer良姆。 LoadBalancer的實(shí)現(xiàn)由云控制器提供,該控制器知道如何為您的service創(chuàng)建負(fù)載均衡器幔戏。 創(chuàng)建service后歇盼,它將公布負(fù)載均衡器的IP地址。 作為最終用戶评抚,可以開始將流量定向到負(fù)載均衡器以開始與提供的service進(jìn)行通信豹缀。
創(chuàng)建一個負(fù)載均衡服務(wù):
借助AWS,負(fù)載均衡器可以識別其目標(biāo)組中的Node慨代,并將平衡群集中所有節(jié)點(diǎn)的流量邢笙。 一旦流量到達(dá)Node,之前在整個群集中為Service安裝的iptables規(guī)則將確保流量到達(dá)感興趣的Service的Pod上侍匙。
下面看下LoadBalancer到Service的一個數(shù)據(jù)包的流轉(zhuǎn)過程:
我們來看看LoadBalancer在實(shí)踐中是如何運(yùn)作的氮惯。部署Service后,正在使用的云提供商將會創(chuàng)建一個新的負(fù)載均衡器(1)想暗。
由于負(fù)載均衡器不能識別容器妇汗,因此一旦流量到達(dá)負(fù)載均衡器,它就會把數(shù)據(jù)包發(fā)送到在構(gòu)成群集的某個Node中(2)说莫。每個Node上的iptables規(guī)則將來自負(fù)載均衡器的傳入流量重定向到正確的Pod(3)
這些iptables規(guī)則是在service創(chuàng)建時候創(chuàng)建杨箭。從Pod到請求方的響應(yīng)將返回Pod的IP,但請求方需要具有負(fù)載均衡器的IP地址储狭。這就需要iptables和conntrack用于在返回路徑上正確地重寫IP互婿。
上圖顯示了承載Pod的三個Node前面的網(wǎng)絡(luò)負(fù)載平衡器。首先流量被傳到的Service的負(fù)載均衡器(1)辽狈。一旦負(fù)載均衡器收到數(shù)據(jù)包(2)慈参,它就會隨機(jī)選擇一個VM。這里我們故意選擇了沒有Pod運(yùn)行的node:node 2刮萌。在這里驮配,node上運(yùn)行的iptables規(guī)則將使用kube-proxy安裝在集群中的內(nèi)部負(fù)載平衡規(guī)則將數(shù)據(jù)包定向到正確的Node 中的Pod。 iptables執(zhí)行正確的NAT并將數(shù)據(jù)包轉(zhuǎn)發(fā)到正確的Pod(4)。
需要注意的是每個服務(wù)需要創(chuàng)建自己獨(dú)有的負(fù)載均衡器壮锻,下面要講解的一種方式所有服務(wù)只需要一個公開服務(wù)琐旁。
5.2.3 第七層流量入口:Ingress Controller
這是一個與上面提到的兩種方式完全不同的機(jī)制,通過一個公開的ip地址來公開多個服務(wù)躯保,第7層網(wǎng)絡(luò)流量入口是在網(wǎng)絡(luò)堆棧的HTTP / HTTPS協(xié)議范圍內(nèi)運(yùn)行,并建立在service之上澎语。
如上圖途事,不像負(fù)載均衡器每個服務(wù)需要一個公開ip,ingress所有服務(wù)只需要一個公網(wǎng)ip,當(dāng)客戶端向Ingress發(fā)送http請求時候,ingress會根據(jù)請求的主機(jī)名和路徑?jīng)Q定請求轉(zhuǎn)發(fā)到那個服務(wù)擅羞。
創(chuàng)建Ingress資源:
如上定義了一個單一規(guī)則的Ingress,確保Ingress控制器接受的所有請求主機(jī)kubia.example.com的http請求被發(fā)送到端口80上的kubia-nodeport服務(wù)上尸变。
工作原理:
如下圖,客戶端首先對kubia.example.com執(zhí)行DNS查找减俏,DNS服務(wù)器返回Ingress控制器的IP召烂,客戶端拿到IP后,向Ingress控制器發(fā)送Http請求娃承,并在Host投中指定kubia.example.com奏夫。控制器接受到請求后從Host頭部就知道該訪問那個服務(wù)历筝,通過與該Service關(guān)聯(lián)的endpoint對象查詢Pod ip,并將請求轉(zhuǎn)發(fā)到某一個Pod酗昼。
這里Ingress并沒把請求轉(zhuǎn)發(fā)給Service,而是自己選擇一個一個Pod來訪問梳猪。
第7層負(fù)載均衡器的一個好處是它們具有HTTP感知能力麻削,因此它們了解URL和路徑。 這允許您按URL路徑細(xì)分服務(wù)流量春弥。 它們通常還在HTTP請求的X-Forwarded-For標(biāo)頭中提供原始客戶端的IP地址呛哟。