2026年5月20日 星期三

macvlan 的介紹

 這是一篇問題與答案的組合文章,是由我和 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 的關鍵注意事項:

  1. IP 衝突問題: 你指定的固定 IP(例如上面範例的 192.168.1.100)必須是實體網路中沒有人使用 IP,且最好在你的路由器(DHCP 伺服器)中將該 IP 排除,以免未來被其他設備佔用導致衝突。
  2. 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.1QVLAN Trunking 技術。

也就是說,同一根實體網路線上,會同時流動著好幾個不同網段的封包,但每個封包上面都貼了一張「隱形的標籤」(例如標籤 10 代表 Host 網段,標籤 20 代表 Container 網段)。

這時,Host 的單一網路孔只要連接到交換器的 Trunk Port(允許通過多個 VLAN 的連接埠),Docker 就能透過剛才提到的 parent: eth0.20 寫法:

  1. 自動幫 Container 發出的封包貼上 VLAN 20 的標籤。
  2. 順著同一個網路孔送進實體交換器。
  3. 實體交換器和路由器看到 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)的角度來看:

  1. Host 的單一實體網卡(如 eth0:在實體交換器端被設定為 802.1Q Trunk Port

  2. 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 的實體 IPHost Port 80 只能給一個容器用。
  • 為了避免衝突,我們必須在啟動時做 Port 映射(例如 -p 8080:80),意思是:「外面的人連到 Host 8080Host 再透過 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:80Host),另一個是 192.168.1.100:80Container),它們的 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)版本(如 KindleOReilly 平台、PDFEPUB 等)。

以下為你推薦這兩本經典著作,並列出它們聚焦在 macvlan 的具體章節:

推薦 1:《Docker Networking Cookbook

  • 作者: Jon Langemak
  • 特點: 這本是目前市面上對網路底層技術挖得最深、最扎實 Docker 專書。作者用實作(Recipe)的方式,從 Linux Kernel 的網路原語(Primitives)講起。對於有 CCNA 背景的人來說,這本讀起來會非常過癮,因為它直接呼應了你熟悉的網絡架構。

核心聚焦章節:

  • Chapter 1: Linux Networking Constructs(強烈建議先讀這個熱身)
    • 專門剖析 Linux Network NamespacesBridges 以及虛擬網卡如何運作。
  • Chapter 9: Exploring Network Features(你的核心目標)
    • Understanding MacVLAN interfaces Linux 核心角度解釋什麼是 macvlan 子介面。
    • Working with the Docker MacVLAN network driver 實務展示 Docker 如何調用它。
    • Understanding IPVLAN interfaces 順便介紹了 macvlan 的兄弟技術 IPVLANL3 路由模式)。
    • 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 的底子:

  1. 先看 Docker Deep Dive 的網路章節,快速抓出 Docker Libnetwork / CNM (Container Network Model) 架構是怎麼把網路抽象化的。
  2. 接著直接把 Docker Networking Cookbook Chapter 9 當作操作手冊。它裡面的指令和實驗會讓你瞬間秒懂:「喔!原來 Docker 底層就是這樣在幫我下 Linux ip link 指令切 VLAN 的!」

這兩本書在技術社群或各大電子書平台都非常容易取得。你可以先從這兩本的指定章節看起,絕對能幫你建立起理論與實務基礎!

 

我的備註:

上面說的兩本書,都可以在 Packt Publishing 買到,後一本一年改一版,很便宜。


沒有留言:

張貼留言

macvlan 的介紹

 這是一篇問題與答案的組合文章,是由我和 Gemini 對談的內容所構成。其實是有關一直以來我對於工作場合上遇到的,想不透的事情,所提出的問題。這些問題主要是針對 Docker 網路的基本概念,涉及的是 Macvlan 這個蠻重要的東西 (網路驅動程式)。