這是一篇問題與答案的組合文章,是由我和 Gemini 對談的內容所構成。其實是有關一直以來我對於工作場合上遇到的,想不透的事情,所提出的問題。這些問題主要是針對 Docker 網路的基本概念,涉及的是 Macvlan 這個蠻重要的東西 (網路驅動程式)。
問:
我想問一個跟Docker有關的網路問題。
如果我想讓我的Docker Container 的IP設定為某一個固定IP,這個IP還不是 host 的IP,而可能是同一個 subnet 下的另一個IP。
我能在dockerfile中設定嗎?在 docker-compose file 中能設定嗎?
答:
這是一個在處理進階網路設定時很常遇到的需求。
先給你最直接的結論:你無法在
Dockerfile 中設定固定 IP,但你完全可以在 docker-compose.yml 中辦到。
這裡面涉及了 Docker 的設計邏輯,以下為你拆解為什麼、以及具體該怎麼設定。
1. 為什麼不能在 Dockerfile 中設定?
Dockerfile 的核心目的是用來建置映像檔(Build Image)。它定義的是環境、套件和應用程式的結構。
而 IP 位址屬於執行期(Runtime)的網路配置。映像檔被建置出來時,Docker 根本不知道它未來會在哪一台機器、哪一個網路環境下被啟動。如果把 IP 寫死在映像檔裡,這個映像檔就失去了移植性。因此,所有網路、IP 的指派,都必須在「容器啟動時」才進行。
2. 如何在 Docker Compose 中設定相同 Subnet 的固定 IP?
要讓容器擁有和 Host(宿主機)同一個網段(Subnet)的獨立 IP,最常見且標準的作法是使用 Docker 的 macvlan 網路驅動程式。
macvlan 會讓 Docker 容器直接連接到 Host 的實體網路卡,並為容器分配一個獨立的
MAC 位址和 IP。在區網(LAN)裡的其他設備看來,這個容器就像是一台獨立的實體電腦。
實作範例
以下是 docker-compose.yml 的設定方式:
YAML
version: '3.8'
services:
my_service:
image: nginx:latest
container_name: my_fixed_ip_container
networks:
my_lan_net:
# 在這裡指定你想要的固定 IP
ipv4_address: 192.168.1.100
networks:
my_lan_net:
driver: macvlan
driver_opts:
# parent 指的是你 Host 實體網路卡的名稱(例如 eth0 或 enp3s0)
# 請根據你實際的環境修改
parent: eth0
ipam:
config:
# 設定與你區網相同的網段與網關
- subnet: 192.168.1.0/24
gateway: 192.168.1.1
⚠️ 使用 macvlan 的關鍵注意事項:
- IP 衝突問題:
你指定的固定 IP(例如上面範例的 192.168.1.100)必須是實體網路中沒有人使用的 IP,且最好在你的路由器(DHCP 伺服器)中將該 IP 排除,以免未來被其他設備佔用導致衝突。
- Host 與 Container 預設無法直接通訊:
出於安全保護,Linux 核心預設不允許 Host 與其上的 macvlan 容器直接透過該網路通訊。如果你需要 Host 也能連到這個容器,通常需要在 Host 上額外建立一個 macvlan 網卡介面(轉發路由),或者透過傳統的 bridge 網路建立第二條通道。
如果你不需要跟 Host 實體網段一模一樣,只是想在 Docker 自建的虛擬內部網路中固定 IP,只要把 driver 改成 bridge(預設),並自訂一個虛擬的 subnet 即可,設定邏輯是一樣的!
問:
我的下一個問題是,所以,用 macvlan,可以設定和 host 不同 subnet 的IP囉?
答:
答案是:是的,完全可以。
使用 macvlan,你的容器絕對可以設定成和 Host 不同 Subnet 的 IP。
不過,這裡有一個非常重要的「網路物理限制」必須先釐清:這取決於你的實體網路環境(交換器/路由器)有沒有開通該網段。
我們可以把情況拆分成以下兩種情境:
情境 A:該 Subnet 在你的實體網路上「已經存在」(有 VLAN 劃分)
如果你的企業或實驗室環境中,實體網路早就劃分了多個網段(例如:Host 在 192.168.1.x,但網路上同時存在 10.0.0.x 的 VLAN),那麼你可以直接透過 macvlan 配合 VLAN 標籤(Tagging),讓容器加入另一個網段。
在 Docker Compose 中,你只需要把 parent 網卡後面加上點和 VLAN ID(例如 eth0.50 代表 VLAN 50):
YAML
networks:
my_different_net:
driver: macvlan
driver_opts:
parent: eth0.50 # 連接到 Host 的 eth0 網卡,但指定走 VLAN 50
ipam:
config:
- subnet: 10.0.0.0/24 # 與 Host 不同的 Subnet
gateway: 10.0.0.1
這樣做,容器就會拿到
10.0.0.x 的 IP,並且區網內其他在 VLAN 50 的實體設備都能直接連到它。
情境 B:該 Subnet 是你「憑空自訂」的(實體網路並不存在)
如果你只是隨意在 Docker 裡寫了一個外面世界根本不存在的 Subnet(例如 Host 在 192.168.1.x,你硬要把容器設為 11.22.33.44),結果會是:
- 容器在 Docker 內部確實會拿到這個 IP。
- 但因為外面的實體路由器、交換器根本不認識 11.22.33.0/24 這個網段,也沒有對應的網關(Gateway)幫它傳送封包,所以這個容器會完全斷網(無法連到外網,外面世界的設備也找不到它)。
💡 這階段的結論
macvlan 就像是拿一條實體網路線,把容器直接插到 Host 所在的同一個交換器(Switch)孔上。
- 只要那個交換器孔收得到、送得出那個不同 Subnet 的封包(通常透過 VLAN),容器就能正常運作。
- 如果只是瞎編一個外網不支援的 IP,容器就會變成網路孤島。
你可以想像成把一台實體筆電插到區網線路上,手動亂改一個不對的靜態 IP,結果是一樣的。
這樣有解開你對不同 Subnet 設定的疑惑嗎?我們接下來要往哪個方向推進?
問:
我懂。所以我想繼續問下去。
那麼 host 需要有兩個 RJ-45 的網路 port,然後其中一個設定到 Container 的那個 subnet 嗎?還是說可以不用,只需整個網路中的路由中當中有 Container 的那組 subnet 的 gateway 存在就可以?
答:
簡短精確的答案是:完全不需要兩個 RJ-45 實體網路孔!只需要實體網路(路由器)中有該 Subnet 的 Gateway 存在即可。
這就是網路虛擬化好玩的地方。下面為你說明為什麼單一網路孔就能辦到:
關鍵在於「單孔多 IP」與「VLAN 標籤」
在傳統網路技術中,一個實體 RJ-45 網路孔本來就能同時承載多個不同的 IP 位址、甚至多個不同的網段。這主要有兩種實現方式:
1. 區網內沒有切 VLAN(單一廣播域,多個 Subnet 混在一起)
如果你的實體路由器或 L3 交換器,在同一個實體線路上同時綁定了兩個網段(例如 192.168.1.1 和 10.0.0.1 都在同一個路由網口上),那麼 Host 的單一網路孔(eth0)會同時接收到這兩個網段的封包。
- 當你設定
Docker macvlan 指向 parent: eth0 時,容器發出的 10.0.0.x 封包就會順著這根線路送出去,實體路由器收到後,就能正確識別並透過 10.0.0.1 幫它轉發。
2. 區網內有切 VLAN(最標準、最常見的作法)
在企業環境中,通常會使用
802.1Q(VLAN Trunking)
技術。
也就是說,同一根實體網路線上,會同時流動著好幾個不同網段的封包,但每個封包上面都貼了一張「隱形的標籤」(例如標籤 10 代表 Host 網段,標籤 20 代表 Container 網段)。
這時,Host 的單一網路孔只要連接到交換器的
Trunk Port(允許通過多個 VLAN 的連接埠),Docker 就能透過剛才提到的 parent: eth0.20 寫法:
- 自動幫
Container 發出的封包貼上 VLAN 20 的標籤。
- 順著同一個網路孔送進實體交換器。
- 實體交換器和路由器看到 VLAN 20 的標籤,就會把它引導到對應的 Subnet Gateway。
💡 結論
Host 只需要一個 RJ-45 孔就夠了。
Host 自己的作業系統只管它原本的 Subnet(對它來說,它只看得到自己的 IP);而 Docker 則像是一台虛擬交換器,把同一個網路孔的頻寬分給 Container,並讓 Container 的封包直接與外面的實體 Gateway(路由器)對話。
只要網路基礎設施(路由器/交換器)那端已經把該 Subnet 的 Gateway 準備好,且網路孔連接的實體埠有放行該網段的流量,這一切就能順暢運作!
這樣有把這條網路線上的邏輯理清楚了嗎?
問:
VLAN 我懂。
答:
既然你熟悉 VLAN,那這整件事在 Linux 內部的本質,其實就完全等同於「Router-on-a-Stick」或是多重子介面(Subinterface)的概念。
在這種架構下,從網路層(Layer 3)的角度來看:
Host 的單一實體網卡(如
eth0):在實體交換器端被設定為 802.1Q Trunk Port。Linux 核心與 Docker:
當你在 Docker Compose 中指定
parent: eth0.50時,Linux 核心實際上是在內部幫你長出一個虛擬的子介面(Subinterface),並自動處理 Tagging 與 Untagging。Docker 的
macvlan驅動程式再把這個子介面橋接給 Container,並賦予它獨立的 MAC 位址。
所以,對於上層那台擁有各 Subnet Gateway 的三層交換器(L3 Switch)或路由器來說,它看到的就只是一條 Trunk 鏈路,裡面跑著不同 VLAN ID 的畫面。
這也是為什麼 Host 的 TCP/IP 堆疊完全不需要參與 Container 的路由,它不需要有兩個實體 Port,甚至 Host 自己的 IP 沒去設定那個 Container 的 Subnet 也完全沒關係。封包直接在二層(Layer 2)就被 macvlan 丟上實體線路,由外部的 Gateway 去做三層轉發。
問:
我再問一個Docker實務上的問題。我們在學Docker的時候,建立的image,曝露的 port,常常不是用 port 80,會把原本的 port 轉換到像 port 8080。
那麼,如果我使用 macvlan 這個 driver 後,我可以把曝露的 port 指向 port 80 嗎?
答:
這是一個非常棒且關鍵的實務問題!
答案是:完全可以,而且這正是使用
macvlan 的巨大優勢之一!你甚至不需要在 Docker 中做任何 Port 的「轉換(映射)」,直接讓容器監聽 Port 80 即可。
這背後的原因,跟我們一開始學 Docker 時常用的 bridge 模式有很大的本質差異。
為什麼我們以前常常需要換 Port(例如改用 8080)?
在預設的 bridge 模式下,所有的容器都是躲在 Host(宿主機)的背後,使用的是
NAT(網路位址轉換)
技術。
- 當多個容器都想使用
Port 80 時,因為它們共享同一個 Host 的實體 IP,Host 的 Port 80 只能給一個容器用。
- 為了避免衝突,我們必須在啟動時做 Port 映射(例如 -p 8080:80),意思是:「外面的人連到 Host 的 8080,Host 再透過 NAT 轉給容器的 80」。
為什麼用 macvlan 就可以直接用 Port 80?
誠如我們前面討論的,macvlan 讓容器在二層(Layer 2)直接拿到了自己獨立的 MAC 位址與獨立的區網 IP。
對於外面的實體網路來說,這個容器就是一台完全獨立的電腦/伺服器。它擁有自己專屬的 65535 個 Port。
- 沒有 Port 衝突問題: 就算 Host 本身已經佔用了
Port 80(例如 Host 裝了 Apache),你的 macvlan 容器依然可以大方地使用自己的 Port 80。因為對外來說,一個是 192.168.1.10:80(Host),另一個是 192.168.1.100:80(Container),它們的 IP 不同,Port 完全不會撞到。
- 不需要 -p
或 ports: 映射:
在 docker-compose.yml 使用 macvlan 時,你不需要寫 ports: - "80:80"。只要你的容器內部的網頁伺服器(如 Nginx/Apache)設定監聽 Port 80,外面的人直接打容器的固定 IP,就能直接連進 Port 80。
實務上的 Docker Compose 寫法對比
在 macvlan 架構下,你的 docker-compose.yml 服務設定會變得非常純粹,連 ports 區塊都不用寫:
YAML
services:
web_server:
image: nginx:latest
container_name: company_web
#
❌ 注意:使用 macvlan 時,不需要寫 ports 映射!
#
ports:
# - "80:80"
networks:
my_lan_net:
ipv4_address: 192.168.1.100 # 容器直接擁有這個 IP
外面同仁的電腦只要在瀏覽器輸入 [http://192.168.1.100](http://192.168.1.100)(預設就是 Port 80),流量就會直接直達容器,不會經過任何
Host 的 Port 轉換。
這對於想把 Docker 容器當作傳統實體機或 VM(虛擬機)來維運的 MIS 來說,管理直覺非常多!
問:
我想更仔細了解 macvlan。請問有關於講到它的書籍嗎?如果有電子書版本就更好了。我需要的是至少有一兩個章節專門 focus 在這個主題的。
答:
針對你需要深入研究 Docker 網路與 macvlan 原理的需求,有兩本非常經典、且高度符合你「專業網路背景」的書籍。這兩本書都有豐富的電子書(eBook)版本(如 Kindle、OReilly 平台、PDF、EPUB 等)。
以下為你推薦這兩本經典著作,並列出它們聚焦在 macvlan 的具體章節:
推薦 1:《Docker Networking Cookbook》
- 作者:
Jon Langemak
- 特點: 這本是目前市面上對網路底層技術挖得最深、最扎實的 Docker 專書。作者用實作(Recipe)的方式,從 Linux Kernel 的網路原語(Primitives)講起。對於有 CCNA 背景的人來說,這本讀起來會非常過癮,因為它直接呼應了你熟悉的網絡架構。
核心聚焦章節:
- Chapter 1: Linux Networking Constructs(強烈建議先讀這個熱身)
- 專門剖析
Linux 的 Network Namespaces、Bridges 以及虛擬網卡如何運作。
- Chapter 9: Exploring Network Features(你的核心目標)
- Understanding MacVLAN interfaces: 從 Linux 核心角度解釋什麼是
macvlan 子介面。
- Working with the Docker MacVLAN network driver: 實務展示
Docker 如何調用它。
- Understanding IPVLAN interfaces: 順便介紹了
macvlan 的兄弟技術 IPVLAN(L3 路由模式)。
- Tagging VLAN IDs with MacVLAN and IPVLAN networks: 這一節就是你最需要的!專門講如何結合 802.1Q 進行 VLAN Tagging。
推薦 2:《Docker Deep Dive》(中譯:Docker 深度導論)
- 作者:
Nigel Poulton
- 特點: 這是 Docker 領域被譽為「聖經」級的入門與進階全方位書籍,每年都會根據 Docker 官方的更新進行改版。它的文字風格比較生動,觀念的架構梳理得極為清晰,非常適合用來建立宏觀的架構圖景。
核心聚焦章節:
- The Container Network Model (CNM) & Network Drivers 相關章節(通常在書中的「Docker
Networking」大章節裡)
- 書中會用一整章的篇幅拆解 Docker 的內建網路驅動。
- 裡面有專門的獨立小節深入探討 Macvlan Networks,包含它的適用場景、它如何繞過標準 Bridge 帶來的效能損耗,以及什麼時候該用它、什麼時候該避免。
💡 閱讀建議
既然你有 CCNA 的底子:
- 先看 《Docker Deep
Dive》 的網路章節,快速抓出
Docker 的 Libnetwork / CNM (Container Network Model) 架構是怎麼把網路抽象化的。
- 接著直接把 《Docker
Networking Cookbook》
的 Chapter 9 當作操作手冊。它裡面的指令和實驗會讓你瞬間秒懂:「喔!原來 Docker 底層就是這樣在幫我下 Linux ip link 指令切 VLAN 的!」
這兩本書在技術社群或各大電子書平台都非常容易取得。你可以先從這兩本的指定章節看起,絕對能幫你建立起理論與實務基礎!
沒有留言:
張貼留言