Docker 介紹: 相關技術
Abstract
本文在現有文檔的基礎上總結了以下幾點內容
-
docker的介紹,包括由來、適用場景等
</li> -
docker背后的一系列技術 - namespace, cgroup, lxc, aufs等
</li> -
docker在利用LXC的同時提供了哪些創新
</li> -
筆者對docker這種container, PaaS的一些理解
</li> -
docker存在的問題和現有的解決思路
</li> </ol>Docker 簡介
Docker is an open-source engine that automates the deployment of any application as a lightweight, portable, self-sufficient container that will run virtually anywhere.
</blockquote>Docker 是 PaaS 提供商 dotCloud 開源的一個基于 LXC 的高級容器引擎,源代碼托管在 Github 上, 基于go語言并遵從Apache2.0協議開源。 Docker近期非常火熱,無論是從 github 上的代碼活躍度,還是Redhat在RHEL6.5中集成對Docker的支持, 就連 Google 家的 Compute Engine 也支持 docker 在其之上運行, 最近百度也用 Docker 作為其PaaS的基礎(不知道規模多大)。
一款開源軟件能否在商業上成功,很大程度上依賴三件事 - 成功的 user case, 活躍的社區和一個好故事。 dotCloud 自家的 PaaS 產品建立在docker之上,長期維護 且有大量的用戶,社區也十分活躍,接下來我們看看docker的故事。
-
環境管理復雜 - 從各種OS到各種中間件到各種app, 一款產品能夠成功作為開發者需要關心的東西太多,且難于管理,這個問題幾乎在所有現代IT相關行業都需要面對
</li> -
云計算時代的到來 - AWS的成功, 引導開發者將應用轉移到 cloud 上, 解決了硬件管理的問題,然而中間件相關的問題依然存在 (所以openstack HEAT和 AWS cloudformation 都著力解決這個問題)。開發者思路變化提供了可能性。
</li> -
虛擬化手段的變化 - cloud 時代采用標配硬件來降低成本,采用虛擬化手段來滿足用戶按需使用的需求以及保證可用性和隔離性。然而無論是KVM還是Xen在 docker 看來, 都在浪費資源,因為用戶需要的是高效運行環境而非OS, GuestOS既浪費資源又難于管理, 更加輕量級的LXC更加靈活和快速
</li> -
LXC的移動性 - LXC在 linux 2.6 的 kernel 里就已經存在了,但是其設計之初并非為云計算考慮的,缺少標準化的描述手段和容器的可遷移性,決定其構建出的環境難于 遷移和標準化管理(相對于KVM之類image和snapshot的概念)。docker 就在這個問題上做出實質性的革新。這正式筆者第一次聽說docker時覺得最獨特的地方。
</li> </ul>面對上述幾個問題,docker設想是交付運行環境如同海運,OS如同一個貨輪,每一個在OS基礎上的軟件都如同一個集裝箱,用戶可以通過標準化手段自由組裝運行環境, 同時集裝箱的內容可以由用戶自定義,也可以由專業人員制造。這樣,交付一個軟件,就是一系列標準化組件的集合的交付,如同樂高積木,用戶只需要選擇合適的積木組合, 并且在最頂端署上自己的名字(最后個標準化組件是用戶的app)。這也就是基于docker的PaaS產品的原型。
What Docker Can Do
在docker的網站上提到了docker的典型場景:
-
Automating the packaging and deployment of applications
</li> -
Creation of lightweight, private PAAS environments
</li> -
Automated testing and continuous integration/deployment
</li> -
Deploying and scaling web apps, databases and backend services
</li> </ul> </blockquote>由于其基于LXC的輕量級虛擬化的特點,docker相比KVM之類最明顯的特點就是啟動快,資源占用小。因此對于構建隔離的標準化的運行環境,輕量級的PaaS(如dokku), 構建自動化測試和持續集成環境,以及一切可以橫向擴展的應用(尤其是需要快速啟停來應對峰谷的web應用)。
-
構建標準化的運行環境,現有的方案大多是在一個base OS上運行一套puppet/chef,或者一個image文件,其缺點是前者需要base OS許多前提條件,后者幾乎不可以修改(因為copy on write 的文件格式在運行時rootfs是read only的)。并且后者文件體積大,環境管理和版本控制本身也是一個問題。
</li> -
PaaS環境是不言而喻的,其設計之初和dotcloud的案例都是將其作為PaaS產品的環境基礎
</li> -
因為其標準化構建方法(buildfile)和良好的REST API,自動測試和持續集成/部署能夠很好的集成進來
</li> -
因為LXC輕量級的特點,其啟動快,而且docker能夠只加載每個container變化的部分,這樣資源占用小,能夠在單機環境下與KVM之類的虛擬化方案相比能夠更加快速和占用更少資源
</li> </ol>What Docker Can NOT Do
Docker并不是全能的,設計之初也不是KVM之類虛擬化手段的替代品,個人簡單總結了幾點
- Docker是基于Linux 64bit的,無法在windows/unix或32bit的linux環境下使用(雖然64-bit現在很普及了)
- LXC是基于cgroup等linux kernel功能的,因此container的guest系統只能是linux base的
- 隔離性相比KVM之類的虛擬化方案還是有些欠缺,所有container公用一部分的運行庫
- 網絡管理相對簡單,主要是基于namespace隔離
- cgroup的cpu和cpuset提供的cpu功能相比KVM的等虛擬化方案相比難以度量(所以dotcloud主要是安內存收費)
- docker對disk的管理比較有限
- container隨著用戶進程的停止而銷毀,container中的log等用戶數據不便收集 </ol>
- 隔離性 - 每個用戶實例之間相互隔離, 互不影響。 硬件虛擬化方法給出的方法是VM, LXC給出的方法是container,更細一點是kernel namespace
- 可配額/可度量 - 每個用戶實例可以按需提供其計算資源,所使用的資源可以被計量。硬件虛擬化方法因為虛擬了CPU, memory可以方便實現, LXC則主要是利用cgroups來控制資源
- 移動性 - 用戶的實例可以很方便地復制、移動和重建。硬件虛擬化方法提供snapshot和image來實現,docker(主要)利用AUFS實現
- 安全性 - 這個話題比較大,這里強調是host主機的角度盡量保護container。硬件虛擬化的方法因為虛擬化的水平比較高,用戶進程都是在KVM等虛擬機容器中翻譯運行的, 然而對于LXC, 用戶的進程是
lxc-start
進程的子進程, 只是在Kernel的namespace中隔離的, 因此需要一些kernel的patch來保證用戶的運行環境不會受到來自host主機的惡意入侵, dotcloud(主要是)利用kernelgrsec
patch解決的.
</ul>
- 每個namespace中的pid是有自己的pid=1的進程(類似
/sbin/init
進程) - 每個namespace中的進程只能影響自己的同一個namespace或子namespace中的進程
- 因為
/proc
包含正在運行的進程,因此在container中的pseudo-filesystem
的/proc目錄只能看到自己namespace中的進程 - 因為namespace允許嵌套,父namespace可以影響子namespace的進程,所以子namespace的進程可以在父namespace中看到,但是具有不同的pid </ol>
- Kernel namespaces (ipc, uts, mount, pid, network and user)
- Apparmor and SELinux profiles
- Seccomp policies
- Chroots (using pivot_root)
- Kernel capabilities
- Control groups (cgroups) </ul>
- The liblxc library
- Several language bindings (python3, lua and Go)
- A set of standard tools to control the containers
- Container templates </ul>
-
節省存儲空間 - 多個container可以共享base image存儲
</li> -
快速部署 - 如果要部署多個container,base image可以避免多次拷貝
</li> -
內存更省 - 因為多個container共享base image, 以及OS的disk緩存機制,多個container中的進程命中緩存內容的幾率大大增加
</li> -
升級更方便 - 相比于 copy-on-write 類型的FS,base-image也是可以掛載為可writeable的,可以通過更新base image而一次性更新其之上的container
</li> -
允許在不更改base-image的同時修改其目錄中的文件 - 所有寫操作都發生在最上層的writeable層中,這樣可以大大增加base image能共享的文件內容。
</li> </ol>以上5條 1-3 條可以通過 copy-on-write 的FS實現, 4可以利用其他的union mount方式實現, 5只有AUFS實現的很好。這也是為什么Docker一開始就建立在AUFS之上。
由于AUFS并不會進入linux主干 (According to Christoph Hellwig, linux rejects all union-type filesystems but UnionMount.), 同時要求kernel版本3.0以上(docker推薦3.8及以上),因此在RedHat工程師的幫助下在docker0.7版本中實現了driver機制, AUFS只是其中的一個driver, 在RHEL中采用的則是Device Mapper的方式實現的container文件系統,相關內容在下文會介紹。
參考文獻
[1]https://groups.google.com/forum/#!topic/docker-dev/KcCT0bACksY
[2]http://blog.docker.io/2013/11/docker-0-7-docker-now-runs-on-any-linux-distribution/
[3]http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-34-a
[4]http://aufs.sourceforge.net/aufs.html
[5]http://aufs.sourceforge.net/
[6]http://en.wikipedia.org/wiki/Aufs
[7]http://docs.docker.io/en/latest/terms/filesystem/
[8]http://docs.docker.io/en/latest/terms/layer/
[9]http://docs.docker.io/en/latest/terms/image/
[10]http://docs.docker.io/en/latest/terms/container/
GRSEC
grsec
是linux kernel安全相關的patch, 用于保護host防止非法入侵。由于其并不是docker的一部分,我們只進行簡單的介紹。grsec
可以主要從4個方面保護進程不被非法入侵:- 隨機地址空間 - 進程的堆區地址是隨機的
- 用只讀的memory management unit來管理進程流程, 堆區和棧區內存只包含數據結構/函數/返回地址和數據, 是non-executeable
- 審計和Log可疑活動
- 編譯期的防護 </ul>
-
Portable deployment across machines. Docker defines a format for bundling an application and all its dependencies into a single object which can be transferred to any docker-enabled machine, and executed there with the guarantee that the execution environment exposed to the application will be the same. Lxc implements process sandboxing, which is an important pre-requisite for portable deployment, but that alone is not enough for portable deployment. If you sent me a copy of your application installed in a custom lxc configuration, it would almost certainly not run on my machine the way it does on yours, because it is tied to your machine's specific configuration: networking, storage, logging, distro, etc. Docker defines an abstraction for these machine-specific settings, so that the exact same docker container can run - unchanged - on many different machines, with many different configurations.
</li> -
Application-centric. Docker is optimized for the deployment of applications, as opposed to machines. This is reflected in its API, user interface, design philosophy and documentation. By contrast, the lxc helper scripts focus on containers as lightweight machines - basically servers that boot faster and need less ram. We think there's more to containers than just that.
</li> -
Automatic build. Docker includes a tool for developers to automatically assemble a container from their source code, with full control over application dependencies, build tools, packaging etc. They are free to use make, maven, chef, puppet, salt, debian packages, rpms, source tarballs, or any combination of the above, regardless of the configuration of the machines.
</li> -
Versioning. Docker includes git-like capabilities for tracking successive versions of a container, inspecting the diff between versions, committing new versions, rolling back etc. The history also includes how a container was assembled and by whom, so you get full traceability from the production server all the way back to the upstream developer. Docker also implements incremental uploads and downloads, similar to "git pull", so new versions of a container can be transferred by only sending diffs.
</li> -
Component re-use. Any container can be used as an "base image" to create more specialized components. This can be done manually or as part of an automated build. For example you can prepare the ideal python environment, and use it as a base for 10 different applications. Your ideal postgresql setup can be re-used for all your future projects. And so on.
</li> -
Sharing. Docker has access to a public registry (http://index.docker.io) where thousands of people have uploaded useful containers: anything from redis, couchdb, postgres to irc bouncers to rails app servers to hadoop to base images for various distros. The registry also includes an official "standard library" of useful containers maintained by the docker team. The registry itself is open-source, so anyone can deploy their own registry to store and transfer private containers, for internal server deployments for example.
</li> -
Tool ecosystem. Docker defines an API for automating and customizing the creation and deployment of containers. There are a huge number of tools integrating with docker to extend its capabilities. PaaS-like deployment (Dokku, Deis, Flynn), multi-node orchestration (maestro, salt, mesos, openstack nova), management dashboards (docker-ui, openstack horizon, shipyard), configuration management (chef, puppet), continuous integration (jenkins, strider, travis), etc. Docker is rapidly establishing itself as the standard for container-based tooling.
</li> </ul>What we can do with Docker
有了docker這么個強有力的工具,更多的玩家希望了解圍繞docker能做什么
Sandbox
作為sandbox大概是container的最基本想法了 - 輕量級的隔離機制, 快速重建和銷毀, 占用資源少。用docker在開發者的單機環境下模擬分布式軟件部署和調試,可謂又快又好。 同時docker提供的版本控制和image機制以及遠程image管理,可以構建類似git的分布式開發環境。可以看到用于構建多平臺image的packer以及同一作者的vagrant已經在這方面有所嘗試了,筆者會后續的blog中介紹這兩款來自同一geek的精致小巧的工具。
PaaS
dotcloud、heroku以及cloudfoundry都試圖通過container來隔離提供給用戶的runtime和service,只 不過dotcloud采用docker, heroku采用LXC, cloudfoundry采用 自己開發的基于cgroup的warden。基于輕量級的隔離機制提供給用戶PaaS服務是比較常見的做法 - PaaS 提供給用戶的并不是OS而是runtime+service, 因此OS級別的隔離機制 向用戶屏蔽的細節已經足夠。而docker的很多分析文章提到『能夠運行任何應用的“PaaS”云』只是從image的角度說明docker可以從通過構 建image實現用戶app的打包以及標準服務service image的復用, 而非常見的buildpack的方式。
由于對Cloud Foundry和docker的了解, 接下來談談筆者對PaaS的認識。PaaS號稱的platform一直以來都被當做一組多語言的runtime和一組常用的middleware,提供這兩樣東西 即可被認為是一個滿足需求的PaaS。然而PaaS對能部署在其上的應用要求很高:
- 運行環境要簡單 - buildpack雖然用于解決類似問題,但仍然不是很理想
- 要盡可能的使用service - 常用的mysql, apache倒能理解,但是類似log之類的如果也要用service就讓用戶接入PaaS平臺, 讓用戶難以維護
- 要盡可能的使用"平臺" - 單機環境構建出目標PaaS上運行的實際環境比較困難,開發測試工作都離不開"平臺"
- 缺少可定制性 - 可選的中間件有限,難于調優和debug。 </ul>
-
通過docker run -v命令將外部存儲mount到container的目錄下,quota從Host方向限制,在device mapper driver中更采用實際的device因此更好控制。 參考[1]
</li> -
通過使用disk quota來限制AUFS的可操作文件大小。類似cloud foundry warden的方法, 維護一個UID池,每次創建container都從中取一個user name, 在container里和Host上用這個username創建用戶,在Host上用setquota限制該username的UID的disk. 網絡上由于docker采用veth的方式,可以采用
</li> </ul>tc
來控制host上的veth的設備。參考[2]參考文獻:
[1]https://github.com/dotcloud/docker/issues/111
[2]https://github.com/dotcloud/docker/issues/471
RHEL 6.5
這里簡單介紹下device mapper driver的思路,參考文獻[2]中的討論非常有價值。 docker的dirver要利用snapshot機制,起初的fs是一個空的ext4的目錄,然后寫入每個layer。每次創建image其實就是對其父image/base image進行snapshot, 然后在此snapshot上的操作都會被記錄在fs的metadata中和AUFS layer(沒讀代碼不是很理解?),
docker commit
將 diff信息在parent image上執行一遍. 這樣創建出來的image就可以同當前container的運行環境分離開獨立保存了。這里僅僅查看材料理解不是很透徹,還是需要深入代碼去了解詳情。貼出 mail list 的片段,如果有理解的請不吝賜教。
The way it works is that we set up a device-mapper thin provisioning pool with a single base device containing an empty ext4 filesystem. Then each time we create an image we take a snapshot of the parent image (or the base image) and manually apply the AUFS layer to this. Similarly we create snapshots of images when we create containers and mount these as the container filesystem."docker diff" is implemented by just scanning the container filesystem and the parent image filesystem, looking at the metadata for changes. Theoretically this can be fooled if you do in-place editing of a file (not changing the size) and reset the mtime/ctime, but in practice I think this will be good enough. "docker commit" uses the above diff command to get a list of changed files which are used to construct a tarball with files and AUFS whiteouts (for deletes). This means you can commit containers to images, run new containers based on the image, etc. You should be able to push them to the index too (although I've not tested this yet).
Docker looks for a "docker-pool" device-mapper device (i.e. /dev/mapper/docker-pool) when it starts up, but if none exists it automatically creates two sparse files (100GB for the data and 2GB for the metadata) and loopback mount these and sets these up as the block devices for docker-pool, with a 10GB ext4 fs as the base image.
This means that there is no need for manual setup of block devices, and that generally there should be no need to pre-allocate large amounts of space (the sparse files are small, and we things up so that discards are passed through all the way back to the sparse loopbacks, so deletes in a container should fully reclaim space.</pre>目前已知存在的問題是刪除的image的 block 文件沒有被刪除,見https://github.com/dotcloud/docker/issues/3182, 筆者發現此問題前4個小時作者給出了原因,看起來是kernel的issue,在討論中包含work around的方法。
參考文獻:
[1]http://blog.docker.io/2013/11/docker-0-7-docker-now-runs-on-any-linux-distribution/
[2]https://groups.google.com/forum/#!topic/docker-dev/KcCT0bACksY
Summary
本文總結了以下幾點內容
-
docker的介紹,包括由來、適用場景等
</li> -
docker背后的一系列技術 - namespace, cgroup, lxc, aufs等
</li> -
docker在利用LXC的同時提供了哪些創新
</li> -
筆者對docker這種container, PaaS的一些理解
</li> -
docker存在的問題和現有的解決思路
</li> </ol>希望能對想要了解docker的朋友有所幫助,更細致的了解還是得深入代碼, 了解個中原委。
docker@github - https://github.com/dotcloud/docker
docker_maillist - https://groups.google.com/forum/#!forum/docker-dev
</div>
http://tiewei.github.io/cloud/Docker-Getting-Start/
-
綜上所述部署在PaaS上的應用幾乎不具有從老平臺遷移到之上的可能,新應用也難以進入參數調優這種深入的工作。個人理解還是適合快速原型的展現,和短期應用的嘗試。
然而docker確實從另一個角度(類似IaaS+orchestration tools)實現了用戶運行環境的控制和管理,然而又基于輕量級的LXC機制,確實是一個了不起的嘗試。 筆者也認為IaaS + 靈活的orchestration tools(深入到app層面的管理 如bosh)是交付用戶環境最好的方式。
Open Solution
前文也提到docker存在disk/network不便限額和在較低版本kernel中(如RHEL的2.6.32)AUFS不支持的問題。本節嘗試給出解答。
disk/network quota
雖然cgroup提供IOPS之類的限制機制,但是從限制用戶能使用的磁盤大小和網絡帶寬上還是非常有限的。
Disk/network的quota現在有兩種思路:
安全永遠是相對的,這些方法只是告訴我們可以從這些角度考慮container類型的安全問題可以關注的方面。
參考文獻
[1] http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-44-g
What docker do more than LXC
看似docker主要的OS級虛擬化操作是借助LXC, AUFS只是錦上添花。那么肯定會有人好奇docker到底比LXC多了些什么。無意中發現 stackoverflow 上正好有人問這個問題, 回答者是Dotcloud的創始人,出于備忘目的原文摘錄如下。
http://stackoverflow.com/questions/17989306/what-does-docker-add-to-just-plain-lxc
On top of this low-level foundation of kernel features, Docker offers a high-level tool with several powerful functionalities:
針對1-2,有windows base應用的需求的基本可以pass了; 3-5主要是看用戶的需求,到底是需要一個container還是一個VM, 同時也決定了docker作為 IaaS 不太可行。 針對6,7雖然是docker本身不支持的功能,但是可以通過其他手段解決(disk quota,
mount --bind
)。總之,選用container還是vm, 就是在隔離性和資源復用性上做tradeoff另外即便docker 0.7能夠支持非AUFS的文件系統,但是由于其功能還不穩定,商業應用或許會存在問題,而AUFS的穩定版需要kernel 3.8, 所以如果想復制dotcloud的 成功案例,可能需要考慮升級kernel或者換用ubuntu的server版本(后者提供deb更新)。我想這也是為什么開源界更傾向于支持ubuntu的原因(kernel版本)
Docker Usage
由于篇幅所限,這里就不再展開翻譯,可參見鏈接 - http://docs.docker.io/en/latest/use/
Docker Build File
由于篇幅所限,這里就不再展開翻譯,可參見鏈接 - http://docs.docker.io/en/latest/use/builder/
Docker's Trick
What Docker Needs
Docker核心解決的問題是利用LXC來實現類似VM的功能,從而利用更加節省的硬件資源提供給用戶更多的計算資源。同VM的方式不同, LXC 其并不是一套硬件虛擬化方法 - 無法歸屬到全虛擬化、部分虛擬化和半虛擬化中的任意一個,而是一個操作系統級虛擬化方法, 理解起來可能并不像VM那樣直觀。所以我們從虛擬化要docker要解決的問題出發,看看他是怎么滿足用戶虛擬化需求的。
用戶需要考慮虛擬化方法,尤其是硬件虛擬化方法,需要借助其解決的主要是以下4個問題:
Linux Namespace (ns)
LXC所實現的隔離性主要是來自kernel的namespace, 其中
pid
,net
,ipc
,mnt
,uts
等namespace將container的進程, 網絡, 消息, 文件系統和hostname 隔離開。pid namespace
之前提到用戶的進程是
lxc-start
進程的子進程, 不同用戶的進程就是通過pid
namespace隔離開的,且不同 namespace 中可以有相同PID。具有以下特征:正是因為以上的特征,所有的LXC進程在docker中的父進程為docker進程,每個lxc進程具有不同的namespace。同時由于允許嵌套,因此可以很方便的實現
LXC in LXC
net namespace
有了
pid
namespace, 每個namespace中的pid能夠相互隔離,但是網絡端口還是共享host的端口。網絡隔離是通過net
namespace實現的, 每個net
namespace有獨立的 network devices, IP addresses, IP routing tables,/proc/net
目錄。這樣每個container的網絡就能隔離開來。 LXC在此基礎上有5種網絡類型,docker默認采用veth的方式將container中的虛擬網卡同host上的一個docker bridge連接在一起。ipc namespace
container中進程交互還是采用linux常見的進程間交互方法(interprocess communication - IPC), 包括常見的信號量、消息隊列和共享內存。然而同VM不同,container 的進程間交互實際上還是host上具有相同pid namespace中的進程間交互,因此需要在IPC資源申請時加入namespace信息 - 每個IPC資源有一個唯一的 32bit ID。
mnt namespace
類似
chroot
,將一個進程放到一個特定的目錄執行。mnt
namespace允許不同namespace的進程看到的文件結構不同,這樣每個 namespace 中的進程所看到的文件目錄就被隔離開了。同chroot
不同,每個namespace中的container在/proc/mounts
的信息只包含所在namespace的mount point。uts namespace
UTS("UNIX Time-sharing System") namespace允許每個container擁有獨立的hostname和domain name, 使其在網絡上可以被視作一個獨立的節點而非Host上的一個進程。
user namespace
每個container可以有不同的 user 和 group id, 也就是說可以以container內部的用戶在container內部執行程序而非Host上的用戶。
有了以上6種namespace從進程、網絡、IPC、文件系統、UTS和用戶角度的隔離,一個container就可以對外展現出一個獨立計算機的能力,并且不同container從OS層面實現了隔離。 然而不同namespace之間資源還是相互競爭的,仍然需要類似
ulimit
來管理每個container所能使用的資源 - LXC 采用的是cgroup
。參考文獻
[1]http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part
[2]http://lwn.net/Articles/531114/
Control Groups (cgroups)
cgroups
實現了對資源的配額和度量。cgroups
的使用非常簡單,提供類似文件的接口,在/cgroup
目錄下新建一個文件夾即可新建一個group,在此文件夾中新建task
文件,并將pid寫入該文件,即可實現對該進程的資源控制。具體的資源配置選項可以在該文件夾中新建子 subsystem ,{子系統前綴}.{資源項}
是典型的配置方法, 如memory.usage_in_bytes
就定義了該group 在subsystemmemory
中的一個內存限制選項。 另外,cgroups
中的 subsystem可以隨意組合,一個subsystem可以在不同的group中,也可以一個group包含多個subsystem - 也就是說一個 subsystem關于術語定義
A cgroup associates a set of tasks with a set of parameters for one or more subsystems.
A subsystem is a module that makes use of the task grouping facilities provided by cgroups to treat groups of tasks in particular ways. A subsystem is typically a "resource controller" that schedules a resource or applies per-cgroup limits, but it may be anything that wants to act on a group of processes, e.g. a</span> virtualization subsystem.</code></pre>
我們主要關心cgroups可以限制哪些資源,即有哪些subsystem是我們關心。
cpu : 在cgroup中,并不能像硬件虛擬化方案一樣能夠定義CPU能力,但是能夠定義CPU輪轉的優先級,因此具有較高CPU優先級的進程會更可能得到CPU運算。 通過將參數寫入
cpu.shares
,即可定義改cgroup的CPU優先級 - 這里是一個相對權重,而非絕對值。當然在cpu
這個subsystem中還有其他可配置項,手冊中有詳細說明。cpusets : cpusets 定義了有幾個CPU可以被這個group使用,或者哪幾個CPU可以供這個group使用。在某些場景下,單CPU綁定可以防止多核間緩存切換,從而提高效率
memory : 內存相關的限制
blkio : block IO相關的統計和限制,byte/operation統計和限制(IOPS等),讀寫速度限制等,但是這里主要統計的都是同步IO
net_cls, cpuacct , devices , freezer 等其他可管理項。
參考文獻
http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-24-c
http://en.wikipedia.org/wiki/Cgroups
https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt
LinuX Containers(LXC)
借助于namespace的隔離機制和cgroup限額功能,LXC提供了一套統一的API和工具來建立和管理container, LXC利用了如下 kernel 的features:
LXC 向用戶屏蔽了以上 kernel 接口的細節, 提供了如下的組件大大簡化了用戶的開發和使用工作:
LXC 旨在提供一個共享kernel的 OS 級虛擬化方法,在執行時不用重復加載Kernel, 且container的kernel與host共享,因此可以大大加快container的 啟動過程,并顯著減少內存消耗。在實際測試中,基于LXC的虛擬化方法的IO和CPU性能幾乎接近 baremetal 的性能(論據參見文獻[3]), 大多數數據有相比 Xen具有優勢。當然對于KVM這種也是通過Kernel進行隔離的方式, 性能優勢或許不是那么明顯, 主要還是內存消耗和啟動時間上的差異。在參考文獻[4]中提到了利用iozone進行 Disk IO吞吐量測試KVM反而比LXC要快,而且筆者在device mapping driver下重現同樣case的實驗中也確實能得到如此結論。參考文獻[5]從網絡虛擬化中虛擬路由的場景(個人理解是網絡IO和CPU角度)比較了 KVM和LXC, 得到結論是KVM在性能和隔離性的平衡上比LXC更優秀 - KVM在吞吐量上略差于LXC, 但CPU的隔離可管理項比LXC更明確。
關于CPU, DiskIO, network IO 和 memory 在KVM和LXC中的比較還是需要更多的實驗才能得出可信服的結論。
參考文獻
[1]http://linuxcontainers.org/
[2]http://en.wikipedia.org/wiki/LXC
[3]http://marceloneves.org/papers/pdp2013-containers.pdf (性能測試)
[4]http://www.spinics.net/lists/linux-containers/msg25750.html (與KVM IO比較)
[5]http://article.sciencepublishinggroup.com/pdf/10.11648.j.ajnc.20130204.11.pdf
AUFS
Docker對container的使用基本是建立唉LXC基礎之上的,然而LXC存在的問題是難以移動 - 難以通過標準化的模板制作、重建、復制和移動 container。 在以VM為基礎的虛擬化手段中,有image和snapshot可以用于VM的復制、重建以及移動的功能。想要通過container來實現快速的大規模 部署和更新, 這些功能不可或缺。 Docker正是利用AUFS來實現對container的快速更新 - 在docker0.7中引入了storage driver, 支持AUFS, VFS, device mapper, 也為BTRFS以及ZFS引入提供了可能。 但除了AUFS都未經過dotcloud的線上使用,因此我們還是從AUFS的角度介紹。
AUFS (AnotherUnionFS) 是一種 Union FS, 簡單來說就是支持將不同目錄掛載到同一個虛擬文件系統下(unite several directories into a single virtual filesystem)的文件系統, 更進一步地, AUFS支持為每一個成員目錄(AKA branch)設定'readonly', 'readwrite' 和 'whiteout-able' 權限, 同時AUFS里有一個類似 分層的概念, 對 readonly 權限的branch可以邏輯上進行修改(增量地, 不影響readonly部分的)。通常 Union FS有兩個用途, 一方面可以實現不借助 LVM, RAID 將多個disk和掛在到一個目錄下, 另一個更常用的就是將一個readonly的branch和一個writeable的branch聯合在一起,Live CD正是基于此可以允許在 OS image 不變的基礎上允許用戶在其上進行一些寫操作。Docker在AUFS上構建的container image也正是如此,接下來我們從啟動container中的linux為例介紹docker在AUFS特性的運用。
典型的Linux啟動到運行需要兩個FS - bootfs + rootfs (從功能角度而非文件系統角度)
bootfs (boot file system) 主要包含 bootloader 和 kernel, bootloader主要是引導加載kernel, 當boot成功后 kernel 被加載到內存中后 bootfs就被umount了. rootfs (root file system) 包含的就是典型 Linux 系統中的
/dev
,/proc
,/bin
,/etc
等標準目錄和文件。由此可見對于不同的linux發行版, bootfs基本是一致的, rootfs會有差別, 因此不同的發行版可以公用bootfs 如下圖:
典型的Linux在啟動后,首先將 rootfs 置為 readonly, 進行一系列檢查, 然后將其切換為 "readwrite" 供用戶使用。在docker中,起初也是將 rootfs 以readonly方式加載并檢查,然而接下來利用 union mount 的將一個 readwrite 文件系統掛載在 readonly 的rootfs之上,并且允許再次將下層的 file system設定為readonly 并且向上疊加, 這樣一組readonly和一個writeable的結構構成一個container的運行目錄, 每一個被稱作一個Layer。如下圖:
得益于AUFS的特性, 每一個對readonly層文件/目錄的修改都只會存在于上層的writeable層中。這樣由于不存在競爭, 多個container可以共享readonly的layer。 所以docker將readonly的層稱作 "image" - 對于container而言整個rootfs都是read-write的,但事實上所有的修改都寫入最上層的writeable層中, image不保存用戶狀態,可以用于模板、重建和復制。
上層的image依賴下層的image,因此docker中把下層的image稱作父image,沒有父image的image稱作base image
因此想要從一個image啟動一個container,docker會先加載其父image直到base image,用戶的進程運行在writeable的layer中。所有parent image中的數據信息以及 ID、網絡和lxc管理的資源限制等具體container的配置,構成一個docker概念上的container。如下圖:
由此可見,采用AUFS作為docker的container的文件系統,能夠提供如下好處:
-
-
-