云原生虛擬網絡 tun/tap & veth-pair( 二 )


flannel UDP 模式使用 tun 設備收發數據早期 flannel 利用 tun 設備實現了 UDP 模式下的跨主網絡相互訪問,實際上原理和上面的 OpenVPN 是差不多的 。
在 flannel 中 flannel0 是一個三層的 tun 設備,用作在操作系統內核和用戶應用程序之間傳遞 IP 包 。當操作系統將一個 IP 包發送給 flannel0 設備之后,flannel0 就會把這個 IP 包,交給創建這個設備的應用程序,也就是 flanneld 進程,flanneld 進程是一個 UDP 進程 , 負責處理 flannel0 發送過來的數據包:

云原生虛擬網絡 tun/tap & veth-pair

文章插圖
flanneld 進程會根據目的 IP 的地址匹配到對應的子網,從 Etcd 中找到這個子網對應的宿主機 Node2 的 IP 地址,然后將這個數據包直接封裝在 UDP 包里面,然后發送給 Node 2 。由于每臺宿主機上的 flanneld 都監聽著一個 8285 端口 , 所以 Node2 機器上 flanneld 進程會從 8285 端口獲取到傳過來的數據 , 解析出封裝在里面的發給 ContainerA 的 IP 地址 。
flanneld 會直接把這個 IP 包發送給它所管理的 TUN 設備,即 flannel0 設備 。然后網絡棧會將這個數據包根據路由發送到 docker0 網橋,docker0 網橋會扮演二層交換機的角色,將數據包發送給正確的端口,進而通過 veth pair 設備進入到 containerA 的 Network Namespace 里 。
上面所講的 Flannel UDP 模式現在已經廢棄,原因就是因為它經過三次用戶態與內核態之間的數據拷貝 。容器發送數據包經過 docker0 網橋進入內核態一次;數據包由 flannel0 設備進入到 flanneld 進程又一次;第三次是 flanneld 進行 UDP 封包之后重新進入內核態,將 UDP 包通過宿主機的 eth0 發出去 。
tap 設備作為虛擬機網卡上面我們也說了 , tap 設備是一個二層鏈路層設備,通常用作實現虛擬網卡 。以 qemu-kvm 為例,它利用 tap 設備和 Bridge 配合使用擁有極大的靈活性,可以實現各種各樣的網絡拓撲 。
云原生虛擬網絡 tun/tap & veth-pair

文章插圖
在 qume-kvm 開啟 tap 模式之后,在啟動的時候會向內核注冊了一個tap類型虛擬網卡 tapx,x 代表依次遞增的數字; 這個虛擬網卡 tapx 是綁定在 Bridge 上面的,是它上面的一個接口 , 最終數據會通過 Bridge 來進行轉發 。
qume-kvm 會通過其網卡 eth0 向外發送數據 , 從 Host 角度看就是用戶層程序 qume-kvm 進程將字符設備寫入數據;然后 tapx 設備會收到數據后由 Bridge 決定數據包如何轉發 。如果 qume-kvm 要和外界通信 , 那么數據包會被發送到物理網卡,最終實現與外部通信 。
從上面的圖也可以看出 qume-kvm 發出的數據包通過 tap 設備先到達 Bridge ,然后到物理網絡中,數據包不需要經過主機的的協議棧,這樣效率也比較高 。
veth-pairveth-pair 就是一對的虛擬設備接口,它是成對出現的,一端連著協議棧 , 一端彼此相連著,在 veth 設備的其中一端輸入數據,這些數據就會從設備的另外一端原樣不變地流出:
云原生虛擬網絡 tun/tap & veth-pair

文章插圖
利用它可以連接各種虛擬設備,兩個 namespace 設備之間的連接就可以通過 veth-pair 來傳輸數據 。
下面我們做一下實驗 , 構造一個 ns1 和 ns2 利用 veth 通信的過程,看看veth是如何收發請求包的 。
# 創建兩個namespaceip netns add ns1ip netns add ns2# 通過ip link命令添加vethDemo0和vethDemo1ip link add vethDemo0 type veth peer name vethDemo1# 將 vethDemo0 vethDemo1 分別加入兩個 nsip link set vethDemo0 netns ns1ip link set vethDemo1 netns ns2# 給兩個 vethDemo0 vethDemo1配上 IP 并啟用ip netns exec ns1 ip addr add 10.1.1.2/24 dev vethDemo0ip netns exec ns1 ip link set vethDemo0 upip netns exec ns2 ip addr add 10.1.1.3/24 dev vethDemo1ip netns exec ns2 ip link set vethDemo1 up然后我們可以看到 namespace 里面設置好了各自的虛擬網卡以及對應的ip:
~ # ip netns exec ns1 ip addrroot@VM_243_186_centos1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group defaultlink/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:007: vethDemo0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether d2:3f:ea:b1:be:57 brd ff:ff:ff:ff:ff:ffinet 10.1.1.2/24 scope global vethDemo0valid_lft forever preferred_lft foreverinet6 fe80::d03f:eaff:feb1:be57/64 scope linkvalid_lft forever preferred_lft forever然后我們 ping vethDemo1 設備的 ip:
ip netns exec ns1 ping 10.1.1.3對兩個網卡進行抓包:
~ # ip netns exec ns1 tcpdump -n -i vethDemo0root@VM_243_186_centostcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on vethDemo0, link-type EN10MB (Ethernet), capture size 262144 bytes20:19:14.339853 ARP, Request who-has 10.1.1.3 tell 10.1.1.2, length 2820:19:14.339877 ARP, Reply 10.1.1.3 is-at 0e:2f:e6:ce:4b:36, length 2820:19:14.339880 IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 27402, seq 1, length 6420:19:14.339894 IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 27402, seq 1, length 64~ # ip netns exec ns2 tcpdump -n -i vethDemo1root@VM_243_186_centostcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on vethDemo1, link-type EN10MB (Ethernet), capture size 262144 bytes20:19:14.339862 ARP, Request who-has 10.1.1.3 tell 10.1.1.2, length 2820:19:14.339877 ARP, Reply 10.1.1.3 is-at 0e:2f:e6:ce:4b:36, length 2820:19:14.339881 IP 10.1.1.2 > 10.1.1.3: ICMP echo request, id 27402, seq 1, length 6420:19:14.339893 IP 10.1.1.3 > 10.1.1.2: ICMP echo reply, id 27402, seq 1, length 64

推薦閱讀