因為網路的知識已經不知道被遺忘到哪去了,導致理解 Docker 的網路配置花了我很多的時間,所以在這篇文章我特別寫下了很多用於理解 Docker 網路的詞彙,希望這樣能夠讓不懂網路的人也能夠理解。

詞彙

這篇文章會用到很多網路的詞彙,對於一個網路不太熟的我,花了很多時間理解這些詞彙,所以我先把這篇會用到的詞彙整理起來,讓不懂網路的人也盡量能夠理解。

詞彙 簡短說明 詳細說明
橋接器 連接兩個網路之間的中繼站,這篇文章是用在主機網路Docker 網路之間的橋接器 Virtual Ethernet Bridge
iptable Linux 管理防火牆的工具,這篇文章使用 iptable 列出 NAT 10分鐘學會IPTables, Linux防火牆iptables基本使用方法
POSTROUTING 在進行路由判斷之後所要進行的規則 鳥哥的 Linux 私房菜 - 第九章、防火牆與 NAT 伺服器
NAT 有分成 SNAT(Source Network Address Translation), DNAT(Destination Network Address Translation),主要在進行來源與目的之 IP 或 port 的轉換,能讓夠內部網路與外部網路溝通。 鳥哥的 Linux 私房菜 - 第九章、防火牆與 NAT 伺服器
MASQUERADE 是 NAT 的一種 IPtables中SNAT和MASQUERADE的區別
網段 主要是可以用來管控網路的配置,這篇文章會提及到 Docker 有自己的網段 網段

更多網路的詳細說明,可以參考 鳥哥的 Linux 私房菜 - 第九章、防火牆與 NAT 伺服器

主機與 Docker 之間的網路關係

當 Docker 啟動時,他會建立一個虛擬的乙太網路橋接器(Virtual Ethernet Bridge),名稱預設是 docker0,如下圖。

docker0docker0

從上圖畫紅線的地方 172.17.0.1/16 說明 docker0 的 IP 位址是 172.17.0.1 橋接器的位址,Docker 在建立容器的時候,會在這個 RFC 1918 中所定義的私有網路範圍裡,也是我們俗稱的同一個 網段內隨機選用一個位址給容器使用。 使用這個橋接器,容器就可以和主機系統以及其他的容器做溝通。

主機與 Docker 網路的示意圖主機與 Docker 網路的示意圖

Docker 網路預設都能連到外部的世界

知道主機與 Docker 之間的網路關係後,接下來就是要來看更細節的地方,我們要列出主機的 NAT 規則,這個能清楚知道,主機與 Docker 之間的 Routing 是如何進行的。

關於 NAT 規則的顯示,會先需要懂一些詞彙,如果不懂網路的人,可以參考鳥哥的詞彙說明。

  • target:代表進行的動作, ACCEPT 是放行,而 REJECT 則是拒絕,此外,尚有 DROP (丟棄) 的項目!
  • prot (protocol):代表使用的封包協定,主要有 tcp, udp 及 icmp 三種封包格式;
  • opt:額外的選項說明
  • source:代表此規則是針對哪個『來源 IP』進行限制?
  • destination:代表此規則是針對哪個『目標 IP』進行限制?

執行 iptable 指令,查看主機的 NAT 規則是怎麼設置的

bash
  • bash
1
sudo iptables -t nat -L -n

NAT 規則NAT 規則

上圖說明 NAT 會跑的 Routing 順序

  1. 首先跑 Chain PREROUTING ,該 Routing 規則是說明了任何來源 IP 跟 prot (source 0.0.0.0/0) ,都可以送到任何目的地 IP 跟 port (destination 0.0.0.0/0),意思是說任何人都可以通過這條 Routing 規則,不會被擋住。 其 target 是 DOCKER,所以這條跑完,會跑下一條 Chain DOCKER
  2. Chain DOCKER 執行的設定規則跟第一條一樣
  3. Chain POSTROUTING 說明了只要來源是 172.17.0.0/16 這個網段的 IP (也是 Docker 建立的網段),都可以允許被送到任何目的地。

這代表什麼意思呢? Docker 預設建立好的網路,預設情況下都可以連線到外部,例如,你在 alpine 容器裡面執行 curl www.google.com.tw 是可以成功得到 google 的回應。

從外部存取容器

從上一段說明了 NAT 配置,可以知道容器預設是沒辦法從外部存取的。 Docker 提供了一個優雅的解決方案,選擇性地提供外界與在容器中執行的服務之間的通訊。 在 docker run 指令下加入 --publish, -p 的參數,這能把指定的容器連接埠(port)發佈到主機。

讓我們看一下,執行一個容器,並且加上 -p 8088:80 參數後的 NAT 規則會怎樣。 請執行下面三行指令。

bash
  • bash
1
2
3
docker run -d --name alpine -p 8088:80 alpine tail -f /dev/null
docker exec alpine ip addr show eth0
sudo iptables -t nat -L -n

NAT 規則NAT 規則

從上圖畫紅線的地方可以看出來,容器的 IP 是 172.17.0.2,接著是 Chain DOCKER 的 DNAT 那條規則,這條規則是說明當有來源是存取 port 8088 時,會轉到 172.17.0.2:80 的容器位址。

透過這個方式我們就可以輕鬆的從外部存取容器內的資源。 而 Docker 還有其他方式能夠讓我們存取到容器的資源,例如 Host 模式能夠將容器的網路加入到主機的網路堆疊中,這樣連 --publish, -p 參數都不用下,就可以直接存取到容器的資源,但是這樣做還是會有缺點的,詳細說明可以參考這篇文章 Docker Networking 101 – Host mode

小結

老實說在研究 Docker 預設網路配置的時候,讀了很多網路的相關名詞,才看懂 Docker 網路的預設配置,希望這篇文章有幫助到閱讀的人有稍微了解到 Docker 預設網路配置,如果有任何問題或需要更正的地方歡迎在下方留言。

延伸閱讀

[Docker Networking 101 – The defaults]
[Docker Networking 101 – Host mode]
[IPtables中SNAT和MASQUERADE的區別]
[IPTables-Linux Firewall]
[Linux防火牆iptables基本使用方法]
[10分鐘學會IPTables]
[Stack overflow - How to retain docker alpine container after “exit” is used?]
[鳥哥的 Linux 私房菜 - 第九章、防火牆與 NAT 伺服器]