到昨天為止,我們建立以下架構,並且也透過 tcpdump
觀察到封包的流向:
但到目前為止,ns1 跟 ns2 都還是出不去,別說出不去,連 host 都無法溝通:
# 進入 ns1
ubuntu@ip-xxx:~$ sudo ip netns exec ns1 bash
# 試著 ping host 的 ip 172.31.59.121
root@ip-xxx:/home/ubuntu# ping 172.31.59.121
ping: connect: Network is unreachable
# 試著 ping 外部網路 8.8.8.8
root@ip-xxx:/home/ubuntu# ping 8.8.8.8
ping: connect: Network is unreachable
注意這邊的錯誤訊息是 connect: Network is unreachable
,遇到這樣的訊息表示這個 ping 封包連送都沒有被送出去,如果試著用 tcpdump
去錄封包,那會看到 0 packets captured
這樣的訊息,會這樣是因為 "the switch cannot find an interface with a route that leads to the destination" (來源)。
所以要處理這個問題還得從 ip route 開始說起,參考這份文件:
A key concept in IP routing is the ability to define what addresses are locally reachable as opposed to not directly known destinations.
Routing 這個詞可以想成是將網路封包從一個點從到另外一個點去,例如我們要連到 www.google.com 去查資料,我們需要將我們的 HTTP request 送到 google 的主機去,但我們的電腦並沒有直接跟 google 的 server 連接啊,那網路封包是怎麼送過去的呢?電腦其實是將這個封包先送到一個叫做 gateway
的地方去,這個 gateway
會再把這個網路封包送到另外一個 gateway 或者 router 去,就這樣一個往下一個去送,最終送達目的地去。
那問題來了,最一開始,我們的作業系統怎麼知道要往哪裡送呢?在 Linux 中,有一個 routing table 會負責記錄網路封包要哪裡送去,我們可以透過 ip route
(文件)或是 route
(文件)來查看,讓我們來看一下目前 ns1 中的 routing table:
# 進入 ns1
ubuntu@ip-xxx:~$ sudo ip netns exec ns1 bash
root@ip-xxx:/home/ubuntu# ip route show
172.18.0.0/24 dev veth0 proto kernel scope link src 172.18.0.2
# 用 route 來看其實更清楚:
root@ip-xxx:/home/ubuntu# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
172.18.0.0 0.0.0.0 255.255.255.0 U 0 0 0 veth0
ps. 如果沒有 route
這個指令
其意思是所有要前往 172.18.0.0/24 的網路封包都從 veth0 這個介面轉發,並且都以 172.18.0.2 作為其來源 ip。看到這裡應該可以理解,為什麼我們 ping 172.18.0.3 可以通了,當我們 ping 172.18.0.3 時,封包會往 veth0 這邊送出去,然後接著到 veth pair 的另外一端 veth0-br 去,再透過 bridge 送到 veth1-br,然後又被送到 veth1-br 的另外一端 veth1 去。
但當我們 ping host ip (172.31.59.121) 或是外部 ip 8.8.8.8 的時候呢?系統在這個 routing table 上找不到要往哪裡送去,於是就連送都不送了,直接來個 unreachable
給我們。同理,我們也來看看 host 的 routing table:
ubuntu@ip-xxx:~$ sudo route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.31.48.1 0.0.0.0 UG 100 0 0 ens5
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.31.48.0 0.0.0.0 255.255.240.0 U 0 0 0 ens5
172.31.48.1 0.0.0.0 255.255.255.255 UH 100 0 0 ens5
這裡一樣可以看到,如果要從 host 去 ping 172.18.0.2
或 172.18.0.3
一樣是會 ping 不到的,因為也不知道要把這個封包往哪裡送。
既然知道原因了,那是不是設定好 routing table 就可以了呢?以下就讓我們來試試看!
我們先來處理 host,這邊意外地簡單,只需要把我們的 bridge docker1
加上 ip 即可:
ubuntu@ip-xxx:~$ sudo ip addr add 172.18.0.1/24 dev docker1
ubuntu@ip-xxx:~$ ip addr
1: lo: ...略
2: ens5: ...略
8: docker0: ...略
9: docker1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 26:ed:7e:d0:ba:ea brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/24 scope global docker1
valid_lft forever preferred_lft forever
inet6 fe80::5854:82ff:fe71:935b/64 scope link
valid_lft forever preferred_lft forever
10: veth0-br@if11: ...略
12: veth1-br@if13: ...略
確認一下 host 的 routing table:
ubuntu@ip-xxx:~$ sudo route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.31.48.1 0.0.0.0 UG 100 0 0 ens5
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.18.0.0 0.0.0.0 255.255.255.0 U 0 0 0 docker1
172.31.48.0 0.0.0.0 255.255.240.0 U 0 0 0 ens5
172.31.48.1 0.0.0.0 255.255.255.255 UH 100 0 0 ens5
這邊果然多了一條 Destination 是 172.18.0.0
的設定了,現在 host 知道舉凡是要去 172.18.0.0/24
的封包,都往 docker1
送去。來試試看結果:
ubuntu@ip-xxx:~$ ping 172.18.0.2 -c 2
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.049 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.046 ms
--- 172.18.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1024ms
rtt min/avg/max/mdev = 0.046/0.047/0.049/0.001 ms
ubuntu@ip-xxx:~$ ping 172.18.0.3 -c 2
PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data.
64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.045 ms
64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.042 ms
--- 172.18.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1020ms
rtt min/avg/max/mdev = 0.042/0.043/0.045/0.001 ms
果然成功了,不管是到 172.18.0.2 或是 172.18.0.3 都可以,那從 172.18.0.2 或 172.18.0.3 可以 ping 到 host 了嗎?你可以試試看,很遺憾,還是不行的,一樣是 unreachable
,其實也不意外啦,我們是跟 host 講要送去哪,但又沒有跟 ns1 講,對吧!
在 ns 裡(其實就是模擬了 container),所有的封包應該都是要往外送的,且都是透過 veth 去傳到另外一端 veth-br,然後再到 bridge docker1
去,而我們現在 docker1
已經有了一個 ip 是 172.18.0.1,那我們就把這個方向設定預設的規則:
# 建立之前先觀察一下
root@ip-xxx:/home/ubuntu# ip route list
172.18.0.0/24 dev veth0 proto kernel scope link src 172.18.0.2
# 增加一條設定,把 172.18.0.1 設定成預設的方向
root@ip-xxx:/home/ubuntu# ip route add default via 172.18.0.1
# 確認是否設定成功
root@ip-xxx:/home/ubuntu# ip route list
default via 172.18.0.1 dev veth0
172.18.0.0/24 dev veth0 proto kernel scope link src 172.18.0.2
# 在 route 這個指令,default 的規則,他的 destination 會寫成 0.0.0.0
root@ip-xxx:/home/ubuntu# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.18.0.1 0.0.0.0 UG 0 0 0 veth0
172.18.0.0 0.0.0.0 255.255.255.0 U 0 0 0 veth0
來確認看看結果吧:
root@ip-xxx:/home/ubuntu# ping 172.31.59.121 -c 2
PING 172.31.59.121 (172.31.59.121) 56(84) bytes of data.
64 bytes from 172.31.59.121: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 172.31.59.121: icmp_seq=2 ttl=64 time=0.051 ms
--- 172.31.59.121 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1008ms
rtt min/avg/max/mdev = 0.051/0.052/0.053/0.001 ms
喔耶,可以抵達 host 了!
那快來試試看能不能到外頭去:
root@ip-xxx:/home/ubuntu# ping 8.8.8.8 -c 2
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
(...卡住了)
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1001ms
誒,還是不行,但至少不是 connect: Network is unreachable
了,根據 ping
的統計,真的是有兩個封包被送出去了,那到底怎麼回事呢?
整理一下到目前為止的進展:
我們好不容易終於可以讓 ns (模擬 container) 可以跟 host 溝通了,但還差最後一步,還無法航向全世界,明天我們就來試試看怎麼解決這個問題!