Windows Docker第一時間揭祕-盆盆跑微軟兩會

【編者的話】這是盆盆談微軟兩會(Build/Iginte)系列之一。文章引用孫建波老師關於Linux內核的6大命名空間隔離,看Windows Docker如何實現類似隔離,同時又有哪些不同。文章素材取自Build和Ignite大會視頻,但主要展示盆盆自己的分析和研究,還望大家指正爲謝。


今天在微信羣裏聽一位兄弟提到,Docker能將DevOps(意即"開發"和"運維")整合在一起,暗合王陽明先生的“知行合一”之教,這真是一種有趣的說法。


話說從頭,盆盆在《Windows Dcoker深入原理分析》裏曾經提到Build大會後,您可以在華來四公衆號裏回覆docker8,閱讀這篇文章(可以在微信裏搜索並關注公衆號:sysinternal),會第一時間給大家介紹Windows Docker技術。


還沒看過Build在線視頻的朋友,您可以泡杯咖啡,帶上耳機,靜靜地欣賞以下由Taylor Brown主講的Windows Docker講座,我們的文章就以此爲藍本。這裏需要注意的是,以下的論述,大多是盆盆根據Taylor的demo效果所做的推論,並不是Taylor本人的陳述,所以並不一定正確。如有問題,還望大家多

多指點哈。

http://channel9.msdn.com/Events/Build/2015/2-704


容器即隔離


拿大家熟悉的Linux Docker來看,其涉及到Linux內核所提供的Namespace隔離技術和資源控制的CGroup技術。


這裏推薦大家閱讀浙大SEL研究生孫建波老師的文章《Docker背後的內核知識——Namespace資源隔離》:

http://www.infoq.com/cn/articles/docker-kernel-knowledge-namespace-resource-isolation?from=timeline&isappinstalled=0


孫建波老師提到了一張表格,其中列出了Linux內核所支持的6種隔離:曰主機名、曰IPC、曰進程ID、曰網絡、曰文件系統、曰賬號。


儘管Windows內核實現和Linux不同,但是兩者還是有不少可比擬處。所以盆盆根據Build大會上Mark Russinovich這位大神以及Taylor Brown的講座,來條分縷析。


文件系統隔離


先來看看浙大SEL另一位大牛孫宏亮老師的文章《Docker源碼分析(九):Docker鏡像 》。這篇文章清晰地描述了Linux Docker的文件系統隔離,多層的可疊加文件系統。

http://blog.daocloud.io/docker-source-code-analysis-part9/


孫宏亮老師指出:假設我們下拉了Ubuntu:14.04映像,並通過命令docker run –it ubuntu:14.04 /bin/bash將其啓動運行。則Docker爲其創建的rootfs以及容器可讀寫的文件系統參見下圖。從容器的視角來看,雖然只有一個邏輯的完整文件系統,但該文件系統由“2層”組成,分別爲讀寫文件系統和只讀文件系統(按只讀層還可以再邏輯分層,所以極大地節省磁盤空間)。

wKiom1Vhv2jzEwo1AACi-9Fcksw515.jpg


Windows Docker同樣如此,頂層的沙盒層(sandbox layer)是可讀寫的,只允許該容器自己佔用,而其他層則是隻讀的,可供不同容器共享。在下圖中,底層的基礎OS層和中間的應用程序框架層都是隻讀的,而頂層的沙盒層則可讀寫,在容器的視角看來,它獨佔了完整的OS。這有點類似於Hyper-V的差異磁盤鏈(頂部的子盤才能讀寫,其上方的所有父盤和Base盤都是隻讀的)。

wKioL1VhwQOxUWXpAAGdUvZCbHE961.jpg


爲了說明文件系統隔離的魔力,Taylor得意地在Windows Container裏執行刪除C盤根目錄下所有文件和註冊表鍵值,儘管這個容器被毀了,但是根本不會影響其他容器,更不會影響主機。

wKiom1Vhv4bw0Y-SAAHdz1pYCIc531.jpg


盆盆猜測,在容器的視角里,如果只是讀取一個文件,該文件在最頂端的沙盒層裏只有重解析點(reparse point);只有在修改該文件時,纔會用copy-on-writer的方法從下方的只讀層中把文件內容複製到可讀寫的沙盒層。


創建Windows Container


視頻裏演示了一個很棒的demo。有一段簡單的代碼,在console裏顯示“This is a pretty cool app”。

wKioL1VhwSGy-eYpAADZmNs2ZaA806.jpg


這是用來構建Windows Docker映像的Dockerfile。這個文件由以下4行命令組成:

  • 第1行:表明該映像基於windowsservercore這個Base映像,可以將其理解爲rootfs

  • 第2行:表示工作目錄是C盤根目錄

  • 第3行:將應用目錄複製到容器映像裏

  • 第4行:啓動該應用程序

wKiom1Vhv6fBToHaAAClhkuH2W8974.jpg


很快用docker build命令將其構建爲容器映像,Tag是1。從命令結果中可以看到Dockerfile裏的每個命令都被執行,在執行第4個命令時,會生成一個臨時的容器。

wKioL1VhwUGDwnqOAAG9Z1-E6SY488.jpg


Docker映像構建完成後,只需運行docker run命令即可快速啓動該映像,併成功顯示"This is a pretty cool app..."。

wKiom1Vhv7-wh6I7AADveoCZYAM167.jpg


通過使用docker history命令,我們可以查看容器映像的構建歷史,這甚至可以用來逆向生成Dockerfile。例如視頻裏演示了sysinternals這個映像的構建歷史。從中我們可以看到:首先記錄維護人員是誰;然後指定工作目錄是C盤根目錄;再者是將sysinternals suite這個工具軟件目錄拷貝到容器映像裏;然後設定容器的運行賬戶;最後設置啓動Ipconfig以便顯示容器的IP地址。

wKioL1VhwVnicR6nAADvz1IJEyE289.jpg


IPC隔離


和Linux Docker Container一樣,Windows Server Container也採用IPC隔離機制。這實際上是利用Windows自己的session隔離機制。


會話(session)隔離機制,最初是用在Windows終端服務和快速用戶切換中。但是從Windows Vista開始,Windows也採用這種技術對系統會話進行隔離,Windows系統的服務和進程會佔用原來的控制檯會話(會話0),而後續的用戶會依次使用會話1、會話2等等。


從《Windows Internals》裏我們可以學習到,不同會話裏的應用,不能夠發送窗口消息(Window Message,以防止粉碎***)。不同會話裏擁有不同的對象命名空間,例如不同容器,有自己獨立的BaseNamedObjects目錄,包含事件、互斥信號和內存段等對象。這樣不同容器在同一個Windows主機上訪問同一個命名對象,就不會導致衝突。以下的WinObj截圖雖然不是取自Windows Docker系統,但是道理是一樣的。

wKiom1Vhv9qDwdwxAAJdagMAcS4426.jpg


有興趣的朋友可以參考盆盆在9年前發表在ITECN博客上的文章,介紹會話隔離技術:

http://blogs.itecn.net/blogs/winvista/archive/2006/06/09/SrvSession0.aspx


視頻裏演示了Docker容器的會話隔離能力,Taylor啓動了兩個容器,都是從同一個windowsservercore映像裏創建出來。其中一個容器,可以運行Tasklist命令,看到該容器運行在會話14中。而且還能看到兩個系統進程,一個是System進程,代表操作系統本身,另一個是空閒進程,這兩個進程都運行在會話0裏。

wKioL1VhwXHysexjAAGpKjhgR5o804.jpg


在同一個映像所創建的另一個容器裏,我們可以看到該容器運行在會話15中,同樣可以看到System和空閒進程。Taylor的解釋是由於容器是共享Windows Kernel的,所以容易可以看到System進程的PID是一樣的,都是4。其實在所有Windows主機上,System進程的PID都是4。

wKiom1Vhv-_zB9D_AAHSSL_-Hw4832.jpg


網絡隔離+圖形化訪問


和Linux容器一樣,Windows容器也可以有自己的獨立網絡配置。

wKioL1VhwZHhfeLzAAE29cREuQ8686.jpg


由於傳統的Windows應用大多是有GUI的,所以這些應用可能需要通過圖形化方式進行遠程操控。


視頻裏Taylor舉了一個sysinternals容器的例子。有趣的是,這個demo本來是Mark Russsinovich的保留曲目,可惜Keynote的時間十分寶貴,最Mark沒能有足夠的時間去演示。


Taylor演示啓動Sysinternals Suite裏的經典工具Process Explorer,由於該工具帶GUI,所以雖然進程已經在容器裏啓動,但是在Docker Client裏無法直接遠程顯示。


如何才能正常顯示呢?Taylor用了一個CC命令,直接連接到該容器的IP地址。從demo裏我們可以看出,實際上這個CC命令連接到容器的RDP服務上,這樣就相當於直接通過終端服務連接到容器裏的會話裏。

wKioL1VhwbLzEHx7AAI9V74brwI268.jpg


盆盆在《Windows Dcoker深入原理分析》裏曾經提到Windows Docker的前身DrawBridge在其沙盒裏實現了RDP服務(請在華來四公衆號裏回覆docker8,閱讀這篇文章),Windows Docker的原理應該類似。

wKiom1VhwDCBV5zVAADl8b8wTpI349.jpg


Linux Docker也能訪問圖形化界面,在以下的這篇文章《在 Docker 中運行 OpenOffice》裏介紹,只需在Dockerfiles裏添加安裝X Server的命令,就能借助VNC客戶端連接到OpenOffice圖形化界面。

http://linux.cn/article-5305-weibo.html


PID隔離


在Linux容器裏,容器裏的PID有自己的獨立命名空間。從演示的情況來看,Windows容器的PID隔離方法看上去略有不同。


在前面IPC隔離一節,我們可以通過Tasklist命令確認不同的容器,其CSRSS、Lsass、SVCHOST等系統進程的PID有所不同,可見彼此之間是完全隔離的。


那麼從宿主機的角度來看呢?


我們可以看Process Explorer的例子(該例子是在Ignite大會上由Taylor所演示的),由於Process Explorer是在終端會話裏打開的,所以我們可以在容器的任務管理器裏看到有兩個會話

  • 14是Docker客戶端訪問的會話

  • 而15則是通過RDP訪問的會話


可以看到Process Explorer的進程有兩個版本。顯然,會話14是Taylor在Docker客戶端裏運行的結果(但是我們無法看到圖形化界面),而會話15則是RDP訪問的結果。


兩者的運行賬戶不一樣,RDP登錄的運行身份爲Administrator(應該和Docker History一致),而會話14則是System賬戶。


以下是容器裏的任務管理器。

wKioL1VhwciCfYlMAAIjFougUmY378.jpg


這個任務管理器是從容器的角度來看的,我們可以記下其中的若干SVCHOST進程的PID。接下來我們打開宿主機的任務管理器,從全局的角度來查看。可以發現,從宿主機的角度來看,能看到每個容器裏的進程,其PID和容器裏面的版本是一樣的。

wKiom1VhwFfSjakCAAJ8PBbciQ4316.jpg


用戶賬戶隔離


Linux內核擁有賬戶隔離能力,可以讓容器裏的進程以root身份運行,但是在宿主機上,該賬戶實際上是普通用戶權限。


在Taylor的這個演示中,我們也能“察覺”到一些蛛絲馬跡。在PID隔離一節的兩個任務管理器截圖裏,容器裏的版本可以看到Process Explorer的運行賬戶爲Administrator,但是從宿主機上查看,該賬戶爲空。所以Windows容器有可能也實現了類似的賬戶隔離技術。


計算機名和域名隔離


Windows容器擁有自己的計算機名和域名隔離能力,這樣在網絡上,Windows容器看上去類似於一臺獨立的虛擬機。


不過由於共享內核,所以Windows容器目前應該不支持活動目錄,畢竟同一臺宿主機上所有容器應該都具有同一個SID,這樣就無法加域(無法驗證計算機賬戶)。


盆盆推測,到了Windows容器時代,類似於活動目錄這類比較笨重的驗證協議可能會逐漸退出歷史舞臺。畢竟活動目錄需要開放那麼多端口,需要藉助ADFS等手段才能穿透Internet。

不過Windows容器的驗證並不會存在問題,Azure AD和證書等都是很合適的辦法。


容器的優勢


容器非常適合開發的快速迭代、快速回滾。Taylor做了一個簡單的演示,對前面所述的代碼進行修改,調用私有的msvcr120.dll文件裏的_snwprintf函數。

wKioL1VhwhnwYqW_AAC5W_oOXgE378.jpg


但是在docker build的時候,Taylor沒有修改Dockerfile,沒有像Mark之前的demo那樣把私有的msvcr120.dll拷貝到容器映像中,生成新的映像Layer。

wKioL1VhwjLxJDDYAADtSLmnNpM049.jpg


結果由於容器的目標宿主機上沒有安裝Visual Studio,所以新Build的容器運行失敗,提醒缺少msvcr120.dll文件。

wKiom1VhwLPC-RVmAAERGGVks1A250.jpg


解決辦法很簡單,要麼修復這個Bug,要麼根據先前的映像快速生成新的容器,這只需要花費幾秒鐘時間!


解決這個問題很簡單,可以根據先前的映像快速生成新的容器,這大概只需要幾秒鐘時間。這樣就可以有充足的時間去調試修改了。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章