# GoDaddy VPS 內的 Docker Container 無法訪問網際網路問題 ## 問題 Docker container 無法對外連線,執行 `ping` 指令,會得到 `100% packet loss`: ```shell $ docker run --rm -it alpine ping -c4 8.8.8.8 4 packets transmitted, 0 received, 100% packet loss, time 0ms ``` 執行 `traceroute` 指令,則會發現封包不知道迷走到哪裡去了: ![](https://i.imgur.com/p5hkSfF.png) 經過爬文有找到幾個苦主也有遇到類似的問題[^similar-1][^similar-2],方向大致上是虛擬機環境下的 iptables 設定造成。 ## 原因及處理方式 先講結論,原因是 GoDaddy 的 VPS 內有設定 IP 別名 (IP Alias): ``` $ ifconfig eth0 Link encap:Ethernet HWaddr fa:16:3e:92:17:e1 inet addr:10.217.145.73 Bcast:10.217.147.255 Mask:255.255.252.0 inet6 addr: fe80::f816:3eff:fe92:17e1/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:505996 errors:0 dropped:7 overruns:0 frame:0 TX packets:18200 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:43510223 (43.5 MB) TX bytes:3221276 (3.2 MB) eth0:0 Link encap:Ethernet HWaddr fa:16:3e:92:17:e1 inet addr:192.168.43.30 Bcast:192.168.43.30 Mask:255.255.255.255 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 ``` (註:我把我的 public IP 位址改成其他的 private IP 位址了) 但是 route 的預設行為總是指向基底界面[^ip-aliases],因此 Docker 在 iptable 裡設定的 MASQUERADE 會直接使用 `eth0` 的 IP ,造成從 container 出去的封包與 host 的封包長得不一樣。(後面會補充細節) 解決的辦法就是用帶有 `--to-source` 參數的 SNAT 取代原本的 MASQUERADE,首先刪除原本存在的規則[^delete-iptables-rules]: ```= # 用這個指令找到 docker 設定的 MASQUERADE $ sudo iptables -S -t nat #... -A POSTROUTING -s 172.19.0.0/16 ! -o br-616d6a0fd2d8 -j MASQUERADE -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE #... # 用這個指令找到 rule 的序號 $ sudo iptables -t nat -v -L POSTROUTING -n --line-number Chain POSTROUTING (policy ACCEPT 244K packets, 16M bytes) num pkts bytes target prot opt in out source destination 1 0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0 2 0 0 MASQUERADE all -- * !br-616d6a0fd2d8 172.19.0.0/16 0.0.0.0/0 # 然後刪除那些 rule,記得要先把 name 和 source 記起來 # e.g: docker0, 172.17.0.0/16 $ sudo iptables -t nat -D PREROUTING ${rule-number-here} ``` 接著加上 `SNAT` 的規則[^tptable-src](需要依照實際狀況決定參數): ```shell sudo iptables -t nat -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j SNAT --to-source 192.168.43.30 ``` ## 歷程 網路問題早在 build image 的時候就遇到了。在 build `Dockerfile` 的時候需要下載套件,但是卻發現連不到網路,當時是直接使用 `--network host` 參數解決這個問題。 後來要正式佈署時,發現我有個需要對外連線的 service 總是連線失敗,進入 container 測試卻發現 ping 不出去,這才發現我的 docker 網路環境有問題。 關於 Docker 網路的問題,網路上的資料不乏檢查 DNS、或是重新安裝 Docker;兩者我都嘗試過了還是沒有頭緒為什麼網路會不通,最讓我困惑的點是,從外部是能夠訪問 container 上跑得 http service,但是 container 內部卻連不出來。 後來在網路上發問之後,有人要我貼 `ip route`, `ip a` 和 `iptables -S -t nat` 等幾個指令的資訊,於是我便朝著這幾個指令的方向去爬文。後來看到一篇文章在講 Docker 是如何在 Linux 上建立網路的[^docker-network],發現文中提到 `tcpdump` 指令以及 NAT 如何修改封包資訊,於是我便依樣畫葫蘆跑幾次指令看執行結果,接著我驚訝的發現:從 container 和 host ping 出去的時候,source 長得不一樣! 當我從 host `ping 8.8.8.8` 時顯示我是從公共 IP 的地址出發,但是從 container 則是顯示 VPS 的私有 IP,在多瀏覽幾篇文章之後,我終於知道了問題是出在 NAT 的設定上。 ## 雜記 筆者對於網路架設方面比較沒經驗,查資料時候經常看到很多陌生名詞,以下就是爬文時留下筆記或一些覺得比較值得參考的資料。 ### Docker 網路模型 - https://www.hwchiu.com/docker-network-model.html - https://www.hwchiu.com/docker-network-model-lab.html - https://www.hwchiu.com/docker-network-model-snat.html ### 網路常用指令 網路參數設定使用的指令[^network-command]: - `ifconfig` - 查詢、設定網路卡與 IP 網域等相關參數。 - ifconfig(Interface Configuration)是設定網路組態最重要的命令。 - `ifup`, `ifdown` - 這兩個檔案是 script,透過更簡單的方式來啟動網路介面; - `route` - 查詢、設定路由表 (route table) - `ip` - 複合式的指令, 可以直接修改上述提到的功能; ### `route` vs `iptables`[^route-vs-iptables] `route` is a command that displays, adds and deletes entries from the kernel's TCP/IP routing table (aka "Forwarding Information Base"). `iptables` is a command that displays, adds, and deletes entries from Netfilter, the Linux kernel's packet filtering and manipulating subsystem. It handles NAT. ### 路由表 用 `route` 或 `ip route` 指令查詢。 使用指令 `route -n` 後,應該要看到至少兩列數字, 類似這樣: ``` Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.3.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 0.0.0.0 192.168.3.254 0.0.0.0 UG 0 0 0 eth0 ``` 這表示: 對於區網內的 IP (`192.168.3.*`), 直接透過網卡 eth0 即可連線; 對於區網外的其他所有 IP (`*.*.*.*`) 則都要通過 gateway (192.168.3.254) 來連線。 [^route] ### 網路界面 (Network interface) 系統中常見的網路介面有下列幾種[^netwok-interface]: - 迴授(Local Loopback)介面 - lo - 此迴授介面為虛擬位址,即使沒有連結實際網路也有一個迴授介面。可當作一個網路介面來測試網路功能,資料由迴授介面送出,再送回主機。迴授介面名稱為 lo,而固定 IP 位址為 127.0.0.1。 - Ethernet 網路介面 – eth0 - 如果主機上安裝有 Ethernet 網路卡,在開機時核心驅動程式就會自動尋找網路卡並建立介面。 - 點對點連線 – ppp0 - 點對點連線是利用 PPP(Point-to-Point Protocol)通訊協定,以數據機連接電話撥接網路時使用。PPP 啟動時以 pppd 為 Daemon,第一個連接介面為 ppp0,依此類推。 ### ICMP (網際網路控制訊息協定) 全名 Internet Control Message Protocol。 比較科普的介紹 - https://www.netadmin.com.tw/netadmin/zh-tw/technology/111381F2995A4AB48672E965F63133AE 比較資工的解釋(涉及封包內容) - http://www.tsnien.idv.tw/Internet_WebBook/chap5/5-4%20ICMP%20通訊協定.html - http://www.tsnien.idv.tw/Network_WebBook/chap13/13-5%20ICMP%20通訊協定.html ### IP (網際網路協定) 全名 Internet Protocol,網路七層架構的第三層(網路層)。平時把 IP 地址 IP、 IP 叫,叫到都忘記這東西是通訊協定了。囧 ### 網路位址轉譯 (NAT) 全名 Network Address Translation ### IP 偽裝 (IP Masquerade) IP Masquerade 是 Linux 發展中的一種網路功能.如果一台 Linux 主機使用 IP Masquerade 功能連線到網際網路上,那麼接上它的電腦(不論是在同一個區域網路上或藉由數據機連線)也可以接觸網際網路,即使它們沒有獲得正式指定的 IP 位址。[^Masquerade]: ### IP Alias IP-aliases are an obsolete way to manage multiple IP-addresses/masks per interface. The corresponding route is also set up by this command. Please note: The route always points to the base interface. [^ip-aliases] [^ip-aliases]: IP-Aliasing — The Linux Kernel documentation. (n.d.). Retrieved 2021-05-11, from https://www.kernel.org/doc/html/latest/networking/alias.html [^route]: 網路基本指令. (洪朝貴). Retrieved 2021-05-11, from https://www.cyut.edu.tw/~ckhung/b/gnu/network.php [^netwok-interface]: Linux 網路命令彙集. (粘添壽). Retrieved 2021-05-11, from http://www.tsnien.idv.tw/Linux_WebBook/chap6/6-5%20網路命令彙集.html [^Masquerade]: Linux IP Masquerade mini HOWTO 中譯版 : 背景知識. ( Asd L. Chen). Retrieved 2021-05-11, from http://www.e-infomax.com/ipmasq/howto-trans/zh/ipmasq-HOWTO-zh-2.html [^network-command]: 鳥哥的 Linux 私房菜 -- Linux 常用網路指令介紹. Retrieved 2021-05-11, from http://linux.vbird.org/linux_server/0140networkcommand.php [^docker-network]: Docker 網路入門篇(三) - 網路存取分析 | Hwchiu Learning Note. (hwchiu). Retrieved 2021-05-11, from https://www.hwchiu.com/docker-network-model-snat.html [^route-vs-iptables]: linux - iptables vs route - Super User. (n.d.). Retrieved 2021-05-11, from https://superuser.com/questions/419659/iptables-vs-route [^similar-1]: Docker Bridge Network Mode - No internet - SOLVED. (n.d.). Retrieved 2021-05-11, from https://forums.docker.com/t/docker-bridge-network-mode-no-internet-solved/105762 [^similar-2]: in docker can't route to internet - General Support - Unraid. (n.d.). Retrieved 2021-05-11, from https://forums.unraid.net/topic/89433-in-docker-cant-route-to-internet/ [^delete-iptables-rules]: Linux iptables delete prerouting rule command - nixCraft. (n.d.). Retrieved 2021-05-11, from https://www.cyberciti.biz/faq/linux-iptables-delete-prerouting-rule-command/ [^tptable-src]: IP 別名和輔助 IP 地址_xiewen99的專欄-CSDN博客. (n.d.). Retrieved 2021-05-11, from https://blog.csdn.net/xiewen99/article/details/54729112 ###### tags: `learning note` `2021-05-10`