Docker為什么 (一)
原文 http://dockone.io/article/357
引言
作為最近一年最火熱的服務端技術(也許沒有之一),Docker引發了大量的關注和討論,我在從事持續集成服務平臺方面的工作,因此也在關注和了解的以后嘗試將現有系統進行了docker化的改造。這個過程加深了認識和理解,本文就是對這些工作的一個總結。
后面的討論假設你知道Docker是什么,如果不了解,可以去google一下再回來。
理解特點
首先明確技術要點,Docker技術就是基于容器的虛擬化技術,相對于其它虛擬化技術,它的特點是:
- 輕量級:單機可以輕松支持上百container,讓各種個位數虛擬化的方案相形見絀
- 快速就緒:一秒以內啟動,即使是以資源快速就緒著稱的青云IAAS也無法相比
- 弱安全:docker能夠對多種OS資源進行隔離,但是它本質上依托于內核,因此所有的內核漏洞都是docker的致命傷
上述三個特點都不是實現上的差異,而是設計方案的先天結果,因此上述結論會在很長一段時間內有效。
理解上述這些特點非常重要。
很多剛接觸docker的人會把它和虛擬機做類比,然而這種類比和docker的最佳實踐場景有所不同,一個docker容器不像是一臺虛擬機,而更像是一個服務單元,如果要類比,反倒更像進程。
當然,docker容器從技術層面上看確實也就是一個進程,然而這不是關鍵,“輕量級”這個特點帶來的其實是服務器軟件工作方式的回歸。不妨來看看服務器軟件的工作單元的抽象工作方式。
服務器軟件的抽象模型是有限狀態機,不難想到,在這類場景下,服務單元最終要做的,無非就是下面這些事情:
- 從網絡得到請求
-
結合內部狀態進行計算
2.1 訪問獨有或共享的內存
2.2 訪問獨有或共享的文件系統
2.3 訪問其它服務單元 - 通過同步或者異步的方式將計算結果由網絡反饋出去
最為復雜的部分當屬第2步,不過如果考慮到服務器軟件需要進行水平擴展,業務也會逐漸分布化,我們會想到一句話,“不要通過共享內存進行通信,而是要通過通信共享內存”,所以,2.1. 可以改為訪問獨有的內存
實際上,由于有2.3的存在,2.2 也可以簡化為訪問獨有的文件系統,不過文件系統本身的IO性能很差,一旦到了這個層面,往往是分布式和水平擴展的壓力不大的場景,所以可以保留。
除了功能性需求,我們還有管理和控制需求,包括:
- 可見性——服務單元應該通過某種方式暴露自己的運行以及資源使用情況
- 可操作性——服務單元應該有某種渠道接受和響應控制信號
這樣,我們可以大致勾勒出一個“服務單元”需要具備的能力列表:
- 獨享內存訪問
- 獨享磁盤訪問
- 對網絡的訪問能力
- 實時暴露自身狀態和資源占用
- 實時反饋運行情況
- 接受和響應外部控制信號
說了這么多顯得很羅嗦,因為這顯然就是一個普通的操作系統進程要做的——結合cgroups和 proc 文件系統這樣的技術,現代linux可以將進程對內存、網絡、文件系統的訪問隔離開,并能實時掌握進程的狀態;通過標準輸出和syslog,進程可以持續 反饋運行情況;通過信號量,進程有機會對外部信號作出反應......那為什么還要docker?
因為傳統虛擬機的傳統進程做的太多了。
舉幾個例子:
- 應用如何變成daemon,很長一段時間都是被應用系統開發人員忽略的問題,這個問題雖然有解決辦法,但是各種語言和框架的辦法并不統一,如果交給運維人員,千差萬別的腳本會帶來很高的維護成本,而這本來應該是進程管理的事,和具體的技術棧無關。
- 我們常常要從安全角度考慮問題,一些鐵律也是這么出現的,比如“不要使用root”,然而對線上服務器而言,它常常是單一進程,其它帳號根本就用不到,所以會出現這樣的怪事:把要做的事分為root和admin,然后運維腳本是 sudo 開頭的
- 由于可以隨意登錄,并進行變更,profile的load過程很復雜并且容易引入不確定性,所以大家形成共識——不用環境變量來約束和客 制化軟件的行為(雖然環境變量本來就是做這個的)。這樣的后果是,c/c++、java、python、ruby各種社區都形成了自己的配置文件習慣用 法,更進一步,java社區曾經有個趨勢,把配置文件本身都做的快和代碼沒區別了(復雜度相當,也是在編譯時而不是運行時確定實際參數值)
而在docker時代,這些都不是問題——
- container自己就是個daemon,應用系統進程放在里面不用再考慮daemonize。
- container是獨占的,因此你完全可以僅用root帳號做事,并且不會破壞授權模型。
- 對于運行中的進程,可以很簡單的使用docker exec命令獲取其當前的環境變量,而且Dockerfile和docker-compose.yml都可以設定環境變量,這樣,各種千奇百怪的“配置文件”的需要就可以大幅度簡化。
不太嚴謹的說,Docker其實是重新定義和簡化了linux,它是在針對服務器的場景進行裁剪,把核心問題解決的更干凈純粹,剝離枝節,使之更加貼近服務器環境的需要。
從這個角度出發再看看之前提到的三個特點,會覺得這是很實用的設計:
輕量級
表面上看,輕量級容器更容易啟動,但更重要的是,這是對 linux 本身設計的某種回歸,所以雖然簡單卻并不簡陋。如果我們實踐中感到不便,也許正應反思是否把運維做復雜了。
快速就緒
不難理解,由于本身僅僅是一個普通的進程,我們可以很快的啟動容器,然而問題不止于此,個人理解,這也是一個“回歸”——這是“以進程為計算單元的協作方式”的回歸,all in one 其實并不是很好,每個計算單元做的少而精才是 linux 的理念。
當然,傳統 linux 腳本組裝的少而精并不能應對web/internet,docker容器的少而精實際上是在另一個角度進行了回歸——讓每個容器都能專注一個方面,通過容器的快速建立/釋放來協調資源,通過標準規范的容器間協作來搭建復雜的系統。
弱安全
這一點其實是一個推論——如果要發揮系統的計算能力,虛擬化應該盡可能的薄,然而過于單薄將對系統的穩定性和安全性造成影響,docker做了一個trade-off——它將安全性交給了IAAS系統,而專注做好穩定性。
docker自身顯然還是要依托于物理系統或者xen/kvm這樣的虛擬機,而這些系統的安全邊界比較清晰,所以docker沒有必要再背起這 么重的負擔,唯一需要考慮的是容器間的資源獨占導致系統穩定性降低,因此重點不在多租戶支持,而在故障隔離,后者其實就是資源隔離。
理解了docker的特點,我們才能進一步分析和討論docker到底能帶來什么。
</div> </div>