雲計算

Kubernetes網絡模型和典型實現總結

前文Kubernetes中的ClusterIP、NodePort、LoadBalancer、Ingress服務訪問方式比較中總結了服務接入訪問的主要方式,以及它們之間隱含關係。有了這些概念基礎後,K8S應用開發和服務部署就容易很多了,但Under the hood服務訪問究竟是如何實現的呢?這篇內容就Kubernetes的網絡模型和典型的容器網絡實現,特別是阿里雲自己的容器網絡插件(Terway)的實現做了一個較詳細的總結。

Pod之間Container-to-Container networking

Linux networking namespace為進程通訊提供了一個邏輯網絡棧,包括network devices、routes、firewall rules。Network namespace(NS)管理實際是為其中的所有進程提供了一個獨立的邏輯網絡Stack。

缺省情況下,Linux將每個進程掛載在Root NS下,這些進程通過eth0通往外面的世界。
image.png

在Pod世界裡所有其中的容器共享一個NS,這些容器都有相同的IP和Port空間,通過localhost訪問也是互通的。Shared storage也是可以訪問的,通過SharedVolume掛載到容器中。如下一個NS per pod圖例:
image.png

同Node中Pod-to-Pod networking

先看同一個Node下Pod之間的networking如何實現?答案是通過Virtual Ethernet Device (or veth pair)的兩塊Virtual interfaces,每塊veth掛載在一個NS上,來實現跨NS的連接。比如,一塊掛在Root NS(host)上,另一塊掛在Pod NS上,好比一根網線把兩個在不同網絡空間的traffic連接起來了,如圖:
image.png

有了veth pair這條網線,Pods網絡可以連通到Root NS了,但在Root NS上如何實現對來自不同Pod的packet通訊呢?答案是通過Linux Ethernet Bridge,一個虛擬的Layer2網絡設備來實現不同network segments之間的Ethernet packet switching。不得不提這個old-school協議:ARP,實現了MAC地址到IP地址的發現協議。Bridge廣播ethframe到所有連接的設備(除發送者外),收到ARP回覆後將packet forward到對應veth設備上。如圖:
image.png

跨Node之間Pod-to-Pod networking

進入這部分之前,先提及K8S在其(Pod)networking設計上的3個fundamental requirements,任何networking部分的實現都必須遵循這三個需求。

  • 在不使用NAT下,所有Pods都能和其它任何Pods通訊
  • 在不使用NAT下,所有Nodes都能和所有Pods通訊
  • Pod所看到自己的IP和其它Pods看到它的IP一定是相同的

簡要來看,K8S網絡模型要求Pod IP在整個網絡中都能通達。具體實現方案有三方面:

  • Layer2(Switching)Solution
  • Layer3(Routing)Solution,如,Calico, Terway
  • Overlay Solution,如Flannel

這部分下文介紹,目前且認為Pod IP的網絡通達性是確保的。

在Pod獲得IP之前,kubelet為每個Node分配一個CIDR地址段(Classless inter-domain routing),每個Pod在其中獲取唯一IP,CIDR 地址塊的大小對應於每個Node的最大 Pod 數量(默認110個)。在Pod IP和跨Node網絡層部署成功後,從源Pod1到目的Pod4的通訊如圖:
image.png

Pod-to-Service networking

K8S Service管理服務的Pods狀態,在Pod有變化下管理對應IP的變化,並管理對外提供服務的Virtual IP到Pod IPs路由訪問,實現外部對服務Virtual IP的訪問路由到Pod IP,以此屏蔽外部對服務後端的實現形態。所以在服務創建時,會對應生成一個Virtual IP(也即是Cluster IP),任何對該Virtual IP的訪問將打散路由到服務所屬的Pods上。

K8S的服務是如何實現對Virtual IP的訪問負載均衡呢?答案是netfilter和iptables。netfilters是Linux built-in networking framework,為Linux提供網絡包過濾、NAT和Port translation等豐富的自定義handler實現。iptables是運行在Linux user-space的規則管理系統,為netfilter框架提供豐富的包轉發規則管理。

在K8S實現中kube-proxy(node deamon)通過watch apiserver來獲得服務配置的變化,比如,服務的Virtual IP變化、Pod IP變化(ie, pod up/down)。iptables規則隨之變化並將請求路由到服務對應的Pod上,Pod IP選取是隨機的,這樣看iptables起到了Pod負載均衡作用。在訪問請求Return path上,iptables會做一次SNAT以替換IP header的Pod IP為服務Virtual IP,這樣使得Client看起來請求僅在服務Virtual IP上進行通訊。

從K8S v1.11中IPVS(IP Virtual Server)被引入成為第二種集群內負載均衡方式。IPVS同樣也是構建基於netfilter之上,在創建服務定義時可指定使用iptables或IPVS。IPVS是特定適合於服務負載均衡的解決方案,提供了非常豐富的均衡算法應用場景。

使用DNS

每個服務會設置一個DNS域名,kubelets為每個容器進行配置--cluster-dns=<dns-service-ip>,用以解析服務所對應DNS域名到對應的Cluster IP或Pod IP。1.12後CoreDNS成為缺省DNS方式。服務支持3種類型DNS records(A record、CNAME、SRV records)。其中常用的是A Records,比如,在cluster.local的DNS name下,A record格式如pod-ip-address.my-namespace.pod.cluster.local,其中Pod hostname和subdomain字段可以設置為標準的FQDN格式,比如,custom-host.custom-subdomain.my-namespace.svc.cluster.local

CNI

網絡模型的實現上K8S在Pod資源管控(kubelet)和一套標準Container Networking Interface(CNI)定義和實現共同完成的。CNI在其中充當了"膠水"作用:各種容器網絡實現能在一致的操作接口下由kubelet統一管控調度。另外,多個容器網絡也能共存於一個集群內,為不同Pod的網絡需求提供服務,都是在kubelet的統一管控下完成。

Overlay networking: Flannel

Flannel是CoreOS為K8S networking開發的解決方案,也是阿里雲ACK產品支持的容器網絡解決方案。Flannel的設計原理很簡潔,在host網絡之上創建另一個扁平網絡(所謂的overlay),在其上地址空間中給每個pod容器設置一個IP地址,並用此實現路由和通訊。

主機內容器網絡在docker bridge docker0上完成通訊,不再贅述。主機間通訊使用內核路由表和IP-over-UDP封裝進行實現。容器IP包流經docker bridge會轉發到flannel0網卡(TUN)設備上,進而流入到flanneld進程中。flanneld會對packet目標IP地址所屬的網段信息查詢其對應的下一跳主機IP,容器子網CIDR和所屬主機IP的映射(key-value)保存在etcd中,flanneld查詢得到packet目標IP所屬的主機IP地址後,會將IP packet封裝到一個UDP payload中並設置UDP packet目標地址為所得到的目標主機IP,最後在host網絡中發送出UDP packet。到達目標主機後,UDP packet會流經flanneld並在這裡解封出IP packet,再發送至flannel0docker0最後到達目標容器IP地址上。下圖示意流程:

image.png

值得一提是,容器CIDR和下一跳主機IP的映射條目容量沒有特殊限制。在阿里雲ACK產品上該條目容量需要在VPC/vSwitch控制面中進行分發,考慮到整體性能因素,在數量上做了一定數量限制(缺省48個)。但在自建主機網絡部署中,該數量限制就不會明顯了,因為主機下一跳主機網絡在一個大二層平面上。

Flannel新版本backend不建議採用UDP封裝方式,因為traffic存在3次用戶空間與內核空間的數據拷貝,(如下圖)性能上存在比較大的損耗。新版本推薦用VxLan和雲服務商版本的backends進行優化。

image.png

L3 networking: Calico

Calico是L3 Routing上非常流行容器網絡架構方案。主要組件是Felix,BIRD和BGP Route Reflector。Felix和BIRD均是運行在Node上的deamon程序。

Felix完成網卡的管理和配置,包括Routes programming和ACLs。實現路由信息對Linux kernel FIB的操作和ACLs的管理操作。由於Felix功能完整性和運行獨立性非常好,其功能作為Off-the-shelf被集成到阿里雲Terway網絡插件中,實現其網絡策略功能。

BIRD(BGP client)完成內核路由FIB條目向集群網絡側分發,使其路由條目對所有網絡節點中可見,並實現BGP路由協議功能。每一個BGP client會連接到網絡中其它BGP client,這對規模較大的部署會是明顯的瓶頸(due to the N^2 increase nature)。鑑於該限制引入了BGP Route Reflector組件,實現BGP clients路由分發的中心化匯聚。在集群網站中Reflector組件可以部署多個,完全能於部署規模大小來決定。Reflector組件僅僅執行路由信令和條目的分發,其中不涉及任何數據面流量。如下簡要的Calico架構:

image.png

L3 networking:Terway

Terway是阿里雲自研CNI插件,提供了阿里雲VPC互通和方便對接阿里雲產品的基礎設施,沒有overlay網絡帶來的性能損耗,同時提供了簡單易用的backend功能。

Terway功能上可分為三部分:1. CNI插件,一個獨立的binary運行程序;2. Backend Server(也稱為daemon),程序以獨立daemonSet方式運行在每個Node上;3. Network Policy,完全集成了Calico Felix實現。

CNI插件binary是通過daemonSet部署中initContainer安裝到所有節點上,實現了ADDDELCHECK三個接口供kubelet調用。這裡以一個Pod在創建過程中的網絡setup步驟來說明:

  • 當一個Pod被調度到節點上時,kubelet監聽到Pod創建在自己節點上,通過runtime(docker...)創建sandbox容器來打通所需namespace。
  • kubelet調用插件binary的cmdAdd接口,插件程序對接口參數設置檢查後,向backendServer發起AllocIP調用。
  • backendServer程序的networkService根據Pod的網絡類型進行相應的Pod IP申請,支持三種網絡類型ENIMultiIPVPCENIVPCIP

    • ENIMultiIP是eni網卡帶多IP類型,由networkService中的ResourceManager在自己的IP地址池中進行IP地址分配
    • VPCENI是為Pod創建和掛載一個eni,由networkService中的allocateENI向阿里雲Openapi發起對所屬ecs實例的eni創建、掛載,並獲得對應eni IP地址
    • VPCIP是為Pod在VPC網絡平面上分配一個IP地址,這是在插件程序中通過調用ipam接口從vpc管控面獲取的IP地址
  • 在backendServer返回AllocIP調用(IP地址)結果後,插件調用不同網絡類型的driver完成net namespace link setup。

綜上圖示:
image.png

為什麼需要支持上述三種網絡類型?根本上是由阿里雲vpc網絡基礎設施所決定,同時覆蓋阿里雲主流應用對vpc網絡資源的使用場景需求。另一方面是對標Amazon AWS的容器網絡解決方案,在基於VPC和ENI的網絡設施上能支持同等功能。

ENI多IP、VPC ENI和VPC IP的主要區別在於前兩者下的Pod網段和VPC網段是相同的,而VPC IP的網段和節點的宿主機網段不同。這樣使得在ENI網絡環境下的IP路由完全在VPC的L2網絡平面上進行,而VPC IP網絡需要在VPC路由表中進行配置Pod網段的下一跳主機,和Flannel的路由模式類似。可以看出,ENI網絡能帶來更靈活的路由選擇和更好的路由性能。如下兩個截圖反映其不同路由特點:

VPC ENI網絡:
image.png

VPC IP網絡:
image.png

ENI多IP(1個主IP/多個輔助IP)網絡下有2種路由模式:veth策略路由ipvlan。兩者本質區別在於使用不同的路由模式,前者使用veth pair的策略路由,後者使用ipvlan網絡路由。策略路由需要在節點上配置策略路由條目來保證輔助IP的流量經過它所屬的彈性網卡。ipvlan實現了一個網卡虛擬出多個子網卡和不同的IP地址,eni將其輔助IP綁定到這些虛擬出來的子網卡上形成一個與vpc平面打通的L3網絡。這種模式使ENI多IP的網絡結構比較簡單,性能相對veth策略路由網絡也更好。兩種網絡模式切換通過配置即可完成(缺省是vethpair):

值得一提的是Terway還實現了ENI多IP地址資源池的管理和分配機制。networkService中的eniIPFactory為每個eni網卡創建一個goroutine,該eni網卡上的eniIP的分配釋放都在這個goroutine中完成。在創建一個eniIP時掃描已經存在的eni網卡,如該eni還存在空閒的eniIP,該goroutine會通過ipResultChan返回給eniIPFactory一個分配的IP。如果所有的eni網卡的eniIP都分配完畢,會先創建一個新的eni網卡和對應的goroutine,首次創建eni網卡時無需做IP分配,直接返回eni網卡主IP即可。eniIP釋放是逆向的,在eni網卡的最後一個eniIP釋放時,整個eni網卡資源會釋放掉。

另外,有一個startCheckIdleTickergoroutine會定期掃描地址池的MaxPoolSizeMinPoolSize水位,在低於和高出水位閥值時會對地址池eniIP資源進行進行創建和釋放,使得地址池IP資源處於一個可控水位範圍中。為了保證資源狀態一致性,有一個startGarbageCollectionLoopgoroutine會定期掃描IP地址是否在用或過期狀態,如檢測到會進行資源GC操作。最後,Pod資源狀態數據都持久化在本地的一個boltDB文件中/var/lib/cni/terway/pod.db,即使Pod已經在apiServer中刪除,GetPod會從本地boltDB中讀取副本數據。在Pod已經刪除但副本還存在DB的情況下,GC goroutine檢測到會執行清理。截圖簡述:

image.png

總結下,從這些backend功能可以看到Terway為阿里雲vpc網絡資源設施連通性做了很好的封裝,讓基於阿里雲Kubernetes的應用開發和部署帶來更加簡便和高效的優點。

Leave a Reply

Your email address will not be published. Required fields are marked *