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.1Q(VLAN 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 的實體 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 的底子:

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

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

 

我的備註:

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


沒有留言:

張貼留言

Prompt Engineering(提示工程)與 Token 節省策略

 問: 如果我請 agy 處理一件看起來很複雜應該很耗費 token 的工作,我是不是在做之前,先請 agy 幫我分析一下,是不是有比較省 token 的方法,再由它建議的方法,分批讓它做,這樣可以省下較多的token,我這樣想對不對? 答: 你的思路完全正確,這是一種非常聰明且...