Unikernel不適合生產(chǎn)環(huán)境
? ?最近我犯了個(gè)錯(cuò),在Twitter上語氣激昂的問是否該講講為什么unikernel不適合用在生產(chǎn)環(huán)境。結(jié)果響應(yīng)十分強(qiáng)烈:有的人感覺unikernel走錯(cuò)方向了,在尋找支持這種觀點(diǎn)的細(xì)節(jié);有的人是unikernel的支持者,也很想知道反對(duì)unikernel的觀點(diǎn)是什么。顯然人們都想聽到反對(duì)在生產(chǎn)環(huán)境上使用unikernel的觀點(diǎn)究竟是什么。
那么,unikernel的問題究竟是什么?讓我們首先來看一下定義:unikernel是一個(gè)完全運(yùn)行在微處理器的特權(quán)模式的應(yīng)用程序(具體的術(shù)語可能不一樣:在x86上這被稱作運(yùn)行在Ring 0)。即在unikernel中,完全沒有傳統(tǒng)意義上的應(yīng)用程序;相反,應(yīng)用程序的功能被拖拽到了操作系統(tǒng)內(nèi)核中。(沒有操作系統(tǒng)(“no os”)的想法會(huì)誤導(dǎo)你;并不是沒有操作系統(tǒng),而是應(yīng)用程序已經(jīng)擔(dān)負(fù)起了操作系統(tǒng)作為硬件接口的責(zé)任 – 這是一個(gè)完全的OS(“all os”),只不過是一個(gè)粗制的,一個(gè)貧血的操作系統(tǒng)。)在我們討論這樣做會(huì)面臨哪些挑戰(zhàn)的時(shí)候,有必要首先探索一下unikernel的背后的動(dòng)機(jī)是什么,即使僅僅是因?yàn)槟敲匆稽c(diǎn)點(diǎn)的好處…
選擇在操作系統(tǒng)內(nèi)核中實(shí)現(xiàn)功能的主要原因是性能:通過消除在用戶-內(nèi)核邊界之間的上下文切換,那些需要依靠在這個(gè)邊界進(jìn)行切換來完成的操作就可以做到更快。對(duì)于unikernel,這些觀點(diǎn)看起來貌似是正確的:一方面現(xiàn)代的平臺(tái)運(yùn)行時(shí)(runtime)的復(fù)雜度很高,一方面現(xiàn)代微處理器的性能很好。很少有人會(huì)留意到應(yīng)用的性能受到了用戶-內(nèi)核態(tài)的上下文切換產(chǎn)生的開銷的限制。并且這種觀點(diǎn)還可能站不住腳的地方在于,unikernel很大程度上依賴硬件虛擬化來實(shí)現(xiàn)多租戶等功能,這個(gè)事實(shí)也削弱了這些觀點(diǎn)的說服力。正如我在過去的一篇文章中展開的,在硬件層的虛擬化有無法逃避的性能稅費(fèi):通過把可以看到硬件的系統(tǒng)(即hypervisor)與可以實(shí)際上看到應(yīng)用程序的系統(tǒng)(借宿操作系統(tǒng)/guest OS)隔離開來,在硬件的利用率(比如DRAM,網(wǎng)卡,CPU、I/O等)方面的損失了效率,而且再多的意志力(willpower)和暴力(brute force)都無法彌補(bǔ)這種損失。但是,繼續(xù)在性能這一點(diǎn)上繼續(xù)糾纏下去沒有什么好處,那就讓我們讓我們認(rèn)為unikernel在性能論這一點(diǎn)上占了上風(fēng),但是存在一些有堅(jiān)實(shí)基礎(chǔ)的反面觀點(diǎn),讓我們繼續(xù)討論。
另外一個(gè)unikernel的支持者給出的觀點(diǎn)是unikernel更加安全,但實(shí)在不清楚這背后的思想基礎(chǔ)是什么。是的,unikernel通常運(yùn)行更少的軟件(因而攻擊面更?。? 但是unikernel并沒有什么什么本質(zhì)上的東西一定會(huì)導(dǎo)出軟件更少的結(jié)果。是的,unikernel通常運(yùn)行新開發(fā)的,與總不同的軟件(因而不會(huì)受到OpenSSL 每周漏洞列表中出現(xiàn)的漏洞的威脅 ),但是這種因?yàn)槟:逎蜁?huì)安全的觀點(diǎn),可以套用到任何新出現(xiàn)的或者難以理解的系統(tǒng)上。這個(gè)安全觀點(diǎn)同時(shí)也似乎忽視了unikernel很大程度上依賴的保護(hù)邊界的事實(shí):由底層hypervisor提供的在寄宿操作系統(tǒng)(guest OS)之間提供的保護(hù)邊界。Hypervisor的漏洞是的確存在的;不能一方面渲染Linux內(nèi)核的漏洞是如何的一種潛在威脅,一方面覺得hypervisor的漏洞只會(huì)存在于想像中。相反,不讓應(yīng)用程序開發(fā)者使用用戶保護(hù)邊界的工具,破壞了最小特權(quán)原則(Principle of least privilege):任何應(yīng)用程序中的漏洞都會(huì)根植在unikernel中。在這個(gè)以容器為基礎(chǔ)進(jìn)行部署的世界中,這會(huì)造成一個(gè)很棘手的問題 – 秘密管理(secret management) – 并且會(huì)讓其變得相當(dāng)難以處理(并且有更高的風(fēng)險(xiǎn))。在最好的情況下,uniknernel算得上是一個(gè)“安全戲劇”(注:security theater意思是那些旨在提升安全,或只想給人們制造一種更安全、一切盡在掌控之中的感覺,卻實(shí)際上對(duì)于提升安全性并沒有或者很少幫助的安全措施);在最糟的情況,這就是一個(gè)安全噩夢。
unikernel的支持者最后一個(gè)的觀點(diǎn)是unikernel體積小 – 但是,unikernel并沒有什么小的地方!拿我自己的經(jīng)驗(yàn)來說,我在小的系統(tǒng)和大的系統(tǒng)上面做過實(shí)現(xiàn)內(nèi)核的事情;你當(dāng)然可以有一個(gè)精簡的系統(tǒng);而無需走上借助于unikernel來實(shí)現(xiàn)一種類似胃繞道手術(shù)一樣的效果。(我自己也喜歡在Alpine Linux非常精簡的用戶態(tài)基礎(chǔ)上來運(yùn)行Linux程序和/或Docker容器。)并且根據(jù)unikernel沒有包含多少代碼的這種程度來說,這種感覺似乎更多是因?yàn)橛字?,而不是因?yàn)樵O(shè)計(jì)。但是僅僅通過代碼來衡量unikernel的體積是錯(cuò)誤的,并且這里unikernel的支持者也忽略了更大型的系統(tǒng)的細(xì)節(jié):因?yàn)閡nikernel以一個(gè)借宿操作系統(tǒng)來運(yùn)行,由hypervisor為它分配的DRAM會(huì)被完全占用,即使應(yīng)用自身沒有利用這些內(nèi)存。因?yàn)閮?nèi)存耗盡仍然是導(dǎo)致應(yīng)用崩潰的罪惡首要之一(特別是在動(dòng)態(tài)環(huán)境中),受這個(gè)原因(需求)的驅(qū)使,內(nèi)存大小調(diào)整很容易被過度工程化,通常采取的是盲目翻倍或者在相反的情況被吞并(slop up)。在unikernel的模型中,任何這種吞并的情況沒有了 – 沒有誰再能使用它,因?yàn)閔ypervisor不知道它有沒有真正地被使用。(這跟在容器中的情況完全不同,在容器中沒有被應(yīng)用使用的內(nèi)存可以被其他的容器使用,或者被系統(tǒng)自身所使用)。因此,當(dāng)考慮了整個(gè)系統(tǒng),unikernel的支持觀點(diǎn)變得更加單薄(如果沒有被完全否決掉的話)。
所以這些就是支持unikernel的原因:很大一部分是因?yàn)樾阅?,然后一點(diǎn)安全戲劇,和一個(gè)軟件崩潰食譜( software crash diet)。正如這些理由不冷不熱所預(yù)示的,它們構(gòu)成了unikernel的好消息的終點(diǎn)。事情從這里開始,都是壞消息:要獲得這些好處必須付出成本,不管這些好處是多么多么的脆弱。
unikernel的缺點(diǎn)從應(yīng)用程序自身的運(yùn)行機(jī)制開始。當(dāng)操作系統(tǒng)的邊界被消除了,你可能也消除了應(yīng)用程序和外部世界的接口,如網(wǎng)絡(luò)和持久存儲(chǔ) – 但難道你就沒有使用這些接口的需求,你就完全沒這需要了?有些unikernel(如OSv和Rumprun)采用的是實(shí)現(xiàn)一個(gè)“POSIX類似”的接口來盡量減少對(duì)應(yīng)用程序的影響。好消息是:app好像也可以工作。壞消息是:我們還未嘗提到這些應(yīng)用程序需要被移植!這里希望你的應(yīng)用的“POSIX相似性”不會(huì)設(shè)計(jì)到一些老的的觀念,如創(chuàng)建一個(gè)進(jìn)程:在unikernel中沒有進(jìn)程,如果你的應(yīng)用程序依靠這個(gè)基礎(chǔ)的話,基本上你就是被囚禁了(或者比囚禁了更糟)。
如果這種方法看起來比較邊緣,對(duì)于那些特定語言的unikernel,事情會(huì)變得更加糟糕,如MirageOS這種特定語言的unikernel深嵌了一個(gè)特定的語言運(yùn)行時(shí)。一方面,只允許用某一個(gè)類型安全的語言的實(shí)現(xiàn)可以讓一些嚴(yán)重的unikernel安全問題被解規(guī)避掉;但另一方面,保佑你所有你需要的都已經(jīng)在OCam中有了!
因此要讓你的應(yīng)用運(yùn)行起來會(huì)有一些問題,假定你都已經(jīng)都解決了這些問題:或者是因?yàn)槟愕膗nikernel暴露的POSIX接口(注:原文是surface)對(duì)于你的應(yīng)用或者平臺(tái)來說足夠了,或者它已經(jīng)是用OCam或者Erlang或者Haskell或者其他什么語言實(shí)現(xiàn)的。假如你有了可以用unikernel來運(yùn)行的應(yīng)用程序,現(xiàn)在到了一個(gè)最深刻的為什么 unikernel 不適合生產(chǎn)環(huán)境的理由 – 并且這個(gè)理由當(dāng)真要部署任何真正在運(yùn)行生產(chǎn)中的時(shí)候,(至少對(duì)于我來說)簡直直擊unikernel的心臟:Unikernel是完全不可調(diào)試的。沒有進(jìn)程,因此也沒有netstat,沒有tcpdump,沒有ping。并且這些還只是已經(jīng)有幾個(gè)世紀(jì)歷史的工具,那任何現(xiàn)代的工具,如DTrace或者M(jìn)DB就更不用說了。站在調(diào)試的角度上,要說這是最基本的工具都言輕了:這豈止是石器時(shí)代,這簡直就是前寒武紀(jì)。我,作為一個(gè)全部職業(yè)生涯都在開發(fā)生產(chǎn)環(huán)境系統(tǒng),和各種工具來調(diào)試這些系統(tǒng)的人,我發(fā)現(xiàn)這種隱含出的拒絕調(diào)試生產(chǎn)環(huán)境是讓人感到憤怒的,似乎在那些unikernel支持者中有更深讓人不爽的癥狀:完全沒有對(duì)運(yùn)維的同理心(empathy)。生產(chǎn)環(huán)境問題就被簡單地?fù)]揮衣袖撇開了 – 服務(wù)在不正常的時(shí)候只能被等著重啟。這種態(tài)度,盡管即使只是被推論出來并不廣為存在,對(duì)于任何曾經(jīng)負(fù)責(zé)過運(yùn)維一個(gè)系統(tǒng)的人來說很難是不讓人不冒火的。(假如你認(rèn)為我是一個(gè)局外人,聽聽我在DockerCon 2015的演講中,在我強(qiáng)調(diào)系統(tǒng)出了問題后系統(tǒng)需要調(diào)試而不是僅僅被重啟后臺(tái)下的掌聲吧)。如果需要把話明說出來,這種態(tài)度讓人憤怒因?yàn)檫@是錯(cuò)誤的:如果一個(gè)生產(chǎn)環(huán)境的應(yīng)用開始工作不正常,原因在于一個(gè)非致命的情況,如如監(jiān)聽降低(listen drops),重啟應(yīng)用是在可能的最遭時(shí)間(換句話說,在負(fù)載最高的情況下)中斷了應(yīng)用,卻并沒有朝著找出問題的根源(一個(gè)不夠的未完成的工作)邁出任何一步。
那么,能在unikernel中實(shí)現(xiàn)一個(gè)生產(chǎn)環(huán)境的調(diào)試工具么?一個(gè)字,不可能。調(diào)試工具通常會(huì)跨越用戶-內(nèi)核邊界,在利用命令行提供的特定工具進(jìn)行查詢是最有成效的,然而提供這種工具的器官已被特意地從uninkernel中摘除了,名曰減肥(注:上文中提到的胃繞道手術(shù)就是一種減肥的手術(shù));任何提供足夠復(fù)雜調(diào)試工具以供生產(chǎn)環(huán)境使用的unikernel都違反了自己的信條。unikerne不適合生產(chǎn)環(huán)境不僅僅因?yàn)槠鋵?shí)現(xiàn)方式,同時(shí)也因?yàn)槠浔焕斫獾姆绞剑核麄冊(cè)谏a(chǎn)環(huán)境工作不正常的時(shí)候會(huì)無法被理解 – 而且源于它們自己的堅(jiān)持,它們永遠(yuǎn)也做不到這一點(diǎn)。
盡管以上所有這些,我確實(shí)發(fā)現(xiàn)了一些和unikernel支持者一樣的觀點(diǎn):我同意容器的革命需要一個(gè)更加精簡,更加安全,更加高效的runtime,而不僅僅是一個(gè)運(yùn)行在虛擬硬件之上的一個(gè)共享的Linux 借宿操作系統(tǒng)(Linux guest OS) – 并且在 Joyent過去的幾年 ,我們的焦點(diǎn)一樣的在用SmartOS and Triton來達(dá)成這個(gè)目標(biāo)。盡管我們和unikernel 的支持者看到了相似的問題,我們采取的方式有本質(zhì)不同:與放棄在一個(gè)多租戶的基礎(chǔ)上運(yùn)行容器的觀念不同,我們拿已經(jīng)安全的zone做為基石并且為其添加了原生執(zhí)行Linux 二進(jìn)制文件的能力。即,我們選擇利用操作系統(tǒng)的進(jìn)步成果,而不是忽視它們的存在,不但給Linux和Docker帶了安全裸機(jī)之上(on-the-metal)的容器,同時(shí)也帶來了操作系統(tǒng)的先進(jìn)成果,如ZFS和Crossbow,也當(dāng)然少不了DTrace。這些努力都值得最后一次重新強(qiáng)調(diào):我們?cè)谏a(chǎn)環(huán)境上的努力體現(xiàn)在我們做的每件事情上,但特別應(yīng)指出的是,全面的工具來調(diào)試生產(chǎn)環(huán)境的操作系統(tǒng) - 并且通過將這些工具鏈帶入Linux容器這個(gè)更廣闊的世界,Triton已經(jīng)允許在生產(chǎn)環(huán)境中的調(diào)試,而這在之前是不敢想的。。
我認(rèn)為,終有一日,unikernel最有成效的結(jié)果還是得益于它產(chǎn)生的負(fù)面效果:它會(huì)作為一個(gè)很好的示例,來證明它們的方法對(duì)于生產(chǎn)系統(tǒng)是不切實(shí)際的。照此,它們會(huì)跟事務(wù)型內(nèi)存( transactional memory ),m-to-n調(diào)度模型一起,成為曾風(fēng)靡一時(shí),但因現(xiàn)實(shí)無情的細(xì)節(jié),而死掉的斷氣軟件。但是沒必要把我的話當(dāng)真,正如我在tweet中提到的,不可調(diào)試的系統(tǒng)自身就如同種下一個(gè)惡果 – 只是自己受用就好,就不要加害其他蕓蕓眾生了。