天天看點

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

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

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

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

多指點哈。

<a href="http://channel9.msdn.com/Events/Build/2015/2-704" target="_blank">http://channel9.msdn.com/Events/Build/2015/2-704</a>

容器即隔離

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

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

http://www.infoq.com/cn/articles/docker-kernel-knowledge-namespace-resource-isolation?from=timeline&amp;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層”組成,分别為讀寫檔案系統和隻讀檔案系統(按隻讀層還可以再邏輯分層,是以極大地節省磁盤空間)。

<a href="http://s3.51cto.com/wyfs02/M02/6D/59/wKiom1Vhv2jzEwo1AACi-9Fcksw515.jpg" target="_blank"></a>

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

<a href="http://s3.51cto.com/wyfs02/M00/6D/55/wKioL1VhwQOxUWXpAAGdUvZCbHE961.jpg" target="_blank"></a>

為了說明檔案系統隔離的魔力,Taylor得意地在Windows Container裡執行删除C槽根目錄下所有檔案和系統資料庫鍵值,盡管這個容器被毀了,但是根本不會影響其他容器,更不會影響主機。

<a href="http://s3.51cto.com/wyfs02/M00/6D/59/wKiom1Vhv4bw0Y-SAAHdz1pYCIc531.jpg" target="_blank"></a>

盆盆猜測,在容器的視角裡,如果隻是讀取一個檔案,該檔案在最頂端的沙盒層裡隻有重解析點(reparse point);隻有在修改該檔案時,才會用copy-on-writer的方法從下方的隻讀層中把檔案内容複制到可讀寫的沙盒層。

建立Windows Container

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

<a href="http://s3.51cto.com/wyfs02/M01/6D/55/wKioL1VhwSGy-eYpAADZmNs2ZaA806.jpg" target="_blank"></a>

這是用來建構Windows Docker映像的Dockerfile。這個檔案由以下4行指令組成:

第1行:表明該映像基于windowsservercore這個Base映像,可以将其了解為rootfs

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

第3行:将應用目錄複制到容器映像裡

第4行:啟動該應用程式

<a href="http://s3.51cto.com/wyfs02/M01/6D/59/wKiom1Vhv6fBToHaAAClhkuH2W8974.jpg" target="_blank"></a>

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

<a href="http://s3.51cto.com/wyfs02/M02/6D/55/wKioL1VhwUGDwnqOAAG9Z1-E6SY488.jpg" target="_blank"></a>

Docker映像建構完成後,隻需運作docker run指令即可快速啟動該映像,并成功顯示"This is a pretty cool app..."。

<a href="http://s3.51cto.com/wyfs02/M02/6D/59/wKiom1Vhv7-wh6I7AADveoCZYAM167.jpg" target="_blank"></a>

通過使用docker history指令,我們可以檢視容器映像的建構曆史,這甚至可以用來逆向生成Dockerfile。例如視訊裡示範了sysinternals這個映像的建構曆史。從中我們可以看到:首先記錄維護人員是誰;然後指定工作目錄是C槽根目錄;再者是将sysinternals suite這個工具軟體目錄拷貝到容器映像裡;然後設定容器的運作賬戶;最後設定啟動Ipconfig以便顯示容器的IP位址。

<a href="http://s3.51cto.com/wyfs02/M00/6D/55/wKioL1VhwVnicR6nAADvz1IJEyE289.jpg" target="_blank"></a>

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系統,但是道理是一樣的。

<a href="http://s3.51cto.com/wyfs02/M00/6D/59/wKiom1Vhv9qDwdwxAAJdagMAcS4426.jpg" target="_blank"></a>

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

<a href="http://blogs.itecn.net/blogs/winvista/archive/2006/06/09/SrvSession0.aspx" target="_blank">http://blogs.itecn.net/blogs/winvista/archive/2006/06/09/SrvSession0.aspx</a>

視訊裡示範了Docker容器的會話隔離能力,Taylor啟動了兩個容器,都是從同一個windowsservercore映像裡建立出來。其中一個容器,可以運作Tasklist指令,看到該容器運作在會話14中。而且還能看到兩個系統程序,一個是System程序,代表作業系統本身,另一個是空閑程序,這兩個程序都運作在會話0裡。

<a href="http://s3.51cto.com/wyfs02/M01/6D/55/wKioL1VhwXHysexjAAGpKjhgR5o804.jpg" target="_blank"></a>

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

<a href="http://s3.51cto.com/wyfs02/M01/6D/59/wKiom1Vhv-_zB9D_AAHSSL_-Hw4832.jpg" target="_blank"></a>

網絡隔離+圖形化通路

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

<a href="http://s3.51cto.com/wyfs02/M02/6D/55/wKioL1VhwZHhfeLzAAE29cREuQ8686.jpg" target="_blank"></a>

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

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

Taylor示範啟動Sysinternals Suite裡的經典工具Process Explorer,由于該工具帶GUI,是以雖然程序已經在容器裡啟動,但是在Docker Client裡無法直接遠端顯示。

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

<a href="http://s3.51cto.com/wyfs02/M00/6D/55/wKioL1VhwbLzEHx7AAI9V74brwI268.jpg" target="_blank"></a>

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

<a href="http://s3.51cto.com/wyfs02/M00/6D/59/wKiom1VhwDCBV5zVAADl8b8wTpI349.jpg" target="_blank"></a>

Linux Docker也能通路圖形化界面,在以下的這篇文章《在 Docker 中運作 OpenOffice》裡介紹,隻需在Dockerfiles裡添加安裝X Server的指令,就能借助VNC用戶端連接配接到OpenOffice圖形化界面。

<a href="http://linux.cn/article-5305-weibo.html" target="_blank">http://linux.cn/article-5305-weibo.html</a>

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賬戶。

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

<a href="http://s3.51cto.com/wyfs02/M01/6D/55/wKioL1VhwciCfYlMAAIjFougUmY378.jpg" target="_blank"></a>

這個任務管理器是從容器的角度來看的,我們可以記下其中的若幹SVCHOST程序的PID。接下來我們打開主控端的任務管理器,從全局的角度來檢視。可以發現,從主控端的角度來看,能看到每個容器裡的程序,其PID和容器裡面的版本是一樣的。

<a href="http://s3.51cto.com/wyfs02/M01/6D/59/wKiom1VhwFfSjakCAAJ8PBbciQ4316.jpg" target="_blank"></a>

使用者賬戶隔離

Linux核心擁有賬戶隔離能力,可以讓容器裡的程序以root身份運作,但是在主控端上,該賬戶實際上是普通使用者權限。

在Taylor的這個示範中,我們也能“察覺”到一些蛛絲馬迹。在PID隔離一節的兩個任務管理器截圖裡,容器裡的版本可以看到Process Explorer的運作賬戶為Administrator,但是從主控端上檢視,該賬戶為空。是以Windows容器有可能也實作了類似的賬戶隔離技術。

計算機名和域名隔離

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

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

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

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

容器的優勢

容器非常适合開發的快速疊代、快速復原。Taylor做了一個簡單的示範,對前面所述的代碼進行修改,調用私有的msvcr120.dll檔案裡的_snwprintf函數。

<a href="http://s3.51cto.com/wyfs02/M02/6D/55/wKioL1VhwhnwYqW_AAC5W_oOXgE378.jpg" target="_blank"></a>

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

<a href="http://s3.51cto.com/wyfs02/M00/6D/55/wKioL1VhwjLxJDDYAADtSLmnNpM049.jpg" target="_blank"></a>

結果由于容器的目标主控端上沒有安裝Visual Studio,是以新Build的容器運作失敗,提醒缺少msvcr120.dll檔案。

<a href="http://s3.51cto.com/wyfs02/M00/6D/59/wKiom1VhwLPC-RVmAAERGGVks1A250.jpg" target="_blank"></a>

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

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

本文轉自 ahpeng 51CTO部落格,原文連結:http://blog.51cto.com/markwin/1649973,如需轉載請自行聯系原作者

繼續閱讀