天天看點

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

導讀:前文 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 通往外面的世界。

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

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

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

同 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 連接配接起來了,如圖:

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

有了

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 裝置上。如圖:

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

跨 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 的通訊如圖:

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

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,再發送至

flannel0

docker0

最後到達目标容器 IP 位址上。下圖示意流程:

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

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

Flannel 新版本 backend 不建議采用 UDP 封裝方式,因為 traffic 存在 3 次使用者空間與核心空間的資料拷貝,(如下圖)性能上存在比較大的損耗。新版本推薦用 VxLan 和雲服務商版本的

backends

進行優化。

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

L3 networking: Calico

Calico

是 L3 Routing 上非常流行容器網絡架構方案。主要元件是

Felix

,BIRD 和 BGP Route Reflector。Felix 和 BIRD 均是運作在 Node 上的 deamon 程式。架構簡要:

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

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 路由資訊在彙聚層上再進行分發(propagation)。在叢集網站中 Reflector 元件可以部署多個,完全能于部署規模大小來決定。Reflector 元件僅僅執行路由信令和條目的分發,其中不涉及任何資料面流量。路由彙聚層分發:

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

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 安裝到所有節點上,實作了

ADD

DEL

CHECK

三個接口供 kubelet 調用。這裡以一個 Pod 在建立過程中的網絡 setup 步驟來說明:

  • 當一個 Pod 被排程到節點上時,kubelet 監聽到 Pod 建立在自己節點上,通過 runtime(docker...)建立 sandbox 容器來打通所需 namespace。
  • kubelet 調用插件 binary 的

    cmdAdd

    接口,插件程式對接口參數設定檢查後,向 backendServer 發起

    AllocIP

    調用。
  • backendServer 程式的

    networkService

    根據 Pod 的網絡類型進行相應的 Pod IP 申請,支援三種網絡類型

    ENIMultiIP

    VPCENI

    VPCIP

    • 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 位址)結果後,插件調用不同網絡類型下的

    NetnsDriver

    `Setup`接口實作來完成從容器網卡通往主機網卡的鍊路設定,其中:
    • ENIMultiIP

      VPCIP

      均是采用

      vethDriver

      的鍊路模式,步驟包括:
      • Create veth pair
      • Add IP addr for container interface
      • Add routes
      • Host side namespace config
      • Add host routes and rules
    • VPCENI

      稍有不同是為每個 Pod 在 VPC 平面上綁定一個 eni,其中包括兩次

      NetnsDriver

      接口調用:
      • vethDriver

      • rawNicDriver

        (主要實作 VPC 平面網絡路由設定,包括預設路由和網關等配置)

綜上圖示:

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

為什麼需要支援上述三種網絡類型?根本上是由阿裡雲 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 網絡:

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

VPC IP 網絡:

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

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 網卡資源會釋放掉。

另外,有一個

startCheckIdleTicker

goroutine 會定期掃描位址池的

MaxPoolSize

MinPoolSize

水位,在低于和高出水位閥值時會對位址池 eniIP 資源進行進行建立和釋放,使得位址池 IP 資源處于一個可控水位範圍中。為了保證資源狀态一緻性,有一個

startGarbageCollectionLoop

goroutine會定期掃描 IP 位址是否在用或過期狀态,如檢測到會進行資源 GC 操作。最後,Pod 資源狀态資料都持久化在本地的一個

boltDB

檔案中

/var/lib/cni/terway/pod.db

,即使 Pod 已經在 apiServer 中删除,

GetPod

會從本地

boltDB

中讀取副本資料。在 Pod 已經删除但副本還存在 DB 的情況下,GC goroutine檢 測到會執行清理。截圖簡述:

Kubernetes 容器網絡模型和典型實作Pod 之間 Container-to-Container networking同 Node 中 Pod-to-Pod networking跨 Node 之間 Pod-to-Pod networkingPod-to-Service networking使用 DNSCNI

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