1.Pod網(wǎng)絡(luò):同一pod內(nèi)不同容器通信
Pod是Kubernetes中最小的可部署單元,它是一個或多個緊密關(guān)聯(lián)的容器的組合,這些容器共享同一個網(wǎng)絡(luò)命名空間和存儲卷,因此Pod中的
所有容器都共享相同的網(wǎng)絡(luò)命名空間和IP地址——PodIP
,所以在同一個Pod內(nèi)的容器間通信可以通過localhost直接通信
。
k8s創(chuàng)建Pod時永遠都是首先創(chuàng)建Infra 容器
,也可以被稱為pause容器
。這個容器為其他容器提供了一個共享的基礎(chǔ)設(shè)施,包括網(wǎng)絡(luò)和存儲功能,其他業(yè)務(wù)容器共享pause容器的網(wǎng)絡(luò)棧和Volume掛載卷。
pause 容器被創(chuàng)建后會初始化Network Namespace網(wǎng)絡(luò)棧,之后其他容器就可以加入到pause 容器中共享Infra容器的網(wǎng)絡(luò)了。而對于同一個 Pod 里面的所有用戶容器來說,它們的進出流量,認為都是通過 pause 容器完成的。
pause 容器會創(chuàng)建并管理虛擬以太網(wǎng)(veth)接口
。在容器啟動之前,pause 容器會為每個容器創(chuàng)建一個虛擬以太網(wǎng)接口,一個保留在宿主機上(稱為 vethxxx),另一個保留在容器網(wǎng)絡(luò)命名空間內(nèi)
并重命名為 eth0
,如下圖所示。這兩個虛擬接口的兩端是連接在一起的,從一端進入的數(shù)據(jù)會從另一端出來。
pause容器主要為每個業(yè)務(wù)容器提供以下功能:
- IPC命名空間:Pod中的多個容器能夠使用SystemV IPC或POSIX消息隊列進行通信。
- 網(wǎng)絡(luò)命名空間:Pod中的多個容器能夠訪問同一個IP和端口范圍。
- PID命名空間:Pod中的不同應(yīng)用程序可以看到其他應(yīng)用程序的進程ID。
- UTS命名空間:Pod中的多個容器共享一個主機名;
- Volumes(共享存儲卷):Pod中的各個容器可以訪問在Pod級別定義的Volum
2.Pod網(wǎng)絡(luò):同一節(jié)點不同pod間相互通信
同一節(jié)點不同POD之間的通信是通過將容器網(wǎng)絡(luò)接口(CNI)
與主機網(wǎng)絡(luò)命名空間中的虛擬以太網(wǎng)(veth)接口
相連來實現(xiàn)的。 每生成一個新的Pod,那么在Node上都會根據(jù)插件來生成一個新的虛擬網(wǎng)卡如vethxxxx或者calixxxx,這個網(wǎng)卡會對應(yīng)到Pod里的eth0。
如圖,veth接口則被保留在主機的網(wǎng)絡(luò)命名空間中,并被連接到CNI插件(如Flannel或Calico等)創(chuàng)建的虛擬網(wǎng)橋
(如cni0或flannel0等)上。一旦這些veth接口被正確地連接起來,它們就可以進行通信了。當一個POD發(fā)送數(shù)據(jù)包時,數(shù)據(jù)包會通過其veth接口被發(fā)送到主機網(wǎng)絡(luò)命名空間中的veth接口,然后該veth接口會將數(shù)據(jù)包發(fā)送到虛擬網(wǎng)橋上。虛擬網(wǎng)橋又會將數(shù)據(jù)包路由到目標POD的veth接口,最終將數(shù)據(jù)包發(fā)送到目標POD。
如圖所示的ip地址與網(wǎng)橋網(wǎng)段,同一節(jié)點的不同POD的IP地址通常屬于同一網(wǎng)段
,并通過CNI插件連接到同一個虛擬網(wǎng)橋(如cni0)上。虛擬網(wǎng)橋會管理其IP地址空間和分配,確保不同POD的IP地址不會沖突。
具體的IP地址和網(wǎng)段取決于所使用的CNI插件和網(wǎng)絡(luò)方案。例如,F(xiàn)lannel插件默認使用10.244.x.0/24的網(wǎng)段,其中x是隨機分配給每個POD的。這意味著不同POD的IP地址將位于10.244.x.0/24的網(wǎng)段中,其中x是不同的值。
2.1 CNI介紹
CNI如Calico、flannel等本身并不能提供網(wǎng)絡(luò)服務(wù),它只是定義了對容器網(wǎng)絡(luò)進行操作和配置的規(guī)范。CNI僅關(guān)注在創(chuàng)建容器時分配網(wǎng)絡(luò)資源,和在銷毀容器時刪除網(wǎng)絡(luò)資源。常見的CNI插件包括Calico、flannel等。
具體的流程如下圖所示:
在集群里面創(chuàng)建一個 Pod 的時候,首先會通過 apiserver 將 Pod 的配置寫入。apiserver 的一些管控組件(比如 Scheduler)會調(diào)度到某個具體的節(jié)點上去。Node節(jié)點上的Kubelet 監(jiān)聽到這個Pod的創(chuàng)建之后,它首先會讀取剛才我們所說的配置目錄中的配置文件,配置文件里面會聲明所使用的是哪一個插件,然后去執(zhí)行具體的CNI插件的二進制文件,再由CNI插件進入Pod的網(wǎng)絡(luò)空間去配置Pod的網(wǎng)絡(luò)。在這個過程中,CNI插件會為Pod創(chuàng)建一個網(wǎng)絡(luò)命名空間,并將其中一個veth接口連接到該命名空間中,另一個veth接口保留在主機的網(wǎng)絡(luò)命名空間中,并連接到CNI插件創(chuàng)建的虛擬網(wǎng)橋上。
3.Pod網(wǎng)絡(luò):不同節(jié)點pod相互通信
若不同節(jié)點pod想要相互通信,在cni0網(wǎng)橋外還有一層CNI插件配置的網(wǎng)絡(luò)隧道,如上圖新的虛擬網(wǎng)卡flannel0接收cni0網(wǎng)橋的數(shù)據(jù),并通過維護路由表,對接收到的數(shù)據(jù)進行封包和轉(zhuǎn)發(fā)(vxlan隧道)。
cni0:網(wǎng)橋設(shè)備,每創(chuàng)建一個pod都會創(chuàng)建一對 veth pair。其中一段是pod中的eth0,另一端是cni0網(wǎng)橋中的端口。
VTEP設(shè)備:、VXLAN Tunnel End Point(虛擬隧道端點),在Flannel中 VNI的默認值是1,這也是為什么宿主機的VTEP設(shè)備都叫flannel.1的原因。VTEP設(shè)備之間通過二層數(shù)據(jù)幀進行通信,源VTEP設(shè)備收到原始IP包后,在上面加上一個目的MAC地址,封裝成一個內(nèi)部數(shù)據(jù)幀,發(fā)送給目的VTEP設(shè)備。
flannel.1:vxlan網(wǎng)關(guān)設(shè)備,用戶 vxlan 報文的解包和封包。不同的 pod 數(shù)據(jù)流量都從overlay設(shè)備以隧道的形式發(fā)送到對端。flannel.1不會發(fā)送arp請求去獲取目標IP的mac地址,而是由Linuxkernel將一個"L3 Miss"事件請求發(fā)送到用戶空間的flanneld程序,flanneld程序收到內(nèi)核的請求事件后,從etcd中查找能夠匹配該地址的子網(wǎng)flannel.1設(shè)備的mac地址,即目標pod所在host中flannel.1設(shè)備的mac地址。
flanneld:在每個主機中運行flanneld作為agent,它會為所在主機從集群的網(wǎng)絡(luò)地址空間中,獲取一個小的網(wǎng)段subnet,本主機內(nèi)所有容器的IP地址都將從中分配。同時Flanneld監(jiān)聽K8s集群數(shù)據(jù)庫,為flannel.1設(shè)備提供封裝數(shù)據(jù)時必要的mac,ip等網(wǎng)絡(luò)數(shù)據(jù)信息。
VXLAN:Virtual eXtensible Local Area Network,虛擬擴展局域網(wǎng)。采用L2 over L4(MAC-in-UDP)的報文封裝模式,將二層報文用三層協(xié)議進行封裝,實現(xiàn)二層網(wǎng)絡(luò)在三層范圍內(nèi)進行擴展,同時滿足數(shù)據(jù)中心大二層虛擬遷移和多租戶的需求。flannel只使用了vxlan的部分功能,VNI被固定為1。
內(nèi)部數(shù)據(jù)楨,并不能在宿主機的二層網(wǎng)絡(luò)傳輸,Linux內(nèi)核還需要把它進一步封裝成為宿主機的一個普通的數(shù)據(jù)幀,承載著內(nèi)部數(shù)據(jù)幀通過宿主機的eth0進行傳輸。
容器跨網(wǎng)絡(luò)通信解決方案:如果集群的主機在同一個子網(wǎng)內(nèi),則跳過flannel.1隧道,而是直接通過路由eth0轉(zhuǎn)發(fā)過去;若不在一個子網(wǎng)內(nèi),就通過隧道轉(zhuǎn)發(fā)過去。
不同node上的pod通信流程:
- pod中的數(shù)據(jù),根據(jù)pod的路由信息,發(fā)送到網(wǎng)橋 cni0
- cni0 根據(jù)節(jié)點路由表,將數(shù)據(jù)發(fā)送到隧道設(shè)備flannel.1
- flannel.1 查看數(shù)據(jù)包的目的ip,從flanneld獲取對端隧道設(shè)備的必要信息,封裝數(shù)據(jù)包 flannel.1,將數(shù)據(jù)包發(fā)送到對端設(shè)備。
- 數(shù)據(jù)包中包括
Outer IP 與 Inner IP等相關(guān)信息, 將PodIP和所在的NodeIP關(guān)聯(lián)起來
,通過這個關(guān)聯(lián)讓不同的Pod互相訪問。 - 對端節(jié)點的網(wǎng)卡接收到數(shù)據(jù)包,發(fā)現(xiàn)數(shù)據(jù)包為overlay數(shù)據(jù)包,解開外層封裝,并發(fā)送內(nèi)層封裝到flannel.1
- 設(shè)備 Flannel.1 設(shè)備查看數(shù)據(jù)包,根據(jù)路由表匹配,將數(shù)據(jù)發(fā)送給cni0設(shè)備
- cni0匹配路由表,發(fā)送數(shù)據(jù)到網(wǎng)橋
3.1 Flannel容器網(wǎng)絡(luò)介紹
Flannel之所以可以搭建kubernets依賴的底層網(wǎng)絡(luò),是因為它可以實現(xiàn)以下兩點:
- 給每個node上的docker容器分配相互不想沖突的IP地址;
- 能給這些IP地址之間建立一個覆蓋網(wǎng)絡(luò),同過覆蓋網(wǎng)絡(luò),將數(shù)據(jù)包原封不動的傳遞到目標容器內(nèi)。
Flannel網(wǎng)絡(luò)傳輸過程:
- 數(shù)據(jù)從源容器中發(fā)出后,經(jīng)由所在主機的dockerO虛擬網(wǎng)卡轉(zhuǎn)發(fā)到flannel0虛擬網(wǎng)卡這是個P2P的虛擬網(wǎng)卡,flanneld服務(wù)監(jiān)聽在網(wǎng)卡的另外—端(Flannel通過Etcd服務(wù)維護了—張節(jié)點間的路由表);
- 源主機的flanneld服務(wù)將原本的數(shù)據(jù)內(nèi)容UDP封裝后根據(jù)自己的路由表投遞給目的節(jié)點的flanneld服務(wù),數(shù)據(jù)到達以后被解包,然后直接進入目的節(jié)點的flannel0虛擬網(wǎng)卡,然后被轉(zhuǎn)發(fā)到目的主機的dockerO虛擬網(wǎng)卡;
- ·最后就像本機容器通信一下的有docker路由到達目標容器,這樣整個數(shù)據(jù)包的傳遞就完成了。
4. Service網(wǎng)絡(luò):ClusterIp
在Kubernetes中,Pod是非持久性的資源
,可以按照需要創(chuàng)建和銷毀。當使用Deployment來運行應(yīng)用時,可以根據(jù)需要動態(tài)地創(chuàng)建或銷毀Pod,實現(xiàn)水平擴縮容。
當引入Deployment,并為Pod設(shè)置多個副本時,那么某一個服務(wù)就會有多個pod及多個podIp,此時即使知道了這些Pod的IP,那訪問起來也并不方便。此外,Pod的IP地址是動態(tài)分配
的,可能發(fā)生變化,所以在實際通信中,直接使用Pod的IP地址進行通信會有一些問題。為了解決這個問題,Kubernetes引入了Service的概念。
所以,這里需要有一個統(tǒng)一入口,其它Pod通過這個統(tǒng)一入口去請求該服務(wù)(Nginx)對應(yīng)的所有Pod。這時就有了Service這個資源對象,它主要作用就是用來提供統(tǒng)一入口,也就是說只需要一個IP就能訪問所有的Pod,而這個入口IP就是ClusterIP,也就是Service的IP。
Pod IP 地址是實際存在于某個網(wǎng)卡(可以是虛擬設(shè)備)上的,但
Cluster IP是一個完全虛擬的IP
,沒有網(wǎng)絡(luò)設(shè)備與其對應(yīng)。
- Cluster IP僅僅作用于Kubernetes Service這個對象,并由Kubernetes管理和分配P地址
- Cluster IP無法被ping,他沒有一個“實體網(wǎng)絡(luò)對象”來響應(yīng)
- Cluster IP只能結(jié)合Service Port組成一個具體的通信端口,單獨的Cluster IP不具備通信的基礎(chǔ),并且他們屬于Kubernetes集群這樣一個封閉的空間。
- 虛擬 ip 是固定的,這也是service可以解決pod動態(tài)ip的原因
Service是一種持久性資源
,它可以提供一個或多個端點(Endpoint),并通過標簽選擇器選擇指向集群中的一組Pod。Service使用ClusterIP來提供內(nèi)部集群的網(wǎng)絡(luò)連接,ClusterIP是固定的虛擬ip,這樣就可以通過Service來訪問Pod
,而不需要直接使用Pod的動態(tài)IP地址。當創(chuàng)建一個Deployment并運行應(yīng)用時,通常會創(chuàng)建一個或多個Service來提供訪問Pod的接口。這樣,即使Pod的IP地址發(fā)生變化,通過Service的端點仍然可以訪問到Pod,以確保集群內(nèi)的可靠通信,并避免因Pod的動態(tài)變化而引起的問題。
K8s通過在引入一層Service抽象,還能解決以下問題:
-
服務(wù)發(fā)現(xiàn):Service提供統(tǒng)一的ClusterIP來解決服務(wù)發(fā)現(xiàn)問題,Client只需通過ClusterIP就可以訪問App的Pod集群,不需要關(guān)心集群中的具體Pod數(shù)量和PodIP,即使是PodIP發(fā)生變化也會被ClusterIP所屏蔽。注意,這里的ClusterIP實際是個虛擬IP,也稱Virtual IP(VIP)。
-
負載均衡:Service抽象層具有負載均衡的能力,支持以不同策略去訪問App集群中的不同Pod實例,以實現(xiàn)負載分攤和HA高可用。K8s中默認的負載均衡策略是RoundRobin,也可以定制其它復(fù)雜策略。
Service在上述K8s集群中被畫成一個獨立組件,實際是沒有獨立Service這樣一個組件的,只是一個抽象概念。
4.1 服務(wù)發(fā)現(xiàn)
K8s通過一個ServiceName+ClusterIP統(tǒng)一屏蔽服務(wù)發(fā)現(xiàn)和負載均衡,底層技術(shù)是在DNS+Service Registry基礎(chǔ)上發(fā)展演進出來。K8s的服務(wù)發(fā)現(xiàn)和負載均衡是在客戶端通過Kube-Proxy + iptables轉(zhuǎn)發(fā)實現(xiàn),它對應(yīng)用無侵入,且不穿透Proxy,沒有額外性能損耗。K8s服務(wù)發(fā)現(xiàn)機制,可以認為是現(xiàn)代微服務(wù)發(fā)現(xiàn)機制和傳統(tǒng)Linux內(nèi)核機制的優(yōu)雅結(jié)合。
5. 外部訪問集群網(wǎng)絡(luò)
5.1 NodePort
K8s的Service網(wǎng)絡(luò)ClusterIp只是一個集群內(nèi)部網(wǎng)絡(luò),集群外部是無法直接訪問的。如果我們要將K8s內(nèi)部的一個服務(wù)通過NodePort方式暴露出去,使用Service的NodePort類型,將Service的ClusterIP對應(yīng)的Port映射到每一個Node的IP上,映射出去的Port范圍為30000~32767。
Service NodePort服務(wù)發(fā)布以后,K8s在每個Worker節(jié)點上都會開啟nodePort這個端口。這個端口的背后是Kube-Proxy
,當K8s外部有Client要訪問K8s集群內(nèi)的某個服務(wù),它通過這個服務(wù)的NodePort端口發(fā)起調(diào)用,這個調(diào)用通過Kube-Proxy轉(zhuǎn)發(fā)到內(nèi)部的Servcie抽象層,然后再轉(zhuǎn)發(fā)到目標Pod上。
5.1.1 containerPort、port、nodePort、targetPort的區(qū)別與聯(lián)系
-
containerPort:
Container容器暴露的端口
。containerPort是在pod控制器中定義的、pod中的容器需要暴露的端口。 -
port:
service暴露在集群中的端口,僅限集群內(nèi)部訪問
。port是暴露在cluster (集群網(wǎng)絡(luò))上的端口,提供了集群內(nèi)部客戶端訪問service的入口,即clusterIP:port。mysql容器暴露了3306端口(參考DockerFile),集群內(nèi)其他容器通過33306端口訪問mysql服務(wù),但是外部流量不能訪問mysql服務(wù),因為mysql服務(wù)沒有配置NodePort。 -
nodePort:
集群節(jié)點Node暴露在外網(wǎng)中的端口
。nodePort提供了集群外部客戶端訪問service的一種方式,即nodeIP:nodePort提供了外部網(wǎng)絡(luò)訪問k8s集群中service的入口。 -
targetPort:
Pod暴露的端口
。targetPort是pod上的端口,從port/nodePort下來的數(shù)據(jù),經(jīng)過kube-proxy流入到后端pod的targetPort上,最后進入容器,因此targetPort與容器的containerPort必須一致。
5.2 LoadBalancer云負載均衡器
在NodePort的基礎(chǔ)上增加了云服務(wù)商提供的負載均衡器Load-Balancer,使用該均衡器后進行訪問時不需要再帶著nodePort30055,可以將流量自動分配到集群的不同節(jié)點上。
5.3 Nginx Service反向代理、域名
Nodeport只支持四層的數(shù)據(jù)包轉(zhuǎn)發(fā),所以不支持基于域名的做分流,基于uri做匹配。因此我們考慮在Nodeport上添加一層Nginx Service進行反向代理。
使用 Nginx 作為反向代理服務(wù)器是一種常見的做法,它可以為多個服務(wù)提供負載均衡和路由功能。在這種情況下,Nginx 通常被部署在一個單獨的 Pod 中,并且該 Pod 會被 Service 暴露給外部網(wǎng)絡(luò)。當外部請求到達 Nginx Service 時,它會被路由到 Nginx Pod。然后,Nginx Pod 根據(jù)配置將請求轉(zhuǎn)發(fā)到相應(yīng)的 APP Service。
上圖為抓包模擬客戶端訪問的情況,通過抓包我們可以發(fā)現(xiàn)在使用Nginx進行反向代理時訪問pod中的服務(wù)需要進行大量包的傳輸,在實驗時訪問一個靜態(tài)頁面需要請求59個包,這帶來了巨大的資源損失,因此引出了Ingress。
5.4 Ingress 代理服務(wù)
Ingress算是Service上面的一層代理,通常在 Service前使用Ingress來提供HTTP路由配置。它讓我們可以設(shè)置外部 URL、基于域名的虛擬主機、SSL 和負載均衡。
ingress相當于一個7層的負載均衡器,是k8s對反向代理的一個抽象。大概的工作原理也確實類似于Nginx,可以理解成在 Ingress 里建立一個個映射規(guī)則 , ingress Controller 通過監(jiān)聽 Ingress這個api對象里的配置規(guī)則并轉(zhuǎn)化成 Nginx 的配置(kubernetes聲明式API和控制循環(huán)) , 然后對外部提供服務(wù)。ingress包括:ingress controller和ingress resources
- ingress controller:核心是一個deployment,實現(xiàn)方式有很多,比如nginx, Contour, Haproxy, trafik, Istio,需要編寫的yaml有:Deployment, Service, ConfigMap, ServiceAccount(Auth),其中service的類型可以是NodePort或者LoadBalancer。
- ingress resources:這個就是一個類型為Ingress的k8s api對象了,這部分則是面向開發(fā)人員。
文章來源:http://www.zghlxwxcb.cn/news/detail-755023.html
如上圖所示,為使用Ingress之后的抓包流程,可以看到此時eth0與docker網(wǎng)橋并沒有通過Kube-Proxy進行通信,而是直接通過Ingress Controller進行通信,再次抓包訪問一個靜態(tài)網(wǎng)頁需要35個包。文章來源地址http://www.zghlxwxcb.cn/news/detail-755023.html
到了這里,關(guān)于K8s進階之網(wǎng)絡(luò):pod內(nèi)不同容器、同節(jié)點不同pod通信、CNI插件、不同節(jié)點pod通信、Flannel容器網(wǎng)絡(luò)、Serivce連接外部網(wǎng)絡(luò)、服務(wù)發(fā)現(xiàn)、Nginx反向代理與域名、Ingress代理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!