六種隔離技術(shù)你知道幾種呢?
為了將我們的應(yīng)用部署到服務(wù)器上,我們需要為其配置一個運(yùn)行環(huán)境。從底層到頂層有這樣的運(yùn)行環(huán)境及容器:
- 隔離硬件:虛擬機(jī)
- 隔離操作系統(tǒng):容器虛擬化
- 隔離底層:Servlet容器
- 隔離依賴版本:虛擬環(huán)境
- 隔離運(yùn)行環(huán)境:語言虛擬機(jī)
- 隔離語言:DSL
實(shí)現(xiàn)上這是一個請求的處理過程,一個HTTP請求會先到達(dá)你的主機(jī)。如果你的主機(jī)上運(yùn)行著多個虛擬機(jī)實(shí)例,那么請求就會來到這個虛擬機(jī)上。又或者是如果你是在Docker這一類容器里運(yùn)行你的程序的話,那么也會先到達(dá)Docker。隨后這個請求就會交由HTTP服務(wù)器來處理,如Apache、Nginx,這些HTTP服務(wù)器再將這些請求交由對應(yīng)的應(yīng)用或腳本來處理。隨后將交由語言底層的指令來處理。

不同的環(huán)境有不同的選擇,當(dāng)然也可以結(jié)合在一起。不過,從理論上來說在最外層還是應(yīng)該有一個真機(jī)的,但是我想大家都有這個明確的概念,就不多解釋了。
1、隔離硬件(虛擬機(jī))
在虛擬機(jī)技術(shù)出現(xiàn)之前,為了運(yùn)行不同用戶的應(yīng)用程序,人們需要不同的物理機(jī)才能實(shí)現(xiàn)這樣的需求。對于Web應(yīng)用程序來說,有的用戶的網(wǎng)站訪問量少消耗的系統(tǒng)資源也少,有的用戶的網(wǎng)站訪問量大消耗的系統(tǒng)資源也多。雖然有不同的服務(wù)器類型可以選擇,然而對于多數(shù)的訪問少的用戶來說他們需要支付同樣的費(fèi)用。這聽上去相當(dāng)?shù)牟缓侠?,并且也浪費(fèi)了大量的資源。并且對于系統(tǒng)管理員來說,管理這些系統(tǒng)也不是一件容易的事。在過去硬件技術(shù)革新特別快,讓操作系統(tǒng)運(yùn)行在不同的機(jī)器上也不是一件容易的事。
虛擬機(jī)(Virtual Machine)指通過軟件模擬的具有完整硬件系統(tǒng)功能的、運(yùn)行在一個完全隔離環(huán)境中的完整計(jì)算機(jī)系統(tǒng)。
這是一個很有意思的技術(shù),它可以讓我們在一個主機(jī)上同時(shí)運(yùn)行幾個不同的操作系統(tǒng)。我們可以為這幾個操作系統(tǒng)使用不同的硬件,在這之上的應(yīng)用可以使用不同的技術(shù)棧來運(yùn)行,并且從理論上互相不影響。其架構(gòu)如下圖所示:

借助于虛擬機(jī)技術(shù),當(dāng)我們需要更多的資源的時(shí)候,創(chuàng)建一個新的虛擬機(jī)就行了。同時(shí),由于這些虛擬機(jī)上運(yùn)行的是同樣的操作系統(tǒng),并且可以使用相同的配置,我們只需要編寫一些腳本就可以實(shí)現(xiàn)其自動化。當(dāng)我們的物聯(lián)機(jī)發(fā)生問題時(shí),我們也可以很快將虛擬機(jī)遷移或恢復(fù)到另外的宿主機(jī)。
2、隔離操作系統(tǒng)(容器虛擬化)
對于大部分的開發(fā)團(tuán)隊(duì)來說,直接開發(fā)基于虛擬機(jī)的自動化工具不是一件容易的事,并且他從使用成本上來說比較高。這時(shí)候我們就需要一些更輕量級的工具容器——它可以提供輕量級的虛擬化,以便隔離進(jìn)程和資源,而且不需要提供指令解釋機(jī)制以及全虛擬化的其他復(fù)雜性。并且,它從啟動速度上來說更快。
LXC
在介紹Docker之前,我們還是稍微提一下LXC。因?yàn)樵谶^去我有一些使用LXC的經(jīng)歷,讓我覺得LXC很贊。
LXC,其名稱來自Linux軟件容器(Linux Containers)的縮寫,一種操作系統(tǒng)層虛擬化(Operating system–level virtualization)技術(shù),為Linux內(nèi)核容器功能的一個用戶空間接口。它將應(yīng)用軟件系統(tǒng)打包成一個軟件容器(Container),內(nèi)含應(yīng)用軟件本身的代碼,以及所需要的操作系統(tǒng)核心和庫。通過統(tǒng)一的名字空間和共用API來分配不同軟件容器的可用硬件資源,創(chuàng)造出應(yīng)用程序的獨(dú)立沙箱運(yùn)行環(huán)境,使得Linux用戶可以容易的創(chuàng)建和管理系統(tǒng)或應(yīng)用容器。
我們可以將之以上面說到的虛擬機(jī)作一個簡單的對比,其架構(gòu)圖如下所示:

我們會發(fā)現(xiàn)虛擬機(jī)中多了一層Hypervisor——運(yùn)行在物理服務(wù)器和操作系統(tǒng)之間,它可以讓多個操作系統(tǒng)和應(yīng)用共享一套基礎(chǔ)物理硬件。這一層級可以協(xié)調(diào)訪問服務(wù)器上的所有物理設(shè)備和虛擬機(jī),然而由于這一層級的存在,它也將消耗更多的能量。據(jù)愛立信研究院和阿爾托大學(xué)發(fā)表的論文表示:Docker、LXC與Xen、KVM在完成相同的工作時(shí)要少消耗10%的能耗。
LXC主要是利用cgroups與namespace的功能,來向提供應(yīng)用軟件一個獨(dú)立的操作系統(tǒng)運(yùn)行環(huán)境。cgroups(即Control Groups)j Linux內(nèi)核提供的一種可以限制、記錄、隔離進(jìn)程組所使用的物理資源的機(jī)制。而由namespace來責(zé)任隔離控制。
與虛擬機(jī)相比,LXC隔離性方面有所不足,這就意味著在實(shí)現(xiàn)可移植部署會遇到一些困難。這時(shí)候,我們就需要Docker來提供一個抽象層,并提供一個管理機(jī)制。
Docker
Docker 是一個開源的應(yīng)用容器引擎,讓開發(fā)者可以打包他們的應(yīng)用以及依賴包到一個可移植的容器中,然后發(fā)布到任何流行的 Linux 機(jī)器上,也可以實(shí)現(xiàn)虛擬化。Docker可以自動化打包和部署任何應(yīng)用、創(chuàng)建一個輕量級私有PaaS云、搭建開發(fā)測試環(huán)境、部署可擴(kuò)展的Web應(yīng)用等。
構(gòu)建出Docker的Container是一個很有意思的過程。在這一個過程中,首先我們需要一個base images,這個基礎(chǔ)鏡像不僅包含了一個基礎(chǔ)系統(tǒng),如Ubuntu、Debian。他還包含了一系列的模塊,如初始化進(jìn)程、SSH服務(wù)、syslog-ng等等的一些工具。由上面原內(nèi)容構(gòu)建了一個基礎(chǔ)鏡像,隨后的修改都將于這個鏡像,我們可以用它生成新的鏡像,一層層的往上疊加。而用戶的進(jìn)程運(yùn)行在writeable的layer中。

從上圖中我們還可以發(fā)現(xiàn)一點(diǎn): Docker容器是建立在Aufs基礎(chǔ)上的。AUFS是一種Union File System,它可以不同的目錄掛載到同一個虛擬文件系統(tǒng)下。它的目的就是為了實(shí)現(xiàn)上圖的增量遞增的過程,同時(shí)又不會影響原有的目錄。即如下的流程如下:

image
其增量的過程和我們使用Git的過程中有點(diǎn)像,除了在最開始的時(shí)候會有一個鏡像層。隨后我們的修改都可以保存下來,并且當(dāng)下次我們提交修改的時(shí)候,我們也可以在舊有的提交上運(yùn)行。
因此,Docker與LXC的差距就如下如圖所示:

LXC時(shí)每個虛擬機(jī)只能是一個虛擬機(jī),而Docker則是一系列的虛擬機(jī)。
3、隔離底層(Servlet容器)
在上面的例子里我們已經(jīng)隔離開了操作系統(tǒng)的因素,接著我們還需要解決操作系統(tǒng)、開發(fā)環(huán)境引起的差異。早期開發(fā)Web應(yīng)用時(shí),人們使用CGI技術(shù),它可以讓一個客戶端,從網(wǎng)頁瀏覽器向執(zhí)行在網(wǎng)絡(luò)服務(wù)器上的程序請求數(shù)據(jù)。并且CGI程序可以用任何腳本語言或者是完全獨(dú)立編程語言實(shí)現(xiàn),只要這個語言可以在這個系統(tǒng)上運(yùn)行。而這樣的腳本語言在多數(shù)情況下是依賴于系統(tǒng)環(huán)境的,特別是針對于C++這一類的編譯語言來說,在不同的操作系統(tǒng)中都需要重新編譯。
而Java的Servlet則是另外一種有趣的存在,它是一種獨(dú)立于平臺和協(xié)議的服務(wù)器端的Java應(yīng)用程序,可以生成動態(tài)的Web頁面。
Tomcat
在開發(fā)Java Web應(yīng)用的過程中,我們在開始環(huán)境使用Jetty來運(yùn)行我們的服務(wù),而在生產(chǎn)環(huán)境使用Tomcat來運(yùn)行。他們都是Servlet容器,可以在其上面運(yùn)行著同一個Servlet應(yīng)用。Servlet是指由Java編寫的服務(wù)器端程序,它們是為響應(yīng)Web應(yīng)用程序上下文中的HTTP請求而設(shè)計(jì)的。它是應(yīng)用服務(wù)器中位于組件和平臺之間的接口集合。
Tomcat服務(wù)器是一個免費(fèi)的開放源代碼的Web應(yīng)用服務(wù)器。它運(yùn)行時(shí)占用的系統(tǒng)資源小,擴(kuò)展性好,支持負(fù)載平衡與郵件服務(wù)等開發(fā)應(yīng)用系統(tǒng)常用的功能。除此,它還是一個Servlet和JSP容器,獨(dú)立的Servlet容器是Tomcat的默認(rèn)模式。其架構(gòu)如下圖所示:

Servlet被部署在應(yīng)用服務(wù)器中,并由容器來控制其生命周期。在運(yùn)行時(shí)由Web服務(wù)器軟件處理一般請求,并把Servlet調(diào)用傳遞給“容器”來處理。并且Tomcat也會負(fù)責(zé)對一些靜態(tài)資源的處理。
4、隔離依賴版本(虛擬環(huán)境)
對于Java這一類的編譯語言來說,不存在太多語言運(yùn)行帶來的問題。而對于動態(tài)語言來說就存在這樣的問題,如Ruby、Python、Node.js等等,這一個問題主要集中于開發(fā)環(huán)境。當(dāng)然如果你在一個服務(wù)器上運(yùn)行著幾個不同的應(yīng)用來說,也會存在這樣的問題。這一類的工具在Python里有VirtualEnv,在Ruby里有RVM、Rbenv,在Node.js里有NVM。
下圖是使用VirtualEnv時(shí)的不同幾個應(yīng)用的架構(gòu)圖:

如下所示,在不同的虛擬環(huán)境里,我們可以使用不同的依賴庫。在這上面構(gòu)建不同的應(yīng)用,也可以使用不同的Python版本來構(gòu)建系統(tǒng)。通常來說,這一類的工具主要用于本地的開發(fā)環(huán)境。
5、隔離運(yùn)行環(huán)境(語言虛擬機(jī))
最后一個要介紹的可能就是更加抽象的,但是也是更加實(shí)用的一個,JVM就是這方面的一個代表。在我們的編程生涯里,我們很容易就會遇到跨平臺問題——即我們在我們的開發(fā)機(jī)器上開發(fā)的軟件,在我們的產(chǎn)品環(huán)境的機(jī)器上就沒有辦法運(yùn)行。特別是當(dāng)我們使用Mac OS或者Windows機(jī)器上開發(fā)了我們的應(yīng)用,然后我們需要在Linux系統(tǒng)上運(yùn)行,就會遇到各種問題。并且當(dāng)我們使用了一個需要重新編譯的庫時(shí),這種問題就更加麻煩。
如下圖所示的是JVM的架構(gòu)示意圖

JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個虛構(gòu)出來的計(jì)算機(jī),是通過在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來實(shí)現(xiàn)的。它可以實(shí)現(xiàn)“編寫一次,到處運(yùn)行”。
換句話來說,它在底層實(shí)現(xiàn)了環(huán)境隔離,它屏蔽了與具體操作系統(tǒng)平臺相關(guān)的信息,使得Java程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可以在多種平臺上不加修改地運(yùn)行。
基于此,只要其他編程語言的編譯器能生成正確Java bytecode文件,這個語言也能實(shí)現(xiàn)在JVM上運(yùn)行。如下圖所示的是基于JVM的Jython語言的架構(gòu)圖:

其底層是基于JVM,而編寫時(shí)則是用Python語言,并且他可以使用Java的模塊來編程。
常見擁有同樣架構(gòu)的工具,還有MySQL,如下圖是所示的是MySQL的架構(gòu)圖:

MySQL在最頂層提供了一個名為SQL的查詢語言,這個查詢語言只能用于查詢數(shù)據(jù)庫,然而它卻是一種更高級的用法 。它不像通用目的語言那樣目標(biāo)范圍涵蓋一切軟件問題,而是專門針對某一特定問題的計(jì)算機(jī)語言,即領(lǐng)域特定語言。
6、隔離語言(DSL)
這是一門特別有意思也特別值得期待的技術(shù),但是實(shí)現(xiàn)它并不是一件容易的事。
作為討論隔離環(huán)境的一部分,我們只看外部DSL。內(nèi)部DSL與外部DSL最大的區(qū)別在于:外部DSL近似于創(chuàng)建了一種新的語法和語義的全新語言。如下圖所示是兩中DSL的一種對比:

在這樣的外部DSL里,我們有自己的語法、自己的解析器、類型檢測器等等。最簡單且最常用的DSL就是Markdown,如下圖所示:

如果我們可以將我們的業(yè)務(wù)邏輯寫成DSL,那么我們就不需要擔(dān)心底層語言的變動過多地影響原有的業(yè)務(wù)邏輯。換句話說,這相當(dāng)于創(chuàng)建了我們自己的語言隔離環(huán)境,我們不需要思考用何種語言來實(shí)用我們的業(yè)務(wù)。