veth pair 不夠用了#
veth pair 只能連通兩個 Network Namespace。想要三個、四個容器互相看得到,就需要一個「集線器」般的元件,把多條 veth 都接上去。這個角色由 Linux bridge 擔任。
bridge 的本質是核心裡的虛擬交換機(virtual switch):
- 接上 bridge 的介面叫做這座 bridge 的 port
- 任何從某個 port 進來的封包,bridge 會依 MAC 表決定要從哪個 port 送出
- 行為很接近一台 L2 switch
Docker 預設的 bridge 模式,就會建立一座叫做 docker0 的 Linux bridge,每個容器透過 veth pair 把一端接到 docker0 上、另一端塞進容器自己的 namespace。
建立 bridge 的兩種方式#
老派工具是 brctl(bridge-utils):
sudo brctl addbr br0
sudo brctl show新派寫法走 iproute2:
sudo ip link add name br0 type bridge
sudo ip link set br0 up
ip -d link show br0兩種方式建出來的東西是同一樣東西,後續操作建議統一用 ip 命令。
用 bridge 連通兩個 namespace#
接下來重做一次上一章的實驗,但改用 bridge 當中介。
1. 建立 namespace 與 bridge#
sudo ip netns add ns1
sudo ip netns add ns2
sudo ip link add name br0 type bridge
sudo ip link set br0 up2. 為每個 namespace 拉一對 veth#
ns1 用 veth-h1(host 端)和 veth-c1(容器端);ns2 用 veth-h2 與 veth-c2:
sudo ip link add veth-h1 type veth peer name veth-c1
sudo ip link add veth-h2 type veth peer name veth-c23. host 端接上 bridge#
sudo ip link set veth-h1 master br0
sudo ip link set veth-h2 master br0
sudo ip link set veth-h1 up
sudo ip link set veth-h2 upmaster br0 表示這個介面成為 br0 的 port。
4. 容器端塞進各自 namespace#
sudo ip link set veth-c1 netns ns1
sudo ip link set veth-c2 netns ns25. 在 namespace 內設 IP 並 up#
sudo ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth-c1
sudo ip netns exec ns1 ip link set veth-c1 up
sudo ip netns exec ns1 ip link set lo up
sudo ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth-c2
sudo ip netns exec ns2 ip link set veth-c2 up
sudo ip netns exec ns2 ip link set lo up6. 互 ping 驗證#
sudo ip netns exec ns1 ping -c 3 10.0.0.2應該收得到回覆。封包路徑是:
ns1的veth-c1出 → host 的veth-h1進br0依 MAC 表把封包轉到veth-h2veth-h2出 →ns2的veth-c2進
bridge 自己不需要 IP,只負責 L2 轉送。要不要給 bridge 一個 IP,取決於是否打算讓 bridge 同時當 default gateway,這留到下一章談。
看一下 bridge 的 port#
bridge link show
ip -d link show master br0會看到 veth-h1 與 veth-h2 都標示為 master br0。
對應到 Docker#
啟動 Docker 之後,docker0 就是一座一模一樣的 Linux bridge:
ip link show docker0
bridge link show每跑一個 bridge 模式的容器,Docker 就會:
- 建一對 veth pair
- 把 host 端接到
docker0 - 把容器端塞進容器的 Network Namespace 並命名為
eth0 - 從
docker0對應的子網(預設172.17.0.0/16)配一個 IP 給容器
整個流程跟手動拼出來的版本一樣,只是被 Docker 自動化了。
還少什麼#
到這裡,namespace 之間互通沒問題了,但容器還沒辦法連到外面的網站。下一章要補上 routing table(路由表)、default gateway 與 ip route,讓封包知道「不認識的地址要往哪送」。
延伸閱讀#
man 8 bridgeman 8 brctl- iproute2 的
ip link ... type bridge