比容器更輕更快的虛擬機

儘管容器技術在今天越來越被人接受,但是安全性依然是一個繞不開的問題,由於容器採用的是共享內核外加 cgroups 和 namespaces 等黑魔法的方式進行隔離註定了會有很多路徑的 bug 導致隔離性問題,安全隱患依然存在。而不使用虛擬機的原因不外乎虛擬機啟動太慢,額外開銷太高,性能由於多了一層會下降。面對容器和虛擬機這兩個極端,容器一方想把容器做的隔離性更好,虛擬化方面想把虛擬機做的更輕,結果 neclab 的一群人居然做到把虛擬機的啟動速度做的比 Docker 還快,內存開銷比 Docker 還小,這種反常識的事情居然發生了!他們把工作以 paper 的形式發表在了 SOSP'17 上,這篇文章會介紹下他們是動用了什麼樣的核武器達到了這樣的效果。

Advertisements

想要加速就要先有個預期,看一下我們的現狀是什麼,極限又能到哪裡。作者對比了進程啟動時間,容器啟動時間以及一個虛擬機啟動的時間。其中進程啟動時間作為一個理論上的極限需要 3.5 ms,docker 容器啟動時間需要 200ms,而一個 Debian 虛擬機的啟動時間需要大約 2s,其中創建 vm 需要 0.5 s, 啟動 vm 需要 1.5s。

而和啟動速度相關最重要的一個因素就是鏡像體積大小,啟動的速度基本上隨著鏡像的體積線性增加。如下圖所示:

vm 通常需要一個完整的操作系統,所以體積會比較大,如果想優化速度就必須要精簡操作系統了,作者通過兩種方法實現了這種精簡。

第一種也是最極端的一種 Unikernels,這可以算是終極核武了。Unikernels 的定義比較複雜,簡單來說就是專門為這個應用做一個操作系統內核,這個內核只提供能運行這個服務的最基本功能,除了能跑這個應用別的什麼都幹不了。應用和內核也是 link 在一起的,你甚至不好說是給這個應用定製了一個內核,還是定製了一個內核具有一定應用功能。這種方法需要為每個特定應用定製一個特定內核,聽著就是個不可能完成的任務,不過作者還是做了好幾個這樣的 unikernels,甚至還做了個能跑 serverless 功能的 unikernels。

Advertisements

當然這種方法太極端了,第二種方法就相對溫和一些,不過依然是洲際導彈重量級別的。既然定製 unikernels 太難了,那就拿一個 Linux 內核進行精簡化只保留需要的模塊和功能,為每個應用編譯一個精簡化的 Linux 內核,這樣就不需要做應用改造了。為了達到這個目的作者還專門做了個 Tinyx 工具,可以根據應用 objdump 出來的信息來自動尋找依賴,並且根據啟發性的測試不斷地去尋找哪些 linux 模塊可以關閉,去除掉那些和應用無關的功能和編譯選項。除了縮小鏡像體積,另一個帶來的好處是會降低 vm 啟動佔用的內存。經過精簡的內核可以只佔用 1.8M 內存,要知道 docker 給每個容器開啟的 shim 進程也需要佔據 4M 左右的內存。

最終的結果是利用 Tinyx 精簡內核後鏡像大小是 9.5M 的啟動時間是 180ms,而 Unikernels 方法鏡像大小是 480K,啟動時間只需要 3ms,這兩種方案都已經比 200ms 的 docker 啟動時間還要快了。並且更要命的是他們的內存額外開銷比 docker 還要低,從下圖可以看出當運行到 3K 容器時宿主機已經無法再創建 docker 容器了,而 Unikernels 可以平穩跑到 8K。

上面介紹的是優化啟動速度,主要是精簡內核的內容,paper 中還有將近一半的篇幅是介紹如何優化創建 vm 的過程,包括把消息隊列的機制改為共享內存機制,虛擬機的預創建以及很多腳本工具 binary 化等很黑科技的優化,對 Xen 進行了大刀闊斧的改進,(畢竟 Xen 也不是用來做這種事情的),有興趣的同學可以去看下原 paper。

話說回來,儘管作者真的做到了虛擬機比容器更輕更快,但是這個過程中用到的技術基本上是屬於『即使我告訴你原理,你也依舊做不出來』的範疇。這其中需要太多內核和虛擬化的專業技術,一般的應用開發,甚至一般的底層開發都接觸不到。而容器和虛擬化兩者的融合,我相信依然是未來的一個必然要走的路線,虛擬機會越來越輕量化,容器也越來越會像虛擬機。這篇 paper 的工作也告訴了我們虛擬化還有大量的性能優化空間,而且既然虛擬機都能比容器更輕更快,那麼現在的容器在這方面也要努力了。讓我們看一下最終是容器長成了虛擬機,還是虛擬機變成了容器呢?

Paper 鏈接:https://dl.acm.org/citation.cfm?id=3132763&CFID=1010583529&CFTOKEN=60147204

Advertisements

你可能會喜歡