Kubernetes 定義了一種簡單、一致的網絡模型,基于扁平網絡結構的設計,無需將主機端口與網絡端口進行映射便可以進行高效地通訊,也無需其他組件進行轉發。該模型也使應用程序很容易從虛擬機或者主機物理機遷移到 Kubernetes 管理的 pod 中。
(資料圖)
這篇文章主要深入探索Kubernetes網絡模型,并了解容器、pod間如何進行通訊。對于網絡模型的實現將會在后面的文章介紹。
Kubernetes 網絡模型該模型定義了:
每個 pod 都有自己的 IP 地址,這個 IP 在集群范圍內可達Pod 中的所有容器共享 pod IP 地址(包括 MAC 地址),并且容器之前可以相互通信(使用localhost)Pod 可以使用 pod IP 地址與集群中任一節點上的其他 pod 通信,無需 NATKubernetes 的組件之間可以相互通信,也可以與 pod 通信網絡隔離可以通過網絡策略實現上面的定義中提到了幾個相關的組件:
Pod:Kubernetes 中的 pod 有點類似虛擬機有唯一的 IP 地址,同一個節點上的 pod 共享網絡和存儲。Container:pod 是一組容器的集合,這些容器共享同一個網絡命名空間。pod 內的容器就像虛擬機上的進程,進程之間可以使用localhost進行通信;容器有自己獨立的文件系統、CPU、內存和進程空間。需要通過創建 Pod 來創建容器。Node:pod 運行在節點上,集群中包含一個或多個節點。每個 pod 的網絡命名空間都會連接到節點的命名空間上,以打通網絡。講了這么多次網絡命名空間,那它到底是如何運作的呢?
網絡命名空間如何工作在 Kubernetes 的發行版k3s創建一個 pod,這個 pod 有兩個容器:發送請求的curl?容器和提供 web 服務的httpbin容器。
雖然使用發行版,但是其仍然使用 Kubernetes 網絡模型,并不妨礙我們了解網絡模型。
apiVersion: v1kind: Podmetadata: name: multi-container-podspec: containers: - image: curlimages/curl name: curl command: ["sleep", "365d"] - image: kennethreitz/httpbin name: httpbin
登錄到節點上,通過lsns -t net?當前主機上的網絡命名空間,但是并沒有找到httpbin?的進程。有個命名空間的命令是/pause?,這個pause進程實際上是每個 pod 中不可見的sandbox
lsns -t net NS TYPE NPROCS PID USER NETNSID NSFS COMMAND4026531992 net 126 1 root unassigned /lib/systemd/systemd --system --deserialize 314026532247 net 1 83224 uuidd unassigned /usr/sbin/uuidd --socket-activation4026532317 net 4 129820 65535 0 /run/netns/cni-607c5530-b6d8-ba57-420e-a467d7b10c56 /pause
既然每個容器都有獨立的進程空間,我們換下命令查看進程類型的空間:
lsns -t pid NS TYPE NPROCS PID USER COMMAND4026531836 pid 127 1 root /lib/systemd/systemd --system --deserialize 314026532387 pid 1 129820 65535 /pause4026532389 pid 1 129855 systemd-network sleep 365d4026532391 pid 2 129889 root /usr/bin/python3 /usr/local/bin/gunicorn -b 0.0.0.0:80 httpbin:app -k gevent
通過進程 PID129889可以找到其所屬的命名空間:
ip netns identify 129889cni-607c5530-b6d8-ba57-420e-a467d7b10c56
然后可以在該命名空間下使用exec執行命令:
ip netns exec cni-607c5530-b6d8-ba57-420e-a467d7b10c56 ip a1: lo:mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever2: eth0@if17: mtu 1450 qdisc noqueue state UP group default link/ether f2:c8:17:b6:5f:e5 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.42.1.14/24 brd 10.42.1.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::f0c8:17ff:feb6:5fe5/64 scope link valid_lft forever preferred_lft forever
從結果來看 pod 的 IP 地址10.42.1.14?綁定在接口eth0?上,而eth0?被連接到17號接口上。
在節點主機上,查看17?號接口信息。veth7912056b?是主機根命名空間下的虛擬以太接口(vitual ethernet device),是連接 pod 網絡和節點網絡的隧道,對端是 pod 命名空間下的接口eth0。
ip link | grep -A1 ^1717: veth7912056b@if2:mtu 1450 qdisc noqueue master cni0 state UP mode DEFAULT group default link/ether d6:5e:54:7f:df:af brd ff:ff:ff:ff:ff:ff link-netns cni-607c5530-b6d8-ba57-420e-a467d7b10c56
上面的結果看到,該veth?連到了個網橋(network bridge)cni0上。
網橋工作在數據鏈路層(OSI 模型的第 2 層),連接多個網絡(可多個網段)。當請求到達網橋,網橋會詢問所有連接的接口(這里 pod 通過 veth 以網橋連接)是否擁有原始請求中的 IP 地址。如果有接口響應,網橋會將匹配信息(IP -> veth)記錄,并將數據轉發過去。
那如果沒有接口響應怎么辦?具體流程就要看各個網絡插件的實現了。我準備在后面的文章中介紹常用的網絡插件,比如 Calico、Flannel、Cilium 等。
接下來看下 Kubernetes 中的網絡通信如何完成,一共有幾種類型:
同 pod 內容器間通信同節點上的 pod 間通信不同節點上的 pod 間通信Kubernetes 網絡如何工作同 pod 內的容器間通信
同 pod 內的容器間通信最簡單,這些容器共享網絡命名空間,每個命名空間下都有lo?回環接口,可以通過localhost來完成通信。
同節點上的 pod 間通信
當我們將curl?容器和httpbin?分別在兩個 pod 中運行,這兩個 pod 有可能調度到同一個節點上。curl?發出的請求根據容器內的路由表到達了 pod 內的eth0?接口。然后通過與eth0?相連的隧道veth1到達節點的根網絡空間。
veth1?通過網橋cni0?與其他 pod 相連虛擬以太接口vethX?相連,網橋會詢問所有相連的接口是否擁有原始請求中的 IP 地址(比如這里的10.42.1.9?)。收到響應后,網橋會記錄映射信息(10.42.1.9?=>veth0?),同時將數據轉發過去。最終數據經過veth0?隧道進入 podhttpbin中。
不同節點的 pod 間通信
跨節點的 pod 間通信會復雜一些,且不同網絡插件的處理方式不同,這里選擇一種容易理解的方式來簡單說明下。
前半部分的流程與同節點 pod 間通信類似,當請求到達網橋,網橋詢問哪個 pod 擁有該 IP 但是沒有得到回應。流程進入主機的路由尋址過程,到更高的集群層面。
在集群層面有一張路由表,里面存儲著每個節點的 Pod IP 網段(節點加入到集群時會分配一個 Pod 網段(Pod CIDR),比如在 k3s 中默認的 Pod CIDR 是10.42.0.0/16?,節點獲取到的網段是10.42.0.0/24、10.42.1.0/24、10.42.2.0/24,依次類推)。通過節點的 Pod IP 網段可以判斷出請求 IP 的節點,然后請求被發送到該節點。
總結現在應該對 Kubernetes 的網絡通信有初步的了解了吧。
整個通信的過程需要各種組件的配合,比如 Pod 網絡命名空間、pod 以太網接口eth0?、虛擬以太網接口vethX?、網橋(network bridge)cni0等。其中有些組件與 pod 一一對應,與 pod 同生命周期。雖然可以通過手動的方式創建、關聯和刪除,但對于 pod 這種非永久性的資源會被頻繁地創建和銷毀,太多人工的工作也是不現實的。
實際上這些工作都是由容器委托給網絡插件來完成的,而網絡插件所遵循的規范 CNI(Container Network Interface)。
網絡插件都做了什么?
創建 pod(容器)的網絡命名空間創建接口創建 veth 對設置命名空間網絡設置靜態路由配置以太網橋接器分配 IP 地址創建 NAT 規則...
參考https://www.tigera.io/learn/guides/kubernetes-networking/
https://kubernetes.io/docs/concepts/services-networking/
https://matthewpalmer.net/kubernetes-app-developer/articles/kubernetes-networking-guide-beginners.html
https://learnk8s.io/kubernetes-network-packets
標簽: Kubernetes 網絡模型 網絡通信