標籤: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、曰網路、曰檔案系統、曰帳號。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmBFBpfKHsPVOcTe5Txxdhvnkk0KspWBA69r2ANpYefSe8GRhiah9cYIQ/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
儘管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層”組成,分別為讀寫檔案系統和唯讀檔案系統(按唯讀層還可以再邏輯分層,所以極大地節省磁碟空間)。650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmpDsX4C5mXSY4dSRzYwaSBibcZdUxqsodtswg54wFaicQIbmJ1T6MAQXg/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
Windows Docker同樣如此,頂層的沙箱層(sandbox layer)是可讀寫的,只允許該容器自己佔用,而其他層則是唯讀,可供不同容器共用。在中,底層的基礎OS層和中間的應用程式架構層都是唯讀,而頂層的沙箱層則可讀寫,在容器的視角看來,它獨佔了完整的OS。這有點類似於Hyper-V的差異磁碟鏈(頂部的子盤才能讀寫,其上方的所有父盤和Base盤都是唯讀)。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmMLVRqHHBBh85FqWuaYMBFeLgIicICxpqy7UUtdYzsiaou9icMmRToeVUw/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
為了說明檔案系統隔離的魔力,Taylor得意地在Windows Container裡執行刪除C盤根目錄下所有檔案和註冊表索引值,儘管這個容器被毀了,但是根本不會影響其他容器,更不會影響主機。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmPUwIcsSIUs3vSTblLFlhCjG2vjPvmEOCKpURqxLRzWYv3UFYHic1ZRw/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
盆盆猜測,在容器的視角裡,如果只是讀取一個檔案,該檔案在最頂端的沙箱層裡只有重解析點(reparse point);只有在修改該檔案時,才會用copy-on-writer的方法從下方的唯讀層中把檔案內容複寫到可讀寫的沙箱層。
建立Windows Container
視頻裡示範了一個很棒的demo。有一段簡單的代碼,在console裡顯示“This is a pretty cool app”。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAm7O7l5sbsGDYeG3PaDkNgPn9icyuCxhpYTZUsSELf6cKXf6P9O2UpRhw/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
這是用來構建Windows Docker映像的Dockerfile。這個檔案由以下4行命令組成:
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAm7uic5DBUsrBRfzQhiaxDZeNJzJaI9a8dsuqYosL0d5Eu7AlI9icfKJmicw/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
很快用docker build命令將其構建為容器映像,Tag是1。從命令結果中可以看到Dockerfile裡的每個命令都被執行,在執行第4個命令時,會產生一個臨時的容器。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAm9ydykUZt7wkcZDk8EBePZIkWU44Xqna20MKAQX9dWUu4fV6jOaMwAA/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
Docker映像構建完成後,只需運行docker run命令即可快速啟動該映像,並成功顯示"This is a pretty cool app..."。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmzW7SbwYssBfIyf8uLWfKPCcQa1mPg6RibBCw6kSuZJq2h3OibbOqnhrw/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
通過使用docker history命令,我們可以查看容器映像的構建曆史,這甚至可以用來逆向產生Dockerfile。例如視頻裡示範了sysinternals這個映像的構建曆史。從中我們可以看到:首先記錄維護人員是誰;然後指定工作目錄是C盤根目錄;再者是將sysinternals suite這個工具軟體目錄拷貝到容器映像裡;然後設定容器的運行賬戶;最後設定啟動Ipconfig以便顯示容器的IP地址。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmWYh7N9sAUwtbb2UHiaSiaV3mAfLemh75ia31nf6oLLODuxpiclf1MVZ7ZQ/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
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系統,但是道理是一樣的。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAm7OHISYoJ0Z7ZK8L4fD04tR5KAdiakPSNqyVnKkQvWryknz7z3hia3Ujw/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
有興趣的朋友可以參考盆盆在9年前發表在ITECN部落格上的文章,介紹會話隔離技術:
http://blogs.itecn.net/blogs/winvista/archive/2006/06/09/SrvSession0.aspx
視頻裡示範了Docker容器的會話隔離能力,Taylor啟動了兩個容器,都是從同一個windowsservercore映像裡建立出來。其中一個容器,可以運行Tasklist命令,看到該容器運行在會話14中。而且還能看到兩個系統進程,一個是System進程,代表作業系統本身,另一個是空閑進程,這兩個進程都運行在會話0裡。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmTmic02GloxYFtQeC03Qgl4t90NXEHlC05gTj0D3MJrE1PUPBmR09iadA/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
在同一個映像所建立的另一個容器裡,我們可以看到該容器運行在會話15中,同樣可以看到System和空閑進程。Taylor的解釋是由於容器是共用Windows Kernel的,所以容易可以看到System進程的PID是一樣的,都是4。其實在所有Windows主機上,System進程的PID都是4。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmZAuQVgdOWOgpib1h69P7dum0kRzYPQzv5kd3U7e6V2h3C4rxb8QHMLg/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
網路隔離+圖形化訪問
和Linux容器一樣,Windows容器也可以有自己的獨立網路設定。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmTpnpT3PfFhMYVrm6ibHuybhTvNA6uhiazf2ItW5n24CzWaTh0SzZr98Q/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
由於傳統的Windows應用大多是有GUI的,所以這些應用可能需要通過圖形化方式進行遠程操控。
視頻裡Taylor舉了一個sysinternals容器的例子。有趣的是,這個demo本來是Mark Russsinovich的保留曲目,可惜Keynote的時間十分寶貴,最Mark沒能有足夠的時間去示範。
Taylor示範啟動Sysinternals Suite裡的經典工具Process Explorer,由於該工具帶GUI,所以雖然進程已經在容器裡啟動,但是在Docker Client裡無法直接遠程顯示。
如何才能正常顯示呢?Taylor用了一個CC命令,直接連接到該容器的IP地址。從demo裡我們可以看出,實際上這個CC命令串連到容器的RDP服務上,這樣就相當於直接通過終端服務串連到容器裡的會話裡。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmf5gz2q51EvX1ibAQQ72FnUwA78PRrOQJ1h3cjLyKxYMGSkWIeVFB9kA/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
盆盆在《Windows Dcoker深入原理分析》裡曾經提到Windows Docker的前身DrawBridge在其沙箱裡實現了RDP服務(請在華來四公眾號裡回複docker8,閱讀這篇文章),Windows Docker的原理應該類似。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYahEbRzIYY9RzvGUo3BcbticfnM4DCjzdGGJBicMtec03h8Sia9bChQrX62AhZfJ2Q5lEpbGjpayyibMg/640?wxfmt=png&wxfrom=5&tp=webp" width="505" height="272" />
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賬戶。
以下是容器裡的工作管理員。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmvxof9OC50YEKBHyu50771NN67SftQGTNZbSYGg6skZhg6xGNedsWTQ/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
這個工作管理員是從容器的角度來看的,我們可以記下其中的若干SVCHOST進程的PID。接下來我們開啟宿主機的工作管理員,從全域的角度來查看。可以發現,從宿主機的角度來看,能看到每個容器裡的進程,其PID和容器裡面的版本是一樣的。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmRGz4QE0wQaUib2rAJxfCqe6ia3b1Yxicau4jfXS962xqA1XWYfeGAprRw/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
使用者賬戶隔離
Linux核心擁有賬戶隔離能力,可以讓容器裡的進程以root身份運行,但是在宿主機上,該賬戶實際上是普通使用者權限。
在Taylor的這個示範中,我們也能“察覺”到一些蛛絲馬跡。在PID隔離一節的兩個工作管理員裡,容器裡的版本可以看到Process Explorer的運行賬戶為Administrator,但是從宿主機上查看,該賬戶為空白。所以Windows容器有可能也實現了類似的賬戶隔離技術。
電腦名稱和網域名稱隔離
Windows容器擁有自己的電腦名稱和網域名稱隔離能力,這樣在網路上,Windows容器看上去類似於一台獨立的虛擬機器。
不過由於共用核心,所以Windows容器目前應該不支援活動目錄,畢竟同一台宿主機上所有容器應該都具有同一個SID,這樣就無法加域(無法驗證電腦賬戶)。
盆盆推測,到了Windows容器時代,類似於活動目錄這類比較笨重的驗證協議可能會逐漸退出曆史舞台。畢竟活動目錄需要開放那麼多連接埠,需要藉助ADFS等手段才能穿透Internet。
不過Windows容器的驗證並不會存在問題,Azure AD和認證等都是很合適的辦法。
容器的優勢
容器非常適合開發的快速迭代、快速復原。Taylor做了一個簡單的示範,對前面所述的代碼進行修改,調用私人的msvcr120.dll檔案裡的_snwprintf函數。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmghlsTFiam1aGiaXxPDebicYiacrvR30RdSE3R9f7o4O8ib7eHEfc1h3PgyA/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
但是在docker build的時候,Taylor沒有修改Dockerfile,沒有像Mark之前的demo那樣把私人的msvcr120.dll拷貝到容器映像中,產生新的映像Layer。650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmk29icibbIpIyL8zmqYMmkC9rhtFj2ZmuVJrc1L1wKUAJ8iaUiccWUj0klw/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
結果由於容器的目標宿主機上沒有安裝Visual Studio,所以新Build的容器運行失敗,提醒缺少msvcr120.dll檔案。
650) this.width=650;" src="http://mmbiz.qpic.cn/mmbiz/7WIzliaCmoYavWULj8Ria77bzXRYszkLAmkvVGJqlaiaiataZ66VuX2Ets6wz09BVU8T618EVeIRQwc2uicSvbsxWoQ/640?wx_fmt=jpeg&tp=webp&wxfrom=5" />
解決辦法很簡單,要麼修複這個Bug,要麼根據先前的映像快速產生新的容器,這隻需要花費幾秒鐘時間!
解決這個問題很簡單,可以根據先前的映像快速產生新的容器,這大概只需要幾秒鐘時間。這樣就可以有充足的時間去調試修改了。
本文出自 “華來四盆盆” 部落格,請務必保留此出處http://markwin.blog.51cto.com/148406/1649973
Windows Docker第一時間揭秘-盆盆跑微軟兩會