iT邦幫忙

2022 iThome 鐵人賽

DAY 28
2
DevOps

那些關於 docker 你知道與不知道的事系列 第 28

Day 28: 為什麼出不了國呢?

  • 分享至 

  • xImage
  •  

昨天我們走到了這裡,成功地讓 ns 可以跟 host (root namespace) 溝通:
https://ithelp.ithome.com.tw/upload/images/20221013/20151857xhtyXCUCBQ.png

但我們發現,還是無法跟 host 以外的網路溝通,很出現的錯誤訊息也不再是 connect: Network is unreachable,看來封包是有送出去的,那讓我們來錄錄看封包,看看到底發生什麼事了?

為了可以在目的地也觀察一下封包收送的情況,所以把目標從 8.8.8.8 改成我另外開的一台 EC2 instance,其 private ip 是 172.31.8.218。

這是我們希望的封包走的路線:https://ithelp.ithome.com.tw/upload/images/20221013/20151857VX8dV4BSWM.png

根據上圖

  1. 在 ns1 錄 veth0 的封包
  2. 在 host 錄 docker1 的封包
  3. 在 host 錄 ens5 的封包
  4. 在 target 錄 eth0 的封包

準備好四個錄封包的指令後,在 ns1 裡執行 ping 172.31.8.218 -c 1,也就是對 target 發出兩個 ICMP request。

  1. 在 ns1 錄 veth0 的封包: 可以看到真的有送出 ICMP echo request,但沒有 reply。
root@ip-xxx:/home/ubuntu# tcpdump -i veth0 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C18:07:27.318105 IP 172.18.0.2 > 172.31.8.218: ICMP echo request, id 23891, seq 1, length 64
18:07:32.439058 ARP, Request who-has 172.18.0.1 tell 172.18.0.2, length 28
18:07:32.439131 ARP, Reply 172.18.0.1 is-at 26:ed:7e:d0:ba:ea, length 28

3 packets captured
3 packets received by filter
0 packets dropped by kernel
  1. 在 host 錄 docker1 的封包: 可以看到,也有從 docker1 送出 ICMP echo request,但也沒有 reply。
ubuntu@ip-xxx:~$ sudo tcpdump -i docker1 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker1, link-type EN10MB (Ethernet), capture size 262144 bytes
18:07:27.318115 IP 172.18.0.2 > 172.31.8.218: ICMP echo request, id 23891, seq 1, length 64
18:07:32.439089 ARP, Request who-has 172.18.0.1 tell 172.18.0.2, length 28
18:07:32.439128 ARP, Reply 172.18.0.1 is-at 26:ed:7e:d0:ba:ea, length 28
^C
3 packets captured
3 packets received by filter
0 packets dropped by kernel
  1. 在 host 錄 ens5 的封包: 在 ens5 也有送出 ICMP echo request,但也沒有 reply。
ubuntu@ip-xxx:~$ sudo tcpdump -i ens5 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens5, link-type EN10MB (Ethernet), capture size 262144 bytes
18:07:27.318162 IP 172.18.0.2 > 172.31.8.218: ICMP echo request, id 23891, seq 1, length 64
^C
1 packet captured
1 packet received by filter
0 packets dropped by kernel

根據上面的三個觀察,看來封包是有送出去的,那 target 那邊呢?根據我這邊測試的結果,target 那邊什麼都沒收到...在 target 那邊我用的指令是 sudo tcpdump -i eth0 icmp -nn,怕自己下錯參數,於是在我們的 host 來 ping 看看:

# 從 host ping target
[ec2-user@ip-xxx ~]$ sudo tcpdump -i eth0 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
18:08:47.151141 IP 172.31.59.121 > 172.31.8.218: ICMP echo request, id 31, seq 1, length 64
18:08:47.151168 IP 172.31.8.218 > 172.31.59.121: ICMP echo reply, id 31, seq 1, length 64
...略

很正常,有 ICMP echo request,並且也 ICMP echo reply,看來我們在 target 錄封包的指令應該沒有下錯,那從 ns 裡 ping target 到底發生什麼事了呢?request 明明都有送出去,但對方似乎沒有收到,當然也就都沒有回覆...


這邊再仔細比較觀察一下,從 ns 裡發出去的 ICMP echo request 的 src IP 是 172.18.0.2,但是從 host 發出去的 ICMP echo request 的 src IP 是 172.31.59.121,雖然這兩個都是 private IP,但 host 的 172.31.59.121 跟 target 的 172.31.8.218 是屬於同一個私有網域,他們彼此會認得,從 target host 這邊來觀察一下他的 routing table:

[ec2-user@ip-xxx ~]$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.31.0.1      0.0.0.0         UG    0      0        0 eth0
169.254.169.254 0.0.0.0         255.255.255.255 UH    0      0        0 eth0
172.31.0.0      0.0.0.0         255.255.240.0   U     0      0        0 eth0

[ec2-user@ip-xxx ~]$ ip route show
default via 172.31.0.1 dev eth0
169.254.169.254 dev eth0
172.31.0.0/20 dev eth0 proto kernel scope link src 172.31.8.218

這邊可以看到有一條 destination 是 0.0.0.0,昨天在 ns1 裡設定 default 時,有用 route -n 來觀察,有發現到 0.0.0.0 就是那條 default 規則,用 ip route 來觀察會得到一樣的結論。對 target 來說,要將 reply 回給 172.31.59.121 的話,因為沒有符合的規則,所以會去找預設規則,因此會往 eth0 送。

那如果是從 ns 裡發出來的 ping request 呢?其 src ip 會是 ns 裡自己的 private ip 172.18.0.2,那照理說,當 target 要回送的時候,在 routing table 中找不到符合的規則。但沒有關係,那既然有一條預設的規則了,所以 src 是 ns 的 private ip 172.18.0.2 也沒有關係嗎?不過,現在問題是在於,連收到 request 都沒有啊,所以中間到底發生什麼事呢?


網路上查到的大多數的說法都說因為 172.18.0.2 不是一個網路上認得的 IP,因此封包雖然有送出去,但是是無法收到 reply 回來的封包,這意思是 target 其實有收到、也有回覆,只是因為回覆的時候,原本的 src IP 172.18.0.2 變成 dst IP,但因為網路上不認得這個 dst IP,所以送不回來?然而我上述的實驗發現,其實 target 是沒有收到這個封包的(如果是以 tcpdump 的結果來說),另外也有用 netstat --statistics 這個指令來觀察 ICMP 的數量變化,如果是從 host 去 ping(可以 ping 成功),那 netstat --statistics 數字是會有變化的,但如果是從 ns 中 ping,netstat --statistics 的數字則不會跳動,這應該也表示了 target host 根本沒有收到從 ns 裡發出來的 ICMP echo request?

除此之外,因為環境是架設在 AWS 上,所以我也去設置了 Flow Log,這邊觀察到更奇妙的是,如果是從 host 直接 ping target,這台 host 中 ens5 的 Flow Log 是會有紀錄的,但如果是從 ns 裡 ping 出來的話,Flow Log 是不會有紀錄的,所以、難道,host 根本沒有把這個 ICMP request 送出來嗎?那 ens5 錄到的又是什麼呢?

上述實驗還有觀察 MAC address,發現從 host 出去時,實際上是會先往 gateway 送,有可能是因為 gateway 不認得這個 src IP(172.18.0.2) 然後就把這個封包 drop 掉,因此 target 才收不到嗎?為了驗證這件事,我啟動了一台與 host 在同一個 subnet 的 EC2 instance 當作 target,發現因為是在同一個 subnet 裡,所以 ARP 裡直接有了 IP 與 MAC address 的對照,因此即便是在 MAC address 這邊的觀察,封包也是直接送到 target 的 MAC address 去的,但在 target 上用 tcpdump 來觀察,依舊錄不到東西。(這裡應該也可以把 Flow Log 打開來看看,但太累啦,改天再來試)

總之這邊有點謎啊,應該是我對於 Linux 及 Network 的理解還不夠,且由於是在 AWS 上,不確定這是 Linux 或網路的機制,還是是 AWS 的網路做了什麼?如果有大大知道這邊的運作,再請多多指教了!


上一篇
Day 27: 讓我們出海去...欸,等一下,只能先國內旅遊...
下一篇
Day 29: 讓我們出國去
系列文
那些關於 docker 你知道與不知道的事32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言