本文首先介紹了操作系統,然後引出容器技術以及虛擬機技術,最後介紹了Docker和Hyper技術。通過本文可以清楚地對三者有感性認識。
我們可以把操作系統簡化爲:操作系統 = 內核 + apps
其中內核負責管理底層硬件資源,包括CPU、內存、IO設備等,並向上爲apps提供系統調用接口,上層apps應用必須通過系統調用方式使用硬件資源,通常並不能直接訪問資源。這裏的apps指的是用戶接口,比如shell、gui、services、包管理工具等(Linux的圖形界面也是作爲可選應用之一,而不像Windows是集成到內核中的),注意與我們手動安裝的應用區別開來。同一個內核加上不同的apps,就構成了不同的操作系統發行版,比如Ubuntu、Red Hat、Android等。因此我們可以認爲,不同的Linux發行版本其實就是由應用apps構成的環境的差別,比如默認安裝的軟件、鏈接庫、軟件包管理以及圖形界面等。我們把所有這些apps環境打成一個包,就可以稱之爲鏡像。
問題來了,假如我們同時有多個apps環境,能否在同一個內核上運行呢?因爲操作系統只負責提供服務,而並不管爲誰服務,因此同一個內核之上可以同時運行多個apps環境是沒有問題的。比如假設我們現在有ubuntu和fedora的apps環境,即兩個發行版鏡像,分別位於/home/int32bit/ubuntu
和/home/int32bit/fedora
,我們最簡單的方式,採用chroot
工具即可快速切換到指定的應用環境中,相當於同時有多個apps環境在運行。
容器技術
我們以上通過chroot方式,感覺上就已經接近了容器的功能,但其實容器並沒有那麼簡單,工作其實還差得遠。首先要作爲雲資源管理還必須滿足:
資源隔離
因爲雲計算本質就是集中資源再分配(社會主義),再分配過程就是資源的邏輯劃分,提供資源抽象的實現方式,我們暫且定義爲虛擬實體,虛擬實體可以是虛擬機、容器等。虛擬實體必須滿足隔離性,包括用戶隔離(或者說權限隔離)、進程隔離、網絡隔離、文件系統隔離等,即虛擬實體只能感知其內部的資源,並且自以爲是獨佔整個資源空間,它既不能感知其所在宿主機的真實資源,也不能感知其他虛擬實體的資源。
資源控制
資源控制指爲虛擬實體分配一定量的資源,虛擬實體得到所分配的資源,不能超出資源最大使用量。
以上是虛擬實體的兩個最基本要求,當然還包括其他很多條件,比如安全、性能等。本文主要基於以上兩個基本條件進行研究。
虛擬機技術
顯然滿足以上兩個條件,虛擬機是一種實現方式,這是因爲:
隔離毋容置疑,因爲不同的虛擬機運行在不同的內核,虛擬機內部是一個獨立的隔離環境。
資源控制也是毋容置疑的,Hypervisor能夠對虛擬機分配指定的資源。
目前OpenStack Nova和AWS EC2都是基於虛擬機提供計算服務,實現CPU、RAM、Disk等資源分配。其他比如Vagrant也是基於虛擬機快速構建應用環境。
但是虛擬機也帶來很多問題,比如:
鏡像臃腫龐大,不僅包括apps,還包括一個龐大的內核。
創建和啓動時間開銷大,不利於應用快速構建重組。
額外資源開銷大,部署密度小。
性能損耗。
…
容器技術
除了虛擬機,有沒有其他實現方式能符合以上兩個基本條件呢?容器技術便是另一種實現方式。表面上和我們使用chroot方式相似,所有的容器實例直接運行在宿主機中,所有實例共享宿主機的內核,而虛擬機實例內部的進程是運行在GuestOS中。由以上原理可知,容器相對於虛擬機有以上好處:
鏡像體積更小,只包括apps以及所依賴的環境,沒有內核。
創建和啓動快,不需要啓動GuestOS,應用啓動開銷基本就是應用本身啓動的時間開銷。
無GuestOS,無Hypervisor,無額外資源開銷,資源控制粒度更小,部署密度大。
使用的是真實物理資源,因此不存在性能損耗。
輕量級。
…
目前比較流行的容器實現比如LXC、LXD以及rkt等,我們需要驗證容器是否能夠實現資源隔離和控制。
隔離性
主要通過內核提供Namespace技術實現隔離性,以下參考酷殼(http://coolshell.cn/articles/17010.html):
Linux Namespace有如下種類:
Mount Namespaces
UTS Namespaces
IPC Namespaces
PID Namespaces
Network Namespaces
User Namespaces
官方文檔在這裏《Namespace in Operation》(http://lwn.net/Articles/531114/)。
由上表可知,容器利用內核的Namespaces技術可以實現隔離性。比如網絡隔離,我們可以通過sudo ip netns ls
查看Namespaces,通過ip netns add NAME
增加Namespaces,不同的Namespaces可以有不同的網卡、Router、iptables等。
資源控制
內核實現了對進程組的資源控制,即Linux Control Group,簡稱CGroup,它能爲系統中運行進程組根據用戶自定義組分配資源。簡單來說,可以實現把多個進程合成一個組,然後對這個組的資源進行控制,比如CPU,內存大小、網絡帶寬、磁盤iops等,Linux把CGroup抽象成一個虛擬文件系統,可以掛載到指定的目錄下,Ubuntu 14.04默認自動掛載在/sys/fs/cgroup
下,用戶也可以手動掛載,比如掛載Memory子系統(子系統可以實現某類資源的控制,比如CPU、Memory、blkio等)到/mnt
下:
sudo mount -t cgroup -o memory memory /mnt
掛載後就能像查看本地文件一樣瀏覽進程組以及資源控制情況,控制組並不是孤立的,而是組織成樹狀結構構成進程組樹,控制組的子節點會繼承父節點。下面以Memory子系統爲例,
ls /sys/fs/cgroup/memory/
輸出:
cgroup.clone_children memory.kmem.failcnt memory.kmem.tcp.usage_in_bytes memory.memsw.usage_in_bytes memory.swappiness cgroup.event_control memory.kmem.limit_in_bytes memory.kmem.usage_in_bytes memory.move_charge_at_immigrate memory.usage_in_bytes cgroup.procs memory.kmem.max_usage_in_bytes memory.limit_in_bytes memory.numa_stat memory.use_hierarchy cgroup.sane_behavior memory.kmem.slabinfo memory.max_usage_in_bytes memory.oom_control notify_on_release docker memory.kmem.tcp.failcnt memory.memsw.failcnt memory.pressure_level release_agent memory.failcnt memory.kmem.tcp.limit_in_bytes memory.memsw.limit_in_bytes memory.soft_limit_in_bytes tasks memory.force_empty memory.kmem.tcp.max_usage_in_bytes memory.memsw.max_usage_in_bytes memory.stat user
以上是根控制組的資源限制情況,我們以創建控制內存爲4MB的Docker容器爲例:
docker run -m 4MB -d busybox ping localhost
返回ID爲0532d4f4af67
,自動會創建以Docker實例DI爲名的控制組,位於/sys/fs/cgroup/memory/docker/0532d4f4af67...
,我們查看該目錄下的memory.limit_in_bytes
文件內容爲:
cat memory.limit_in_bytes 4194304
即最大的可使用的內存爲4MB,正好是我們啓動Docker所設定的。
由以上可知,容器可以通過CGroup實現資源控制。
Docker技術在Docker之前其實容器技術早就有了,Google的Borg以及Omega都利用了容器技術。但是之前容器一直沒有形成一個標準,也沒有一個很好的管理工具。LXC是Linux原生支持的容器,很多工具依賴於具體的發行版,可能會出現移植性差的問題,並且也缺乏一組完善的管理工具集。而Docker基於底層的內核特性的基礎上,在上層構建了一個更高層次的具備多個強大功能的工具集,它是PaaS提供商dotCloud開源的一個基於LXC的高級容器引擎,簡單說Docker提供了一個能夠方便管理容器的工具並形成標準。Docker相當於把應用以及應用所依賴的環境完完整整地打成了一個包,這個包拿到哪裏都能原生運行。
其特性包括:
快速構建基於容器的分佈式應用
具有容器的所有優點
提供原生的資源監控
自動構建和版本控制
快速構建和重組
…
Docker與虛擬機原理對比:
容器技術在很早就有了,因此不能說Docker發明了容器技術,而僅僅是發明了一套完整的管理容器的工具集。但其實Docker核心的創新在於它的鏡像管理,因此有人說:
Docker = LXC + Docker Image
Docker鏡像的創新之處在於使用了類似層次的文件系統AUFS,簡單說就是一個鏡像是由多個鏡像層層疊加的,從一個Base鏡像中通過加入一些軟件構成一個新層的鏡像,依次構成最後的鏡像,如圖:
知乎:Docker的幾點疑問:
Image的分層,可以想象成Photoshop中不同的layer。每一層中包含特定的文件,當Container運行時,這些疊加在一起的層就構成了Container的運行環境(包括相應的文件、運行庫等,不包括內核)。Image通過依賴的關係,來確定整個鏡像內到底包含那些文件。之後的版本的Docker,會推出Squash的功能,把不同的層壓縮成爲一個,和Photoshop中合併層的感覺差不多。
作者:Honglin Feng
鏈接:https://www.zhihu.com/question/25394149/answer/30671258 來源:知乎 著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
這裏利用了COW(copy on write)技術,即從一個鏡像啓動一個容器實例,這個鏡像是以只讀形式掛載的,即不允許任何修改操作。當在容器實例中修改一個文件時,會首先從鏡像裏把這個文件拷貝到可寫層,然後執行更新操作。當讀一個文件時,會首先從可寫層裏找這個文件,若這個文件存在,直接返回文件內容,如果不存在這個文件,則會從最頂層的鏡像開始查找,直到最底層的Base鏡像。這裏存在的一個問題是,當鏡像層很多時,查找一個文件可能需要一層一層查找,影響性能。基於Ceph構建OpenStack創建虛擬機也一樣的原理,我們上傳一個鏡像到Glance時,首先對這個鏡像創建一個快照並保護起來不允許寫操作,當基於這個鏡像創建虛擬機時,直接從鏡像快照克隆一個新的rbd p_w_picpath作爲虛擬機的根磁盤,最開始這個根磁盤除了指向其parent快照的指針,沒有任何內容,不佔任何磁盤空間,當虛擬機需要修改一個對象時,首先從parent中拷貝這個對象到它所在的空間,再執行更新操作。當讀一個文件時,如果存在這個文件,直接讀取,否則需要去parent所在的p_w_picpath中查找。
這樣的好處是:
節省存儲空間——多個鏡像共享Base Image存儲;
節省網絡帶寬——拉取鏡像時,只需要拉取本地沒有的鏡像層,本地已經存在的可以共享,避免多次傳輸拷貝;
節省內存空間——多個實例可共享Base Image,多個實例的進程命中緩存內容的機率大大增加。如果基於某個鏡像啓動一個虛擬機需要資源k,則啓動n個同一個鏡像的虛擬機需要佔用資源kn,但如果基於某個鏡像啓動一個Docker容器需要資源k,無論啓動多少個實例,資源都是k;
維護升級方便——相比於copy-on-write類型的FS,Base Image也是可以掛載爲可Writeable的,可以通過更新Base Image而一次性更新其之上的Container;
允許在不更改Base Image的同時修改其目錄中的文件——所有寫操作都發生在最上層的writeable層中,這樣可以大大增加Base Image能共享的文件內容。
使用容器技術,帶來了很多優點,但同時也存在一些問題:
隔離性相對虛擬機弱-由於和宿主機共享內核,帶來很大的安全隱患,容易發生逃逸。
如果某些應用需要特定的內核特性,使用容器不得不更換宿主機內核。
…
更多關於AUFS參考酷殼:Docker基礎技術-AUFS(http://coolshell.cn/articles/17061.html)。
Hyper技術
上文提到容器存在的問題,並且Docker的核心創新在於鏡像管理,即:
於是就有人提出把容器替換成最初的Hypervisor,而又利用Docker Image的優勢,接下來介紹的Hyper技術以及VMware最新的vic技術大體如此,Hyper官方(https://hyper.sh/)定義:
即:
簡而言之Hyper是一種基於虛擬化技術(Hypervisor)的Docker引擎。官方(http://mt.sohu.com/20150625/n415640410.shtml)認爲:
創建一個基於Hyper的Ubuntu:
sudo hyper run -t ubuntu:latest bash
創建時間小於1秒,確實達到啓動容器的效率。
查看內核版本:
root@ubuntu-latest-7939453236:/# uname -a Linux ubuntu-latest-7939453236 4.4.0-hyper #0 SMP Mon Jan 25 01:10:46 CST 2016 x86_64 x86_64 x86_64 GNU/Linux
宿主機內核版本:
$ uname -a Linux lenovo 3.13.0-77-generic #121-Ubuntu SMP Wed Jan 20 10:50:42 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
啓動基於Docker的Ubuntu並查看內核版本:
$ docker run -t -i ubuntu:14.04 uname -a Linux 73a88ca16d94 3.13.0-77-generic #121-Ubuntu SMP Wed Jan 20 10:50:42 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
我們發現Docker和宿主機的內核版本是一樣的,即3.13.0-77-generic
,而Hyper內核不一樣,版本爲4.4.0-hyper
。
以下爲官方數據(https://github.com/hyperhq/hyper):
因此Hyper是容器和虛擬機的一種很好的折衷技術,未來可能前景廣大,但需要進一步觀察,我個人主要存在以下疑問:
使用極簡的內核,會不會導致某些功能丟失?
是不是需要爲每一個應用維護一個微內核?
有些應用需要特定內核,這些應用實際多麼?可以通過其他方式避免麼?
Hyper引擎能否提供和Docker引擎一樣的API,能否在生態圈中相互替代?
隔離性加強的同時也犧牲了部分性能,如何權衡?
近年來容器技術以及微服務架構非常火熱,CaaS有取代傳統IaaS的勢頭,未來雲計算市場誰成爲主流值得期待。