天天看點

從零開始入門 K8s | Kubernetes 網絡模型進階Kubernetes 網絡模型來龍去脈Pod 究竟如何上網Service 究竟如何工作啥?負載均衡還分内部外部本文總結

作者 |  葉磊(稻農)阿裡巴巴進階技術專家

本文整理自《CNCF x Alibaba 雲原生技術公開課》第 25 講,

點選直達課程頁面

關注“阿裡巴巴雲原生”公衆号,回複關鍵詞“入門”,即可下載下傳從零入門 K8s 系列文章 PPT。

導讀:本文将基于之前介紹的

基本網絡模型 ,進行更深入的一些了解,希望給予讀者一個更廣更深的認知。首先簡單回顧一下容器網絡的曆史沿革,剖析一下 Kubernetes 網絡模型的由來;其次會剖析一個實際的實作(Flannel Hostgw),展現了資料包從容器到主控端的變換過程;最後對于和網絡息息相關的 Servcie 做了比較深入的機制和使用介紹,通過一個簡單的例子說明了 Service 的工作原理。

Kubernetes 網絡模型來龍去脈

從零開始入門 K8s | Kubernetes 網絡模型進階Kubernetes 網絡模型來龍去脈Pod 究竟如何上網Service 究竟如何工作啥?負載均衡還分内部外部本文總結

容器網絡發端于 Docker 的網絡。Docker 使用了一個比較簡單的網絡模型,即内部的網橋加内部的保留 IP。這種設計的好處在于容器的網絡和外部世界是解耦的,無需占用主控端的 IP 或者主控端的資源,完全是虛拟的。它的設計初衷是:當需要通路外部世界時,會采用 SNAT 這種方法來借用 Node 的 IP 去通路外面的服務。比如容器需要對外提供服務的時候,所用的是 DNAT 技術,也就是在 Node 上開一個端口,然後通過 iptable 或者别的某些機制,把流導入到容器的程序上以達到目的。

該模型的問題在于,外部網絡無法區分哪些是容器的網絡與流量、哪些是主控端的網絡與流量。比如,如果要做一個高可用的時候,172.16.1.1 和 172.16.1.2 是擁有同樣功能的兩個容器,此時我們需要将兩者綁成一個 Group 對外提供服務,而這個時候我們發現從外部看來兩者沒有相同之處,它們的 IP 都是借用主控端的端口,是以很難将兩者歸攏到一起。

從零開始入門 K8s | Kubernetes 網絡模型進階Kubernetes 網絡模型來龍去脈Pod 究竟如何上網Service 究竟如何工作啥?負載均衡還分内部外部本文總結

在此基礎上,Kubernetes 提出了這樣一種機制:即每一個 Pod,也就是一個功能聚集小團夥應有自己的“身份證”,或者說 ID。在 TCP 協定棧上,這個 ID 就是 IP。

這個 IP 是真正屬于該 Pod 的,外部世界不管通過什麼方法一定要給它。對這個 Pod IP 的通路就是真正對它的服務的通路,中間拒絕任何的變造。比如以 10.1.1.1 的 IP 去通路 10.1.2.1 的 Pod,結果到了 10.1.2.1 上發現,它實際上借用的是主控端的 IP,而不是源 IP,這樣是不被允許的。Pod 内部會要求共享這個 IP,進而解決了一些功能内聚的容器如何變成一個部署的原子的問題。

剩下的問題是我們的部署手段。Kubernetes 對怎麼實作這個模型其實是沒有什麼限制的,用 underlay 網絡來控制外部路由器進行導流是可以的;如果希望解耦,用 overlay 網絡在底層網絡之上再加一層疊加網,這樣也是可以的。總之,隻要達到模型所要求的目的即可。

Pod 究竟如何上網

容器網絡的網絡包究竟是怎麼傳送的?

從零開始入門 K8s | Kubernetes 網絡模型進階Kubernetes 網絡模型來龍去脈Pod 究竟如何上網Service 究竟如何工作啥?負載均衡還分内部外部本文總結

我們可以從以下兩個次元來看:

  • 協定層次
  • 網絡拓撲

第一個次元是協定層次。

它和 TCP 協定棧的概念是相同的,需要從兩層、三層、四層一層層地摞上去,發包的時候從右往左,即先有應用資料,然後發到了 TCP 或者 UDP 的四層協定,繼續向下傳送,加上 IP 頭,再加上 MAC 頭就可以送出去了。收包的時候則按照相反的順序,首先剝離 MAC 的頭,再剝離 IP 的頭,最後通過協定号在端口找到需要接收的程序。

第二次元是網絡拓撲。

一個容器的包所要解決的問題分為兩步:第一步,如何從容器的空間 (c1) 跳到主控端的空間 (infra);第二步,如何從主控端空間到達遠端。

我個人的了解是,容器網絡的方案可以通過接入、流控、通道這三個層面來考慮。

  • 第一個是接入,就是說我們的容器和主控端之間是使用哪一種機制做連接配接,比如 Veth + bridge、Veth + pair 這樣的經典方式,也有利用高版本核心的新機制等其他方式(如 mac/IPvlan 等),來把包送入到主控端空間;
  • 第二個是流控,就是說我的這個方案要不要支援 Network Policy,如果支援的話又要用何種方式去實作。這裡需要注意的是,我們的實作方式一定需要在資料路徑必經的一個關節點上。如果資料路徑不通過該 Hook 點,那就不會起作用;
  • 第三個是通道,即兩個主機之間通過什麼方式完成包的傳輸。我們有很多種方式,比如以路由的方式,具體又可分為 BGP 路由或者直接路由。還有各種各樣的隧道技術等等。最終我們實作的目的就是一個容器内的包通過容器,經過接入層傳到主控端,再穿越主控端的流控子產品(如果有)到達通道送到對端。

一個最簡單的路由方案:Flannel-host-gw

這個方案采用的是每個 Node 獨占網段,每個 Subnet 會綁定在一個 Node 上,網關也設定在本地,或者說直接設在 cni0 這個網橋的内部端口上。該方案的好處是管理簡單,壞處就是無法跨 Node 遷移 Pod。就是說這個 IP、網段已經是屬于這個 Node 之後就無法遷移到别的 Node 上。

從零開始入門 K8s | Kubernetes 網絡模型進階Kubernetes 網絡模型來龍去脈Pod 究竟如何上網Service 究竟如何工作啥?負載均衡還分内部外部本文總結

這個方案的精髓在于 route 表的設定,如上圖所示。接下來為大家一一解讀一下。

  • 第一條很簡單,我們在設定網卡的時候都會加上這一行。就是指定我的預設路由是通過哪個 IP 走掉,預設裝置又是什麼;
  • 第二條是對 Subnet 的一個規則回報。就是說我的這個網段是 10.244.0.0,掩碼是 24 位,它的網關位址就在網橋上,也就是 10.244.0.1。這就是說這個網段的每一個包都發到這個網橋的 IP 上;
  • 第三條是對對端的一個回報。如果你的網段是 10.244.1.0(上圖右邊的 Subnet),我們就把它的 Host 的網卡上的 IP (10.168.0.3) 作為網關。也就是說,如果資料包是往 10.244.1.0 這個網段發的,就請以 10.168.0.3 作為網關。

再來看一下這個資料包到底是如何跑起來的?

假設容器 (10.244.0.2) 想要發一個包給 10.244.1.3,那麼它在本地産生了 TCP 或者 UDP 包之後,再依次填好對端 IP 位址、本地以太網的 MAC 位址作為源 MAC 以及對端 MAC。一般來說本地會設定一條預設路由,預設路由會把 cni0 上的 IP 作為它的預設網關,對端的 MAC 就是這個網關的 MAC 位址。然後這個包就可以發到橋上去了。如果網段在本橋上,那麼通過 MAC 層的交換即可解決。

這個例子中我們的 IP 并不屬于本網段,是以網橋會将其上送到主機的協定棧去處理。主機協定棧恰好找到了對端的 MAC 位址。使用 10.168.0.3 作為它的網關,通過本地 ARP 探查後,我們得到了 10.168.0.3 的 MAC 位址。即通過協定棧層層組裝,我們達到了目的,将 Dst-MAC 填為右圖主機網卡的 MAC 位址,進而将包從主機的 eth0 發到對端的 eth0 上去。

是以大家可以發現,這裡有一個隐含的限制,上圖中的 MAC 位址填好之後一定是能到達對端的,但如果這兩個主控端之間不是二層連接配接的,中間經過了一些網關、一些複雜的路由,那麼這個 MAC 就不能直達,這種方案就是不能用的。當包到達了對端的 MAC 位址之後,發現這個包确實是給它的,但是 IP 又不是它自己的,就開始 Forward 流程,包上送到協定棧,之後再走一遍路由,剛好會發現 10.244.1.0/24 需要發到 10.244.1.1 這個網關上,進而到達了 cni0 網橋,它會找到 10.244.1.3 對應的 MAC 位址,再通過橋接機制,這個包就到達了對端容器。

大家可以看到,整個過程總是二層、三層,發的時候又變成二層,再做路由,就是一個大環套小環。這是一個比較簡單的方案,如果中間要走隧道,則可能會有一條 vxlan tunnel 的裝置,此時就不填直接的路由,而填成對端的隧道号。

Service 究竟如何工作

Service 其實是一種負載均衡 (Load Balance) 的機制。

我們認為它是一種使用者側(Client Side) 的負載均衡,也就是說 VIP 到 RIP 的轉換在使用者側就已經完成了,并不需要集中式地到達某一個 NGINX 或者是一個 ELB 這樣的元件來進行決策。

從零開始入門 K8s | Kubernetes 網絡模型進階Kubernetes 網絡模型來龍去脈Pod 究竟如何上網Service 究竟如何工作啥?負載均衡還分内部外部本文總結

它的實作是這樣的:首先是由一群 Pod 組成一組功能後端,再在前端上定義一個虛 IP 作為通路入口。一般來說,由于 IP 不太好記,我們還會附贈一個 DNS 的域名,Client 先通路域名得到虛 IP 之後再轉成實 IP。Kube-proxy 則是整個機制的實作核心,它隐藏了大量的複雜性。它的工作機制是通過 apiserver 監控 Pod/Service 的變化(比如是不是新增了 Service、Pod)并将其回報到本地的規則或者是使用者态程序。

一個 LVS 版的 Service

我們來實際做一個 LVS 版的 Service。LVS 是一個專門用于負載均衡的核心機制。它工作在第四層,性能會比用 iptable 實作好一些。

假設我們是一個 Kube-proxy,拿到了一個 Service 的配置,如下圖所示:它有一個 Cluster IP,在該 IP 上的端口是 9376,需要回報到容器上的是 80 端口,還有三個可工作的 Pod,它們的 IP 分别是 10.1.2.3, 10.1.14.5, 10.1.3.8。

從零開始入門 K8s | Kubernetes 網絡模型進階Kubernetes 網絡模型來龍去脈Pod 究竟如何上網Service 究竟如何工作啥?負載均衡還分内部外部本文總結

它要做的事情就是:

從零開始入門 K8s | Kubernetes 網絡模型進階Kubernetes 網絡模型來龍去脈Pod 究竟如何上網Service 究竟如何工作啥?負載均衡還分内部外部本文總結
  • 第 1 步,綁定 VIP 到本地(欺騙核心);

首先需要讓核心相信它擁有這樣的一個虛 IP,這是 LVS 的工作機制所決定的,因為它工作在第四層,并不關心 IP 轉發,隻有它認為這個 IP 是自己的才會拆到 TCP 或 UDP 這一層。在第一步中,我們将該 IP 設到核心中,告訴核心它确實有這麼一個 IP。實作的方法有很多,我們這裡用的是 ip route 直接加 local 的方式,用 Dummy 啞裝置上加 IP 的方式也是可以的。

  • 第 2 步,為這個虛 IP 建立一個 IPVS 的 virtual server;

告訴它我需要為這個 IP 進行負載均衡分發,後面的參數就是一些分發政策等等。virtual server 的 IP 其實就是我們的 Cluster IP。

  • 第 3 步,為這個 IPVS service 建立相應的 real server。

我們需要為 virtual server 配置相應的 real server,就是真正提供服務的後端是什麼。比如說我們剛才看到有三個 Pod,于是就把這三個的 IP 配到 virtual server 上,完全一一對應過來就可以了。Kube-proxy 工作跟這個也是類似的。隻是它還需要去監控一些 Pod 的變化,比如 Pod 的數量變成 5 個了,那麼規則就應變成 5 條。如果這裡面某一個 Pod 死掉了或者被殺死了,那麼就要相應地減掉一條。又或者整個 Service 被撤銷了,那麼這些規則就要全部删掉。是以它其實做的是一些管理層面的工作。

啥?負載均衡還分内部外部

最後我們介紹一下 Service 的類型,可以分為以下 4 類。

ClusterIP

叢集内部的一個虛拟 IP,這個 IP 會綁定到一堆服務的 Group Pod 上面,這也是預設的服務方式。它的缺點是這種方式隻能在 Node 内部也就是叢集内部使用。

NodePort

供叢集外部調用。将 Service 承載在 Node 的靜态端口上,端口号和 Service 一一對應,那麼叢集外的使用者就可以通過 : 的方式調用到 Service。

LoadBalancer

給雲廠商的擴充接口。像阿裡雲、亞馬遜這樣的雲廠商都是有成熟的 LB 機制的,這些機制可能是由一個很大的叢集實作的,為了不浪費這種能力,雲廠商可通過這個接口進行擴充。它首先會自動建立 NodePort 和 ClusterIP 這兩種機制,雲廠商可以選擇直接将 LB 挂到這兩種機制上,或者兩種都不用,直接把 Pod 的 RIP 挂到雲廠商的 ELB 的後端也是可以的。

ExternalName

擯棄内部機制,依賴外部設施,比如某個使用者特别強,他覺得我們提供的都沒什麼用,就是要自己實作,此時一個 Service 會和一個域名一一對應起來,整個負載均衡的工作都是外部實作的。

下圖是一個執行個體。它靈活地應用了 ClusterIP、NodePort 等多種服務方式,又結合了雲廠商的 ELB,變成了一個很靈活、極度伸縮、生産上真正可用的一套系統。

從零開始入門 K8s | Kubernetes 網絡模型進階Kubernetes 網絡模型來龍去脈Pod 究竟如何上網Service 究竟如何工作啥?負載均衡還分内部外部本文總結

首先我們用 ClusterIP 來做功能 Pod 的服務入口。大家可以看到,如果有三種 Pod 的話,就有三個 Service Cluster IP 作為它們的服務入口。這些方式都是 Client 端的,如何在 Server 端做一些控制呢?

首先會起一些 Ingress 的 Pod(Ingress 是 K8s 後來新增的一種服務,本質上還是一堆同質的 Pod),然後将這些 Pod 組織起來,暴露到一個 NodePort 的 IP,K8s 的工作到此就結束了。

任何一個使用者通路 23456 端口的 Pod 就會通路到 Ingress 的服務,它的後面有一個 Controller,會把 Service IP 和 Ingress 的後端進行管理,最後會調到 ClusterIP,再調到我們的功能 Pod。前面提到我們去對接雲廠商的 ELB,我們可以讓 ELB 去監聽所有叢集節點上的 23456 端口,隻要在 23456 端口上有服務的,就認為有一個 Ingress 的執行個體在跑。

整個的流量經過外部域名的一個解析跟分流到達了雲廠商的 ELB,ELB 經過負載均衡并通過 NodePort 的方式到達 Ingress,Ingress 再通過 ClusterIP 調用到背景真正的 Pod。這種系統看起來比較豐富,健壯性也比較好。任何一個環節都不存在單點的問題,任何一個環節也都有管理與回報。

本文總結

本文的主要内容就到此為止了,這裡為大家簡單總結一下:

  • 大家要從根本上了解 Kubernetes 網絡模型的演化來曆,了解 PerPodPerIP 的用心在哪裡;
  • 網絡的事情萬變不離其宗,按照模型從 4 層向下就是發包過程,反正層層剝離就是收包過程,容器網絡也是如此;
  • Ingress 等機制是在更高的層次上(服務<->端口)友善大家部署叢集對外服務,通過一個真正可用的部署執行個體,希望大家把 Ingress+Cluster IP + PodIP 等概念聯合來看,了解社群出台新機制、新資源對象的思考。
從零開始入門 K8s | Kubernetes 網絡模型進階Kubernetes 網絡模型來龍去脈Pod 究竟如何上網Service 究竟如何工作啥?負載均衡還分内部外部本文總結
阿裡巴巴雲原生 關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”

繼續閱讀