Windows Server 2004之docker-compose啟動多組NAT網路後HNS服務CPU飆高且容器無法正常啟動
近期,我們有意將 Windows Container 升級至最新半年通道 Windows Server 2004,從 1709、1803、1809、1903、1909 一路被虐過來,總覺得會越走越輕鬆,結果沒有。前面,在解決了 VMTools 造成容器服務會被中斷的問題之後。馬上又碰到第二個問題。
問題說明
如果先啟動第一個 docker-compose 的服務,之後又啟第二個 docker-compose 服務,有很大的機會 docker-compose 在啟動過程會被卡住,然後整個就 docker-compose 卡死在那裡(最多有成功至第4個才卡住)。一開始覺得是 docker-compose 的問題,但由於是新主機,習慣性都會去 docker-compose 去下載最新版來使用。也有找了一下 compose 的 Issues,沒有看到類似的問題。從 Get-EventLog 去查詢,因為程式是那種卡住,並不是被中斷或 Crash,也沒有什麼日誌可以參考。
問題重現
這部分可以說花了我好多的時間,以下是最簡單問題重現方式。我們準備多組 docker-compose.yml。為了簡化問題,我們只使用官方映像檔,不去使用有額外封裝過的映像檔。
version: '3.8'
services:
iis8080:
image: "mcr.microsoft.com/windows/servercore/iis:windowsservercore-2004"
ports:
- 8080:80
restart: on-failure
version: '3.8'
services:
iis8081:
image: "mcr.microsoft.com/windows/servercore/iis:windowsservercore-2004"
ports:
- 8081:80
restart: on-failure
version: '3.8'
services:
aspnet9090:
image: "mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-2004"
ports:
- 9090:80
restart: on-failure
執行 docker-compose.exe up
或 docker-compose.exe up -d
都行。
在反覆測試的過程,看到一個不正常的現象。如下圖,在第4次(第三個執行個體)啟動時,發現建立容器但無法啟動的現象:
這個 HNS 服務的 CPU 使用率不斷被拉高。Windows Host Network Service (HNS,主機網路服務),簡單來說,我們 Docker 建立的網路與端點,最終還是要跟實體主機上進行對應,而這個虛實之間的對應工作就是 HNS 服務的內容。
無解
說真的,由於我們是採用最新的 Windows Server 2004 (OS Build 19041.450) 與 Windows Container 的組合,找遍了網路沒有看到任何案例。而 Windows Server 2004 剛推出時,有著不少 Issues 的風評,一項一項看著,也沒看到類似的 Issue 被回報。想說不會那麼賽吧,沒人回報的 Server 級 bug 也能讓我碰到。
NAT
將自己沉澱下來,細想著整個 Windows Server 與 Windows Container 運作流程,突然回想一篇參與過的 Github Issue 3076 討論串。Windows Server 官方在 Windows Server 2019 做了一個重大的改變,它們改變了 NAT 網路的行為。文件只簡單寫了一句:重新開機之後,在 Windows Server 2019 (或更新版本上建立的 NAT 網路將不再保存) 。
不要小看這句話,影響不小。沒它我還解不出來。
解法:One NAT
很明顯,Windows Server 對於這種多組 NAT 網路有一定的考量,重開機就讓它消失不見。事情不單純。不過我沒空查它。我要先解決的我的 Windows Container 啟動卡住的問題。
既然 Windows Server 2019 之後不希望這種"多組 NAT 網路"的出現,那麼是否讓所有容器都跑在預設 NAT 網路之下就好呢?
PS C:\> docker network ls
NETWORK ID NAME DRIVER SCOPE
ed8f896b69b0 nat nat local
cee682c39629 none null local
要讓 docker-compose 以預設的 nat 網路來啟動,也很簡單,參考文件加入以下組態到 docker-compose.yml:
networks:
default:
external:
name: "nat"
Yes,順利啟動多組 compose 的容器服務,一切又回到美麗的世界。一般而言,到這裡就可以結束了。
Redis HA
不過,那個"我"不要高興的太早!
在 Redis HA for Windows 介紹過,我為了讓 Redis HA 機制能正常在 Windows Container 運作,所以在 redis.config 的組態檔裡預先設計好網路架構。原本的想法是,我們依照 Docker Host 的 NAT 預設值重新修改組態與建置新映像檔。結果查了其他台 Docker Host 的 NAT 設定:
"Config": [
{
"Subnet": "172.29.16.0/20",
"Gateway": "172.29.16.1"
}
]
"Config": [
{
"Subnet": "172.31.112.0/20",
"Gateway": "172.31.112.1"
}
]
"Config": [
{
"Subnet": "172.25.224.0/20",
"Gateway": "172.25.224.1"
}
]
簡單說,每一台 Docker Host 的 NAT 的網段都不一樣。這樣根本就不能製作出通用的映像檔。怎麼辦呢?
統一 NAT 網段
山不轉,路再轉一次。那有無可能我們統一所有 Docker Host 的 NAT 網段,而且是我們想要的網段。查詢後,Yes,太棒了,兩者只要一行組態就能一次滿足。
"fixed-cidr": "10.10.0.0/20"
只要在 daemon.json 加入我們想要的網段組態。這裡要注意,Restart-Service docker
並不會讓組態生效,要進行重開機(Restart-Computer -Force
)才會生效。
Windows Container 能設的組態與 Linux Container 不一樣,細節請查詢 daemon.json for Windows 文件。
PS C:\> docker network inspect nat
[
{
"Name": "nat",
"Id": "ed8f896b69b0c029b8512ef20c27238001b7bc198d1f9a2374e98a267f70dd8c",
"Created": "2020-09-10T17:51:42.2092873+08:00",
"Scope": "local",
"Driver": "nat",
"EnableIPv6": false,
"IPAM": {
"Driver": "windows",
"Options": null,
"Config": [
{
"Subnet": "10.10.0.0/20",
"Gateway": "10.10.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"2231f2c8ca7c166ea3374f0f2a6f7b1d626540fd26a0616b164454f6f67e8849": {
"Name": "iis8080_iis8080_1",
"EndpointID": "a269731591ebc257a46c017a790fd3f0a7d1b993749b621f42205d64e41a886d",
"MacAddress": "00:15:5d:b9:08:db",
"IPv4Address": "10.10.9.66/8",
"IPv6Address": ""
},
"2eeeaa0d9586133dfb3c00d8599894cdce1e269e8af31c02630d04448ab4930b": {
"Name": "aspnet9090_aspnet9090_1",
"EndpointID": "6adb00dcd33177a9689d9f4797de26771c875b3af5009cc4c5b737bac8998313",
"MacAddress": "00:15:5d:b9:00:8f",
"IPv4Address": "10.10.4.219/8",
"IPv6Address": ""
},
"355df4c40180148b8211e2b2c3f2c06919681ce415e0ddd073c1606b02df6e2c": {
"Name": "redisha_master_1",
"EndpointID": "42a2ba34914e594512ad0b8eff130572dd3f9b845f3255e214fbc77417495dd6",
"MacAddress": "00:15:5d:b9:04:06",
"IPv4Address": "10.10.10.10/20",
"IPv6Address": ""
},
"918c28a67cca7071758adb616401dfc52d3edffd693bdc154640c4ac7a7a091d": {
"Name": "redisha_sentinel_1",
"EndpointID": "8a5690315a1b67cc0f890e93260f9e3d4f8b134c5ae84fc19024440fb79cf9f9",
"MacAddress": "00:15:5d:b9:03:13",
"IPv4Address": "10.10.4.169/8",
"IPv6Address": ""
},
"9d7910bf75cd5910e05055cc702477eeef2c52f42ddb39e742bd2d3c8ef09ae0": {
"Name": "nginx_nginx_1",
"EndpointID": "58eebbcac637b18a37c7767448c26dc6273df1ac67ed0fd2b5fb572e37f47f85",
"MacAddress": "00:15:5d:b9:06:31",
"IPv4Address": "10.10.3.222/8",
"IPv6Address": ""
},
"f7bb57ce581abc97ed96537dd45603774a6fbadb1dc9cb0957d874e71f1fd956": {
"Name": "redisha_slave_1",
"EndpointID": "d41eb4f813a0331e184c3e8f12d4b52f235fe44fdf13ee7537840952561e859d",
"MacAddress": "00:15:5d:b9:00:34",
"IPv4Address": "10.10.8.210/8",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.windowsshim.hnsid": "40703147-2B99-4CFD-BA32-5F060F66EFAD",
"com.docker.network.windowsshim.networkname": "nat"
},
"Labels": {}
}
]
預設 Redis HA 的 docker-compose.yml 只改吃預設 NAT 網路,其他完全不變。我們可以看到 redisha_master_1
順利的拿到我們希望的 "10.10.10.10" 這組 IP 位置。
小結
要小心 One NAT 這件事,因為現在大家都在同一網段之內,所以容器互相是可見的,用 docker exec
進去隨便一個容器內 ping
一下其他的 Container Name ,就能驗證。也就是,原本的網路獨立這件事在 One NAT 架構下給取捨掉了。
這段山路有夠不好走。前前後後追了二週以上(中間有去忙其他事)。想放棄,又出現一絲絲希望,又卡關,想放棄,又出現一絲絲希望,又卡關,想放棄,又出現一絲絲希望,又卡關。但總算有個好結果,有走到山頭。
謝謝,鬼打牆二天,這篇救了我。
回覆刪除