1. 程式人生 > >細說Windows與Docker之間的趣事

細說Windows與Docker之間的趣事

眾所周知,Docker能打通開發和運維的任督二脈,所謂DevOps是也。有朋友說,這符合王陽明的"知行合一"之教。

而Windows Server 2016內建的Windows Docker亦已經出來一段時間,這裡就來和諸公彙報一下測試結果。

Linux和Windows,Docker裡各有多少程序
在安裝配置Container Host的時候,經常報錯Container OS Image下載失敗(沒辦法,牆內的緣故)。
什麼是Container OS?顧名思義,是從容器角度看到的OS。
Container OS實際是應用所依賴的使用者模式(User mode)OS元件,對於Windows容器來說,例如ntdll.dll、kernel32.dll或者coresystem.dll之類的System DLL。主機上的所有容器共享核心模式(Kernel mode)OS元件,對於Windows,就是ntoskrnl.exe,還有驅動等。

例如對於以下命令,意味著Windows系統從docker映像中獲取Windows Server Core的使用者模式OS元件,並啟動cmd獲得Shell。

docker run -it windowsservercore cmd
Linux也是一理,如果執行以下命令,意味著從docker映像中獲取Ubuntu的使用者模式元件,並且啟動Bash Shell。
docker run -it ubuntu /bin/bash

對於以上兩個容器,Linux容器裡的程序比較少,可以參考以下截圖:

而Windows容器,則情況略有不同。

在Windows主機上啟動Process Explorer,可以看到這個Windows容器的程序相對多一些:

這是因為在Windows系統中,需要給應用提供一些使用者模式的系統服務,例如DNS、DHCP、RPC等服務,這樣從容器的角度來看,容器獲得了自己獨有的服務(一般是在各自的svchost或者其他服務宿主程序裡執行),構成了所謂的Container OS。

我們可以用PowerShell命令檢視容器內部啟動的Windows服務,大概有27個,參考附圖。

很可惜,這個版本的Windows docker裡,雖然有遠端桌面服務,但是目前還不支援遠端桌面到容器,所以無法使用容器應用的圖形化介面。

容器裡的應用,到底應該啟動多少Windows服務?由於Windows服務的具體作用是非文件化的,所以不像Linux可以做到最精簡。但是由於這些服務幾乎不佔用什麼額外的資源,對於容器效能沒有影響。

Windows容器的程序如何隔離

由於在最新的測試版本里,容器物件的許可權設定有了改變,只有SYSTEM許可權才能檢視。所以要檢視Windows容器的程序隔離,需要用SYSTEM許可權啟動Winobj。這可以藉助Psexec來實現:

Psexec -i -d -s winobj.exe

可以看到Windows物件空間裡多了一個Containers的節點,其下有若干個GUID分支,這些GUID代表系統裡的容器。其下每個容器有自己獨立的BaseNamedObjects等名稱空間,包括互斥訊號量、記憶體Section、事件等。

可以用PowerShell檢視容器的GUID,參考附圖。

每個容器節點下,有自己的Session分支,例如該容器,佔據了Windows系統的Session 2。如附圖所示。

這就是為什麼,不管用工作管理員,還是PowerShell,抑或是Process Explorer等工具,我們都在Windows主機裡看到容器裡的所有程序都會標記Session為2。

藉助Process Explorer,我們可以看到容器裡的程序,所開啟的Handle,其中就指向先前所看到的Windows容器物件名稱空間。

同時還能看到,容器程序所在的WindowStation並不是WinSta0,而是Service-0x0-3e7$,3e7的10進位制等於999,等於九五之尊,這是SYSTEM服務所在的視窗站。所以容器程序無法在Windows桌面上擁有圖形化介面。

還可以檢視一個有意義的物件,Windows容器所掛載的主機目錄,類似於Linux容器的Volume。

Windows容器的檔案系統如何隔離

和Linux一樣,Windows容器映像採用分層的檔案系統,基於映像建立容器後,相當於在只讀的分層檔案系統上再覆蓋一層可讀寫的檔案系統層。如果要修改的檔案在最上層的可讀寫層裡沒有,則沿著分層的Layer找到目標檔案後,將其用COW(Copy on write:寫時複製)複製到可讀寫層再修改。

讓我們進入到Windows主機的以下目錄:

C:\ProgramData\Microsoft\Windows\Hyper-V\Containers

該目錄下列出所有通過PowerShell命令建立的容器檔案。其下有資料夾和檔案,都以容器的GUID來命名。

其中的926A300B-ACB7-4B28-9D86-45BF82C1211C.vhdx就是該容器的最上層的可讀寫層,是一個VHDX檔案。

記住該可讀寫層並不是一個完整的檔案系統,它需要和Image的現有檔案系統組成Union File System。如果嘗試雙擊該VHDX(只能嘗試掛載停止狀態的容器VHDX),試圖掛載到Windows系統,會彈出以下報錯資訊,提示該虛擬硬碟無法掛載。

Image的檔案系統位於以下路徑(Windows Server Core的Container OS檔案):

C:\ProgramData\Microsoft\Windows\Images\CN=Microsoft_WindowsServerCore_10.0.10586.0\Files

如果用Process Explorer檢視容器程序訪問的Dll,可以看到其訪問的路徑為Container OS檔案。

如果是用docker命令建立的程序,道理類似,但是其可讀寫層檔案系統位於以下路徑:

C:\ProgramData\docker\windowsfilter

Windows容器還有登錄檔

和Linux不一樣,Windows容器還需要考慮登錄檔的隔離問題。和檔案系統名稱空間隔離一樣,登錄檔名稱空間隔離也採用類似Union FS形式。

下面讓我們進入PowerShell命令建立的Windows容器資料夾內部。

C:\ProgramData\Microsoft\Windows\Hyper-V\Containers\926A300B-ACB7-4B28-9D86-45BF82C1211C\Hives

在這個Hives資料夾下方,有很多命名為*_Delta的檔案,這是容器所訪問的登錄檔配置單元檔案。

從命名方式中可以看到,容器的登錄檔和檔案系統一樣,也採用分層架構,最上層的是可讀寫的登錄檔名稱空間。而Image映像也有隻讀部分的登錄檔空間,路徑如下。

C:\ProgramData\Microsoft\Windows\Images\CN=Microsoft_WindowsServerCore_10.0.10586.0\Hives

在Process Explorer裡可以看到可讀寫層、只讀層登錄檔合併後所載入的內容。

Docker命令所建立的容器,方法類似,位於類似以下路徑:

Windows容器的資源限制

大家知道,Docker可以呼叫CGroup技術來限制Linux容器的CPU、記憶體等資源佔用。而在Windows容器裡,記憶體資源的限制,則是通過Windows的JO(作業物件)技術來實現。

可以參考以下技術來限定Windows容器的CPU、記憶體和磁碟IO。例如可以將容器的記憶體限定為最大佔用為5GB。

https://msdn.microsoft.com/en-us/virtualization/windowscontainers/management/manage_resources?f=255&MSPPError=-2147217396

然後用Process Explorer開啟任意一個容器程序的屬性對話方塊,切換到Job標籤頁。

可以看到所有容器程序共享一個作業物件,而且該作業物件的記憶體限額(Job Memory Limit)為5GB。

作者簡介

彭愛華 網名盆盆,微軟混合雲技術顧問。11屆微軟最有價值專家(MVP),微軟高階認證講師。

出版過近20本技術圖書,2005年建立ITECN部落格(09年全國IT部落格五十強)。

微信公眾號(華來四笑侃Windows):sysinternal。

曾獲得微軟技術大會TechEd最佳講師、中國首屆IT管理技術大會(2009)最佳講師的光榮稱號。