Windows Container 的網路有時候會遇到一些奇怪的現象,尤其是在我這台筆電,因為實在安裝過太多版本,作業系統也升級好幾次,有時候為了演講也要調整本機網路設定,所以設定很亂也應該是正常的。這幾天我就發現升級到 Windows 10 版本 1903 之後,網路功能出了一點狀況,這次撰文的過程也徹底將 docker networking 做了深入的研究與理解,收穫頗豐!
執行環境
Microsoft Windows 10 [版本 10.0.18362.116]
Docker Desktop Community Version 2.0.0.3 (31259)
(Channel: Stable)
問題描述
我的筆電自從升級到 Windows 10 (版本 1903) 之後,Docker 的網路設定變得有點奇怪,狀況如下:
-
執行 Process 隔離模式時,網路一切正常,可以透過本地迴路進行連線 (這是 Windows 10 1809 之後才有的功能)
docker run -d --rm -p 80:80 --isolation process mcr.microsoft.com/windows/servercore/iis:windowsservercore-1903
-
執行 Hyper-V 隔離模式時,容器網路是正常的,但是卻無法透過本地迴路進行連線!
docker run -d --rm -p 80:80 --isolation hyperv mcr.microsoft.com/windows/servercore/iis:windowsservercore-1903
容器可以正常啟動,雖然我加上了 -p 80:80
參數,但是透過 http://localhost
是無法連上的,很明顯的 nat 網路設定出了問題。
解決過程
我的筆電距離上次重灌已經過了一段時間,歷經了幾代的 Docker Desktop 版本,又歷經了兩次 Windows 10 版本升級,設定混亂也是正常的事。照理說這個問題,很簡單:「只要重新安裝作業系統就能解決」。這個解決方案我當然知道,但是重灌電腦曠日廢時;另一方面來說,如果我選擇了重灌,就真的無法了解技術原理了。因此,我選擇跟他奮戰一個下午外加一個晚上,最後終於解決問題,過程中也學習到不少 Docker Networking 與 Hyper-V 的網路知識。
由於這個問題非常詭異,容器網路其實並沒有問題,只有本地迴路(loopback)連不上而已。況且,在 Process isolation 模式下,本地迴路的網路也是正常的,只有在 Hyper-V 隔離模式下才出現問題。這種鬼打牆的情況之前沒遇過,所以完全不知道從哪裡著手修復,只好亂槍打鳥,只能傻傻的嘗試各種可能。
-
Restart Docker Desktop (重新啟動)
無效!
-
Reset to factory defaults (回復出廠設定)
無效!
-
Reinstall Docker Desktop (重新安裝)
How to completely remove Docker in Windows 10
Docker Desktop for Windows - Docker Hub
無效!
-
完整移除並重新安裝 Hyper-V
Disable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-All,Containers
Restart-Computer
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V,Containers -All
Restart-Computer
無效!
我甚至於用了 WindowsContainerNetworking-LoggingAndCleanupAide.ps1 指令檔來清除網路狀態,不過一樣無效!
-
比對兩台不同電腦的 Docker 設定
我直接到 Azure 建立一台 D2S_v3 的虛擬機器 (支援 Nested Virtualization 功能),並將 Docker 安裝好,我發現新電腦會自動在 Hyper-V 的 VSwitch 建立一組名為 nat
的虛擬交換器。如果有切換到 Linux container 的話,則會自動建立起一個名為 DockerNAT
的虛擬交換器。
接著我便手動在我的 Hyper-V 中手動新增一個 Internal 的 VSwitch,並且設定名稱為 nat
,結果還是完全沒有效果,本地迴路還是無法連線!
-
認真看完、看懂 Windows 容器網路功能 的所有重要知識
之前我只有看完這份文件,但卻沒有真正理解全部。這次遇到問題重看一遍,發現之前不太理解的地方突然懂了,因此也看的比較深入些。
Windows 支援可透過 Docker 來建立的五種不同網路驅動程式或模式:nat
、overlay
、transparent
、l2bridge
和 l2tunnel
。
底下這當圖片對我幫助很大,因為無論是 Windows Server Container 或 Hyper-V Container,每個容器都會使用一張虛擬網卡 (vNIC) 取得連線,而且該 vNIC 會連接到 Hyper-V 的虛擬交換器 (vSwitch) 來進行連線。這些虛擬網卡會由 Docker 自動建立與管理,照理來說你都不需要自己建立才對。
Docker 引擎第一次執行時,會建立預設 NAT 網路 nat
,它會使用內部 vSwitch 和名為 WinNAT
的 Windows 元件。你可以透過 docker network ls
發現 nat
網路的蹤跡。而 nat
網路是在 Windows 上執行的容器的預設網路。任何容器如果在 Windows 上執行,但沒有任何旗標或引數實作特定網路組態,其將會連接到預設的 nat
網路,且系統會自動從 nat
網路的內部首碼 IP 範圍指派 IP 位址給容器。 用於 nat
的預設內部 IP 首碼為 172.16.0.0/16
。
我這次的問題其實就出在 nat
網路,因為感覺上 Docker 並沒有幫我建立這個 vSwitch,下圖是我從 Azure VM 取得的截圖,這是一台全新的 Windows 10 安裝,環境最乾淨:
但是在我的筆電上,執行 docker network ls
會看見 nat
網路,但是從 Get-VMSwitch
就是看不到,非常詭異!
但因為這次問題,讓我徹底的認識了 主機網路服務 (HNS),原來這是 Windows Container 一個相當重要的服務,用來自動對 Hyper-V 建立虛擬交換器 (vSwitch),還有建立所需的 NAT 及 IP 集區!
我從完全乾淨的 VM 中,透過 hnsdiag list networks
命令,可以找出所有透過 HNS 管理的網路:
從我的筆電上,也可以透過 hnsdiag list networks
命令看見一樣的 nat
網路,但是我透過 Get-VMSwitch
就是看不到。也就是說,HNS 幫我建立了 nat
網路,但是 Hyper-V 卻沒有出現相對應的 nat
虛擬交換器,這真的太怪了!
我後來在我的筆電上,用 hnsdiag list networks 3705E6F2-9EEE-4C14-BF9B-721EE0A11548 -dl
命令,列出 HNS 所管理的 nat
網路的完整資訊,找出了 SwitchId
屬性,然後再透過 Get-VMSwitch | select Id,Name,SwitchType
命令取得所有 vSwitch 的 Id
欄位,這才發現原來 HNS 竟然自動幫我選了 Default Switch 作為 nat
網路的預設 vSwitch,為什麼會這樣呢?(抓頭)
備註: Default Switch 預設會由 Hyper-V 服務進行管理,如果找不到也會自動被建立,此虛擬交換器無法刪除,若要刪除 Default Switch 虛擬交換器,則必須移除 Hyper-V 才行。
-
嘗試移除用不到的虛擬網路介面 (vNIC)
由於無法理解為何 HNS 不會自動建立名為 nat
的虛擬交換器,反而改用 Default Switch 來進行連線。
此時我開始往 vNIC 著手研究,從 裝置管理員 (devmgmt.msc
) 中發現有好多張 Hyper-V Virtual Ethernet Adapter 虛擬網卡,我從 控制台 的 網路連線 也可以看到一堆不明用途的 Hyper-V 網路介面。其實這個問題我之前就有發現,只是無法理解為什麼會有這麼多虛擬網路介面。這次查看後發現,有一張網路介面的命名很詭異,叫做【Hyper-V Broken Virtual Ethernet Adapter】,另外還有兩個【Hyper-V Virtual Ethernet Adapter (nat)】與【Hyper-V Virtual Ethernet Adapter (nat 2)】的網路介面。
我直接將看不懂的 vNIC 全部移除,事實上我也有嘗試過把所有 Hyper-V Virtual Ethernet Adapter 虛擬網卡全數移除,反正 Hyper-V 會自動幫我重新建立。所以我連包含 Default Switch 字樣的 vNIC 也移除。結果,問題就修好了!因為當 HNS 看不到名為 nat
的 vSwitch,又找不到 Default Switch 的預設虛擬交換器,它就會自動幫我建立一組 nat
虛擬交換器(vSwitch) 與 vEthernet (nat)
虛擬網路介面(vNIC)。
不過,重開機之後,問題依舊存在,真的快崩潰!我從 Windows container networking 文件得知,原來 NAT 網路從 Windows Server 2019 (1809) 開始,每次重開機都會重建新的。
現在我的電腦,每次重開機,都要執行以下命令,先把 Docker 服務停止,然後將所有透過 HNS 管理的網路移除,然後重新啟動 docker 服務,停三秒之後,再執行 Get-VMSwitch | Select Id,Name,SwitchType
命令,這個命令會讓 Hyper-V 自動建立缺少的 Default Switch 虛擬交換器。這個執行的過程順序不能有錯,一定要先刪除所有 HNS 管理的網路,然後重啟 docker 服務,此時 Docker 才能透過 HNS 建立 nat
虛擬交換器,之後 Hyper-V 隔離模式下的 NAT 網路才會正常!
Stop-Service docker
Get-Service docker
Get-HnsNetwork | Remove-HnsNetwork
Start-Service docker
Start-sleep 3
Get-Service docker
Get-VMSwitch | Select Id,Name,SwitchType
你可以透過以下命令查詢網路設定值:
docker network ls
hnsdiag list networks -dl
Get-VMSwitch | Select Id,Name,SwitchType
不過,這不叫 解決方案 (Solution),這叫 應變措施 (Workaround) 而已,但礙於網路對於 HNS 的資源太少,根本找不到任何蛛絲馬跡!
-
嘗試建立獨立的 vSwitch、vNIC 與 docker network
New-VMSwitch -Name nat2 -SwitchType Internal
docker network create -d nat -o com.docker.network.windowsshim.interface='vEthernet (nat2)' nat2
docker run -d --rm -p 80:80 --network nat2 --isolation hyperv mcr.microsoft.com/windows/servercore/iis:windowsservercore-1903
如果是用這種方式建立網路,所有問題都可以被解決,唯一的麻煩之處就是每次執行 docker run
都要額外加上 --network nat2
選項,有點不方便!而且我也查了一下,看能不能將 docker run 都加入預設參數 --network nat2
選項,結果是沒辦法! Orz
-
全新的 Docker 啟動命令
由於我的筆電原本就設定 Docker 不會隨作業系統自動啟動服務,因此我大可另外寫一個 PowerShell 指令檔,用我自定的命令來啟動 Docker 服務,如此一來便可漂亮的解決我的問題。
Write-Host '移除所有 HNS 網路'
Get-HnsNetwork | Remove-HnsNetwork
Write-Host '啟動 Docker 伺服器'
&"C:\Program Files\Docker\Docker\Docker for Windows.exe"
Sleep 5
Write-Host '建立 Default Switch 虛擬交換器'
Get-VMSwitch | Select Id,Name,SwitchType | Out-Host
Write-Host '查詢 Docker 網路資訊'
Write-Host ''
docker network ls
Write-Host ''
Write-Host '查詢 HNS 網路資訊'
Write-Host ''
hnsdiag list networks
Sleep 5
疑惑待解
我回報了這個問題到 Docker for Windows 的官方 Repo,希望未來可以得到解答!
相關連結