antony@notes:~/container$ cat "Container-網路基礎.md"
Container 網路基礎
Container 網路基礎
認識虛擬網路裝置
在虛擬網路世界中,需要一定程度的內部管道來修補、隧道和轉送系統內的封包。這種 「內部管道 」是利用虛擬網路設備建構的,如 TUN、TAP 和 veth Pairs。
TUN/TAP 裝置
TUN/TAP 為 user space 程式提供資料包接收和傳輸功能。它可以被視為一個簡單的點對點或乙太網路設備,不是從 physical media 接收資料包,而是從 user space 程式接收資料包,也不是透過 physical media 發送資料包,而是將封包寫入 user space 程序。
換句話說,TUN/TAP 驅動程式會在 Linux 主機上建立一個虛擬網路介面。此介面的功能與其他介面類似,例如,你可以為其分配 IP、分析流量、路由流量等。當流量被傳送到該介面時,流量會被傳送到你的 user space 程序,而不是真實網路。
TUN/TAP 有兩種驅動模式,沒錯,就是 TUN 和 TAP。
- TUN (tunnel) 設備在第 3 層運行,這表示從 file descriptor (檔案描述符) 接收的資料(packets)將基於 IP。寫回設備的資料也必須是 IP 封包形式。
- TAP(網路分路器)的操作方式與 TUN 很相似,但它不能向/從檔案描述符寫入和接收第 3 層封包,而是可以使用原始乙太網路封包。 KVM/Qemu 虛擬化通常會使用 TAP 設備,在建立過程中將 TAP 設備指派給虛擬客戶介面。

Veth Pairs
Veth 裝置是成對建立的虛擬乙太網介面,可以被看作是虛擬網路線。一端進去的資料會從另一端出來。
這使得 veth 對成為連接不同虛擬網路元件(如 Linux bridge、OVS bridge 和 LXC Container)的理想選擇。

認識 Linux Namespace
Linux 中的 namespace 機制是一種蒙蔽了程序的雙眼「讓一個程序只能看見作業系統中的部分資源」的機制。這些資源可能包含檔案系統、使用者、時間、網路等等。
Namespace 其中一個應用是「隔離」同一個作業系統上的不同程序。把一個程序放在某一個種類的 namespace 中,它就會只看得到該 namespace 下看得到的資源。儘管處在該 namespace 中的程序可能以為自己可以存取整個根目錄、以為自己是它 root,但在 namespace 以外的程序看來只是一個普通權限的程序。而這也是容器 (container) 的基礎。
甚麼是 Linux Network Namespace
network namespace 如其名稱所示,用來隔離作業系統跟網路相關的資源。被 network namespace 隔離的那些行程,會以為自己獨享了一個完整的 network stack。或者更明確地,按照 man 中的 network_namespaces(7) 中的說法:
Network namespaces provide isolation of the system resources associated with networking: network devices, IPv4 and IPv6 protocol stacks, IP routing tables, firewall rules…
也就是說:不同的 network namespce 中的行程會看見不同的 interface、routing table、netfilter 規則等等。
雖然可以用 unshare -n 建立一個 network namespace,但更常見的可能是使用 ip netns。不同的 network namespace 可以各自有自己的 interface、IP、routing table、port mapping 等等。這些隔離出來的 namespace 之間可以使用虛擬的 bridge 連接起來。關於這個命令更詳細的行為可以在 ip-netns(8) 找到。
實作 Linux Network Namespace
要根據下圖建立對應的網路

開始建立
# 1. Creating the bobo network namespace
$ sudo ip netns add bobo
# 2. 確認已建立
$ sudo ip netns ls
bobo (id: 3)
### 補充: network namespace 會被放在 /var/run/netns 底下
$ ls -l /var/run/netns
total 0
-r--r--r-- 1 root root 0 Jun 3 16:12 bobo
# 3. Creating the veth pair
$ sudo ip link add veth99 type veth peer name veth100
# 4. Adding one end of the veth pair to the bobo namespace
$ sudo ip link set veth100 netns bobo
# 5. 檢查是否新增成功
$ sudo ip netns exec bobo ip a s veth100
8: veth100@if9: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 02:9b:3b:a9:ea:93 brd ff:ff:ff:ff:ff:ff link-netnsid 0
# 6. Configuring the interface in the network namespace with an IP address
$ sudo ip netns exec bobo ip addr add 10.0.8.11 dev veth100
# 7. Enabling the interface inside the network namespace
$ sudo ip netns exec bobo ip link set dev veth100 up
# 8. Enabling the interface on the node
$ sudo ip link set dev veth99 up
# 9. Setting the loopback interface in the network namespace (not mandatory)
$ sudo ip netns exec bobo ip link set lo up
# 10. Setting the routes on the node
$ sudo ip route add 10.0.8.11/32 dev veth99
# 11. Setting the default route in the network namespaces
$ sudo ip netns exec bobo ip route add default via 10.0.8.11 dev veth100
# 12. 檢視在 bobo network namespace 的路由規則
$ sudo ip netns exec bobo route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.0.8.11 0.0.0.0 UG 0 0 0 veth100
# 13. 檢視路由規則
$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.61.2 0.0.0.0 UG 202 0 0 eth0
10.0.8.11 0.0.0.0 255.255.255.255 UH 0 0 0 veth99
10.89.0.0 0.0.0.0 255.255.255.0 U 0 0 0 podman1
192.168.61.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
# 12. test connection
$ ping -c 1 10.0.8.11
PING 10.0.8.11 (10.0.8.11) 56(84) bytes of data.
64 bytes from 10.0.8.11: icmp_seq=1 ttl=64 time=0.055 ms
--- 10.0.8.11 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.055/0.055/0.055/0.000 ms
# 13. 在 bobo network namespace 建一個網站測試
$ sudo ip netns exec bobo python3 -m http.server &
[1] 51714
# 14. 檢查網站
$ curl 10.0.8.11:8000
...
<h1>Directory listing for /</h1>
...