K8S之網絡深度剖析
一 、關于K8S的網絡模型
在K8s的世界上,IP是以Pod為單位進行分配的。一個Pod內部的所有容器共享一個網絡堆棧(相當于一個網絡命名空間,它們的IP地址、網絡設備、配置等都是共享的)。按照這個網絡原則抽象出來的為每個Pod都設置一個IP地址的模型也被稱作為IP-per-Pod模型。
Kubernetes對集群網絡有如下要求:
- 所有容器都可以在不用NAT的方式下同別的容器通訊。
- 所有節點都可以在不用NAT的方式下同別的容器通訊。
- 容器的地址和別人看到的地址是同一個地址。
二、Docker 網絡
Docker 使用到的與Linux網絡有關的主要技術:
- 網絡命名空間(Network Namespace)
- Veth 設備對
- 網橋
- Iptables
- 路由
2.1 網絡命名空間
為了支持網絡協議棧中的多個實例,Linux 2.6.24內核版本的網絡協議棧中引入了網絡命名空間,這些獨立的協議棧被隔離到不用的命名空間中。處于不用命名空間中的網絡棧是完全隔離的,彼此之間無法通訊,就好像兩個"平行宇宙"。通過對于網絡資源的隔離,就能在一個宿主機上虛擬多個不同的網絡環境。Docker 正是利用了網絡的命名空間特性,實現了不同容器之間的網絡隔離。
在Linux的網絡命名空間中可以有自己獨立的路由表,及獨立的Iptables設置來提供包轉發、NAT、及IP包過濾等功能。
為了隔離出獨立的協議棧,需要納入命名空間的元素有進程、套接字、網絡設備等。進程創建的套接字必須屬于某個網絡命名空間,套接字的操作也必須在命名空間中進行。同樣,網絡設備也必須屬于某個命名空間。因為網絡設備屬于公共資源,所以可以通過修改屬性實現在命名空間之間移動。當然,是否允許移動,和設備的特征有關系。
2.1.1 網絡命名空間的實現
Linux的網絡協議棧是十分復雜的,為了支持獨立的協議棧,相關的這些全局變量都必須被修改為協議棧私有,最好的辦法就是讓這些全局變量成為一個 Net NameSpace變量的成員,然后修改協議棧的函數調用,在加入一個NameSpace的參數,這就是Linux 實現網絡命名空間的核心。
同時,為了保證對已經開發的應用程序,以及內核代碼的兼容性,內核代碼隱式地使用了網絡命名空間中的變量,程序如果沒有對網絡命名空間有特殊需求,就不需要編寫額外的代碼,網絡命名空間對應用程序而言是透明的。
在建立了新的網絡命名空間,并將某個進程關聯到這個網絡命名空間后,就出現了如下圖的數據結構,所有的網絡棧變量都沒放到了私有的命名空間,和其他進程組并不沖突。
上圖來自網絡命名空間深度好文
在新生成的理由命名空間中,只有回環設備,(名為"lo" 且是停止狀態),其他設備默認都不存在,如果我們需要,則要一一手工建立,Docker容器中的各類網絡棧設備都是Docker Daemon 在啟動時自動創建和配置的。
所有的網絡設備(物理的或虛擬接口、橋等在內核里都叫做Net Device)都只能屬于一個命名空間、當然,物理設備(連接實際硬件的設備)通常只能關聯到root 這個命名空間。虛擬的網絡設備(虛擬的以太網接口或者虛擬網口對)則可以被關聯到一個給定的命名空間,而且可以在這些命名空間中移動。
前面提到,由于網絡命名空間代表的是一個獨立的協議棧,所以它們之間是相互隔離的,彼此無法通訊,在協議棧內部都看不到對方, 那么有沒有辦法打破這種限制,讓處于不同網絡命名空間的網絡互相通訊,甚至和外部的網絡通訊呢? 答案就是“有”,應用 “Veth設備對即可”, Veth 設備對一個重要的作用就是打通互相看不到的協議棧的協議棧之間的壁壘,他就像一條管子,一端連著這個網絡命名空間的協議棧,一端連著另一個網絡命名空間協議棧,所以想要在兩個命名空間之間通訊,就必須有一個 Veth 設備對。
2.1.2 網絡命名空間的操作
我們可以使用 Linux iproute2 系列配置工具中的IP 命令來操作網絡命名空間,注意: 這個命令需要使用root 用戶來執行。
安裝:
# alphine
apk add iproute2# ubuntu
sudo apt install iproute2# centos
sudo yum -y install iproute2
創建一個網絡命名空間:
ip netns add <name>
創建一個名為test1 的網絡命名空間
ip netns add test1
在命名空間中執行命令:
ip netns exec <name> <command>
在命名空間test1 執行 ip a s 命令
root@test:~# ip netns exec test1 ip a s
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
也可以先通過切換到對應的網絡命名空間,執行各種命令:
ip netns exec <name> bash
執行 exit 或Ctrl +d 退出當前網絡命名空間。
上面的操作相當于網絡命名空間進行了切換,文件系統還是當前,在Docker 中,其實也做了類似操作,并且將文件系統通過chroot 進行了切換,通過cgroup 對資源進行了隔離。
2.1.3 網絡命名空間的實用技巧
操作系統網絡命名空間的一些技巧如下:
我們可以在不同的網絡命名空間之間轉移設備,列如下面會提到的Veth 設備對的轉移,因為一個設備只能屬于一個命名空間,所以轉移后在這個網絡命名空間內就看不到這個設備了,具體哪些設備能被轉移到不同的網絡命名空間, 在設備里面有一個屬性: NETIF_F_NETNS_LOCAL,如果這個屬性為 on,就不能被轉移到其他命名空間中。Veth 設備屬于可轉移設備,而其他設備如,lo 設備,vxlan,ppp 設備,bridge設備等都是不可轉移的。將無法轉移的設備移動到別的命名空間時,會得到無效的參數錯誤提示。
root@test1:~# ip link set lo netns test1
RTNETLINK answers: Invalid argument
如何知道這些設備是否可以轉移呢? 可以使用ethtool 工具查看:
root@test1:~# apt install ethtool
root@test1:~# ethtool -k docker0 | grep netns
netns-local: on [fixed]
root@test1:~# ethtool -k veth276e185 | grep netns
netns-local: off [fixed]