Docker

Docker

Docker 是一個開源的套用[漢語詞語]容器引擎,讓開發者可以打包他們的套用以及依賴包到一個可移植的容器中,然後發布到任何流行的 Linux 機器上,也可以實現虛擬化。容器是完全使用沙箱機制,相互之間不會有任何接口(類似 iPhone 的 app)。幾乎沒有性能開銷,可以很容易地在機器和數據中心中運行。最重要的是,他們不依賴於任何語言、框架包括系統。

基本信息

簡介

Docker是一個開源的套用容器引擎,讓開發者可以打包他們的套用以及依賴包到一個可移植的容器中,然後發布到任何流行的Linux機器上,也可以實現虛擬化,容器是完全使用沙箱機制,相互之間不會有任何接口。

一個完整的Docker有以下幾個部分組成:

dockerClient客戶端

DockerDaemon守護進程

DockerImage鏡像

DockerContainer容器

起源

Docker是PaaS提供商dotCloud開源的一個基於LXC的高級容器引擎,原始碼託管在Github上,基於go語言並遵從Apache2.0協定開源。

Docker自2013年以來非常火熱,無論是從github上的代碼活躍度,還是Redhat在RHEL6.5中集成對Docker的支持,就連Google的ComputeEngine也支持docker在其之上運行。

一款開源軟體能否在商業上成功,很大程度上依賴三件事-成功的usercase(用例),活躍的社區和一個好故事。dotCloud自家的PaaS產品建立在docker之上,長期維護且有大量的用戶,社區也十分活躍,接下來我們看看docker的故事。

環境管理複雜-從各種OS到各種中間件到各種app,一款產品能夠成功作為開發者需要關心的東西太多,且難於管理,這個問題幾乎在所有現代IT相關行業都需要面對。

雲計算時代的到來-AWS的成功,引導開發者將套用轉移到cloud上,解決了硬體管理的問題,然而中間件相關的問題依然存在(所以openstackHEAT和AWScloudformation都著力解決這個問題)。開發者思路變化提供了可能性。

虛擬化手段的變化-cloud時代採用標配硬體來降低成本,採用虛擬化手段來滿足用戶按需使用的需求以及保證可用性和隔離性。然而無論是KVM還是Xen在docker看來,都在浪費資源,因為用戶需要的是高效運行環境而非OS,GuestOS既浪費資源又難於管理,更加輕量級的LXC更加靈活和快速

LXC的移動性-LXC在linux2.6的kernel里就已經存在了,但是其設計之初並非為雲計算考慮的,缺少標準化的描述手段和容器的可遷移性,決定其構建出的環境難於遷移和標準化管理(相對於KVM之類image和snapshot的概念)。docker就在這個問題上做出實質性的革新。這是docker最獨特的地方。

DockerDocker

面對上述幾個問題,docker構想是交付運行環境如同海運,OS如同一個貨輪,每一個在OS基礎上的軟體都如同一個貨櫃,用戶可以通過標準化手段自由組裝運行環境,同時貨櫃的內容可以由用戶自定義,也可以由專業人員製造。這樣,交付一個軟體,就是一系列標準化組件的集合的交付,如同樂高積木,用戶只需要選擇合適的積木組合,並且在最頂端署上自己的名字(最後一個標準化組件是用戶的app)。這也就是基於docker的PaaS產品的原型。

特性

在docker的網站上提到了docker的典型場景:

Automating the packaging and deployment of applications

Creation of lightweight, private PAAS environments

Automated testing and continuous integration/deployment

Deploying and scaling web apps, databases and backend services

由於其基於LXC的輕量級虛擬化的特點,docker相比KVM之類最明顯的特點就是啟動快,資源占用小。因此對於構建隔離的標準化的運行環境,輕量級的PaaS(如dokku), 構建自動化測試和持續集成環境,以及一切可以橫向擴展的套用(尤其是需要快速啟停來應對峰谷的web套用)。

構建標準化的運行環境,現有的方案大多是在一個baseOS上運行一套puppet/chef,或者一個image檔案,其缺點是前者需要base OS許多前提條件,後者幾乎不可以修改(因為copy on write 的檔案格式在運行時rootfs是read only的)。並且後者檔案體積大,環境管理和版本控制本身也是一個問題。

PaaS環境是不言而喻的,其設計之初和dotcloud的案例都是將其作為PaaS產品的環境基礎

因為其標準化構建方法(buildfile)和良好的REST API,自動測試和持續集成/部署能夠很好的集成進來

因為LXC輕量級的特點,其啟動快,而且docker能夠只載入每個container變化的部分,這樣資源占用小,能夠在單機環境下與KVM之類的虛擬化方案相比能夠更加快速和占用更少資源

原理

Docker核心解決的問題是利用LXC來實現類似VM的功能,從而利用更加節省的硬體資源提供給用戶更多的計算資源。同VM的方式不同, LXC 其並不是一套硬體虛擬化方法 - 無法歸屬到全虛擬化、部分虛擬化和半虛擬化中的任意一個,而是一個作業系統級虛擬化方法, 理解起來可能並不像VM那樣直觀。所以我們從虛擬化要docker要解決的問題出發,看看他是怎么滿足用戶虛擬化需求的。

用戶需要考慮虛擬化方法,尤其是硬體虛擬化方法,需要藉助其解決的主要是以下4個問題:

隔離性 - 每個用戶實例之間相互隔離, 互不影響。 硬體虛擬化方法給出的方法是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(主要是)利用kernel grsec patch解決的.

LinuxNamespace(ns)

LXC所實現的隔離性主要是來自kernel的namespace, 其中pid, net, ipc, mnt, uts 等namespace將container的進程, 網路, 訊息, 檔案系統和hostname 隔離開。

pid namespace

之前提到用戶的進程是lxc-start進程的子進程, 不同用戶的進程就是通過pidnamespace隔離開的,且不同 namespace 中可以有相同PID。具有以下特徵:

每個namespace中的pid是有自己的pid=1的進程(類似/sbin/init進程)

每個namespace中的進程只能影響自己的同一個namespace或子namespace中的進程

因為/proc包含正在運行的進程,因此在container中的pseudo-filesystem的/proc目錄只能看到自己namespace中的進程

因為namespace允許嵌套,父namespace可以影響子namespace的進程,所以子namespace的進程可以在父namespace中看到,但是具有不同的pid

正是因為以上的特徵,所有的LXC進程在docker中的父進程為docker進程,每個lxc進程具有不同的namespace。同時由於允許嵌套,因此可以很方便的實現 LXC in LXC

net namespace

有了 pid namespace, 每個namespace中的pid能夠相互隔離,但是網路連線埠還是共享host的連線埠。網路隔離是通過netnamespace實現的,

每個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。

ControlGroups(cgroups)

cgroups 實現了對資源的配額和度量。 cgroups 的使用非常簡單,提供類似檔案的接口,在 /cgroup目錄下新建一個資料夾即可新建一個group,在此資料夾中新建task檔案,並將pid寫入該檔案,即可實現對該進程的資源控制。具體的資源配置選項可以在該資料夾中新建子 subsystem ,{子系統前綴}.{資源項} 是典型的配置方法,

如memory.usage_in_bytes 就定義了該group 在subsystem memory中的一個記憶體限制選項。

另外,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

virtualization subsystem.

我們主要關心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 等其他可管理項。

Linux容器(LXC)

藉助於namespace的隔離機制和cgroup限額功能,LXC提供了一套統一的API和工具來建立和管理container, LXC利用了如下 kernel 的features:

Kernel namespaces (ipc, uts, mount, pid, network and user)

Apparmor and SELinux profiles

Seccomp policies

Chroots (using pivot_root)

Kernel capabilities

Control groups (cgroups)

LXC 向用戶禁止了以上 kernel 接口的細節, 提供了如下的組件大大簡化了用戶的開發和使用工作:

The liblxc library

Several language bindings (python3, lua and Go)

A set of standard tools to control the containers

Container templates

LXC 旨在提供一個共享kernel的 OS 級虛擬化方法,在執行時不用重複載入Kernel, 且container的kernel與host共享,因此可以大大加快container的 啟動過程,並顯著減少記憶體消耗。在實際測試中,基於LXC的虛擬化方法的IO和CPU性能幾乎接近 baremetal 的性能

, 大多數數據有相比 Xen具有優勢。當然對於KVM這種也是通過Kernel進行隔離的方式, 性能優勢或許不是那么明顯, 主要還是記憶體消耗和啟動時間上的差異。在參考文獻

中提到了利用iozone進行 Disk IO吞吐量測試KVM反而比LXC要快,而且筆者在device mapping driver下重現同樣case的實驗中也確實能得到如此結論。參考文獻從網路虛擬化中虛擬路由的場景(網路IO和CPU角度)比較了KVM和LXC, 得到結論是KVM在性能和隔離性的平衡上比LXC更優秀 - KVM在吞吐量上略差於LXC, 但CPU的隔離可管理項比LXC更明確。

關於CPU, DiskIO, network IO 和 memory 在KVM和LXC中的比較還是需要更多的實驗才能得出可信服的結論。

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里有一個類似

圖1圖1

分層的概念, 對 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 (從功能角度而非檔案系統角度)(圖1)

bootfs (boot file system) 主要包含 bootloader 和 kernel, bootloader主要是引導載入kernel, 當boot成功後 kernel 被載入到記憶體中後 bootfs就被umount了.

rootfs (root file system) 包含的就是典型 Linux 系統中的 /dev, /proc, /bin, /etc 等標準目錄和檔案。

圖2圖2

由此可見對於不同的linux發行版, bootfs基本是一致的, rootfs會有差別, 因此不同的發行版可以公用bootfs 如下(圖2):

典型的Linux在啟動後,首先將 rootfs 置為 readonly, 進行一系列檢查, 然後將其切換為 “readwrite” 供用戶使用。在docker中,起初也是將 rootfs 以readonly方式載入並檢查,然而接下來利用 union mount 的將一個 readwrite 檔案系統掛載在 readonly 的rootfs之上,並且允許再次將下層的 file system設定為readonly 並且向上疊加, 這樣一組readonly和一個writeable的結構構成一個container的運行目錄, 每一個被稱作一個Layer。如下(圖3):

圖3圖3

得益於AUFS的特性, 每一個對readonly層檔案/目錄的修改都

只會存在於上層的writeable層中。這樣由於不存在競爭, 多個container可以共享readonly的layer。

所以docker將readonly的層稱作 “image” - 對於container而言整個rootfs都是read-write的,但事實上所有的修改都寫入最上層的writeable層中,

image不保存用戶狀態,可以用於模板、重建和複製。

(圖4、5)

圖4圖4
圖5圖5
圖6圖6

上層的image依賴下層的image,因此docker中把下層的image稱作父image,沒有父image的image稱作base image (圖6)

圖7圖7

因此想要從一個image啟動一個container,docker會先載入其父image直到base image,用戶的進程運行在writeable的layer中。所有parent image中的數據信息以及

ID、網路和lxc管理的資源限制等具體container的配置,構成一個docker概念上的container。如下(圖7):

由此可見,採用AUFS作為docker的container的檔案系統,能夠提供如下好處:

節省存儲空間 - 多個container可以共享base image存儲

快速部署 - 如果要部署多個container,base image可以避免多次拷貝

記憶體更省 - 因為多個container共享base image, 以及OS的disk快取機制,多個container中的進程命中快取內容的幾率大大增加

升級更方便 - 相比於 copy-on-write 類型的FS,base-image也是可以掛載為可writeable的,可以通過更新base image而一次性更新其之上的container

允許在不更改base-image的同時修改其目錄中的檔案 - 所有寫操作都發生在最上層的writeable層中,這樣可以大大增加base image能共享的檔案內容。

以上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檔案系統。

GRSEC

grsec是linux kernel安全相關的patch, 用於保護host防止非法入侵。由於其並不是docker的一部分,我們只進行簡單的介紹。

grsec可以主要從4個方面保護進程不被非法入侵:

隨機地址空間 - 進程的堆區地址是隨機的

用唯讀的memory management unit來管理進程流程, 堆區和棧區記憶體只包含數據結構/函式/返回地址和數據, 是non-executeable

審計和Log可疑活動

編譯期的防護

安全永遠是相對的,這些方法只是告訴我們可以從這些角度考慮container類型的安全問題可以關注的方面。

操作方法

隨著Docker在雲計算市場中領先地位的日益穩固,容器技術也成為了一種主流技術。為了對用戶的應用程式使用容器技術,可遵循以下五個步驟。

Docker容器技術已在雲計算市場中風靡一時了,而眾多主流供應商則面臨著技術落後的窘境。那么,是什麼讓Docker容器技術變得如此受歡迎呢?對於剛入門的新手來說,容器技術可實現不同雲計算之間應用程式的可移植性,以及提供了一個把應用程式拆分為分散式組件的方法。此外,用戶還可以管理和擴展這些容器成為集群。

在企業用戶準備把應用程式遷往容器之前,理解應用程式的遷移過程是非常重要的。這裡將介紹把用戶應用程式遷往Docker容器的五個基本步驟。

步驟1:分解

一般來說,應用程式都是複雜的,它們都有很多的組件。例如,大多數應用程式都需要資料庫或中間件服務的支持以實現對數據的存儲、檢索和集成。所以,需要通過設計和部署把這些服務拆分成為它們自己的容器。如果一個應用程式能夠被拆分成為越多的分散式組件,那么應用程式擴展的選擇則越多。但是,分散式組件越多也意味著管理的複雜性越高。

步驟2:選擇一個基礎映像

當執行應用程式遷移時,應儘量避免推倒重來的做法。搜尋Docker註冊庫找到一個基本的Docker映像並將其作為應用程式的基礎來使用。

隨著時間的推移,企業將會發現這些Docker註冊庫中基本映像的價值所在。請記住,Docker支持著一個Docker開發人員社區,所以項目的成功與否很大程度上取決於用戶對於映像管理和改良的參與度。

步驟3:解決安全性和管理問題

安全性和管理應當是一個高優先權的考慮因素;企業用戶不應再把它們當作應用程式遷移至容器的最後一步。反之,企業必須從一開始就做好安全性和管理的規劃,把它們的功能納入應用程式的開發過程中,並在應用程式運行過程中積極主動地關注這些方面。這就是企業應當花大功夫的地方。

基於容器的應用程式是分散式應用程式。企業應當更新較老的應用程式以支持聯合身份管理方法,這將非常有利於確保分散式應用程式的安全性。為了做到這一點,應為每一個應用程式組件和數據提供一個唯一的標識符,這個標識符可允許企業在一個細粒度的級別上進行安全性管理。企業用戶還應當增加一個日誌記錄的方法。

步驟4:增加代碼

為了創建映像,企業用戶需要使用一個Dockerfile來定義映像開發的必要步驟。一旦創建了映像,企業用戶就應將其添加至Docer Hub。

步驟5:配置、測試、部署

應對在容器中運行的應用程式進行配置,以便於讓應用程式知道可以在哪裡連線外部資源或者應用程式集群中的其他容器。企業用戶可以把這些配置部署在容器中或使用環境變數。

對基於容器的應用程式進行測試類似於對其他分散式應用程式的測試。企業可以對每個容器進行組件測試,並將容器集群作為一個整體進行測試。 確定應用程式應如何能夠在負載增加的情況下進行擴展。如果用戶正在使用一個集群管理器(例如Swarm),則可測試其性能。

最後,把容器部署到實際生產環境中。為了積極主動地關注基於容器的應用程式的運行狀況,可考慮實施必要的監控和管理機制 。確保打開日誌記錄功能。

很多應用程式遷移至雲計算都是採用容器技術的。雖然遷移有一點複雜,但是容器可以保護應用程式投資並賦予了它一個更長的使用壽命。

對比LXC

看似docker主要的OS級虛擬化操作是藉助LXC, AUFS只是錦上添花。那么肯定會有人好奇docker到底比LXC多了些什麼。無意中發現 stackoverflow 上正好有人問這個問題,

回答者是Dotcloud的創始人,出於備忘目的原文摘錄如下。

On top of this low-level foundation of kernel features, Docker offers a high-level tool with several powerful functionalities:

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.

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.

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.

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.

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.

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.

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.

套用

有了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。

綜上所述部署在PaaS上的套用幾乎不具有從老平台遷移到之上的可能,新套用也難以進入參數調優這種深入的工作。個人理解還是適合快速原型的展現,和短期套用的嘗試。

然而docker確實從另一個角度(類似IaaS+orchestration tools)實現了用戶運行環境的控制和管理,然而又基於輕量級的LXC機制,確實是一個了不起的嘗試。

筆者也認為IaaS + 靈活的orchestration tools(深入到app層面的管理 如bosh)是交付用戶環境最好的方式。

國內也已經開始出現基於Docker的PaaS。2015年3月11日,雲雀Alauda雲平台正式開啟內測,對外提供基於Docker的PaaS服務。

OpenSolution

前文也提到docker存在disk/network不便限額和在較低版本kernel中(如RHEL的2.6.32)AUFS不支持的問題。

disk/network quota

雖然cgroup提供IOPS之類的限制機制,但是從限制用戶能使用的磁碟大小和網路頻寬上還是非常有限的。

Disk/network的quota有兩種思路:

通過docker run -v命令將外部存儲mount到container的目錄下,quota從Host方向限制,在device mapper driver中更採用實際的device因此更好控制。

通過使用disk quota來限制AUFS的可操作檔案大小。類似cloud foundry warden的方法, 維護一個UID池,每次創建container都從中取一個user name, 在container里和Host上用這個username創建用戶,在Host上用setquota限制該username的UID的disk. 網路上由於docker採用veth的方式,可以採用tc來控制host上的veth的設備。

RHEL 6.5

這裡簡單介紹下device mapper driver的思路:

docker的dirver要利用snapshot機制,起初的fs是一個空的ext4的目錄,然後寫入每個layer。每次創建image其實就是對其父image/base image進行snapshot,

然後在此snapshot上的操作都會被記錄在fs的metadata中和AUFS layer,docker commit將 diff信息在parent image上執行一遍.

這樣創建出來的image就可以同當前container的運行環境分離開獨立保存了。

部署方式

1.Docker鏡像

Docker 1.3 現在支持數字簽名來確認官方倉庫鏡像的起源和完整性。因該功能仍在開發中所以Docker將拋出警告但不會阻止鏡像的實際運行。

通常確保鏡像只從受信庫中檢索並且不使用—insecure-registry=[]命令項。

2.網路命名空間

在默認情況下,Docker REST API用來控制容器通過系統Docker守護進程是唯一能夠通過Unix域套接字的方式暴露出來。在Docker上開啟一個TCP連線埠(即 當引導Docker守護進程時必須使用 -H 選項綁定地址)將允許任何人通過該連線埠訪問容器,有可能獲得主機上的root訪問許可權以及在某些場景下本地用戶所屬的Docker組。

3.日誌和審核

收集和歸檔與Docker相關的安全日誌來達到審核和監督的目的。從host,可以使用下面的命令來訪問容器外的日誌檔案:

docker run -v /dev/log:/dev/log/bin/sh

使用Docker命令內置:

docker logs ... (-f to follow log output)

日誌檔案也可以從持續存儲導出到一個使用壓縮檔裡面:

docker export ...

4.SELinux 或 AppArmor

Linux的內部安全模組,例如通過訪問控制的安全策略來配置安全增強型Linux(SELinux)和AppArmor,從而實現強制性的訪問控制(MAC)一套有限的系統資源的限制進程,如果先前已經安裝和配置過SELinux,那么它可以使用setenforce 1在容器中被激活。Docker程式的SELinux支持是默認無效的,並且需要使用—selinux功能來被激活。通過使用新增的—security-opt來載入SELinux或者AppArmor的策略對容器的標籤限制進行配置。該功能已經在Docker版本1.3中介紹過。例如:

docker run --security-opt=secdriver:name:value -i -t centos bash

5.守護特權

不要使用--privileged命令行選項。這本來允許容器來訪問主機上的所有設備,並為容器提供一個特定的LSM配置(例如SELinux或AppArmor),而這將給予如主機上運行的程式同樣水平的訪問。避免使用--privileged有助於減少主機泄露的攻擊面和潛力。然而,這並不意味著程式將沒有優先權的運行,當然這些優先權在最新的版本中還是必須的。發布新程式和容器的能力只能被賦予到值得信任的用戶上。通過利用-u選項儘量減少容器內強制執行的許可權。例如:

docker run -u-it/bin/bash

Docker組的任何用戶部分可能最終從容器中的主機上獲得根源。

6.cgroups

為了防止通過系統資源耗盡的DDoS攻擊,可以使用特定的命令行參數被來進行一些資源限制。

CPU使用率:

docker run -it --rm --cpuset=0,1 -c 2 ...

記憶體使用:

docker run -it --rm -m 128m ...

存儲使用:

docker -d --storage-opt dm.basesize=5G

磁碟I/O:

不支持Docker。BlockIO*屬性可以通過systemd暴露,並且在支持作業系統中被用來控制磁碟的使用配額。

7.二進制SUID/GUID

SUID和GUID二進制檔案不穩定的時候容易受到攻擊,而這個時候是很危險的,,導致任意代碼執行(如緩衝區溢出),因為它們會進程的檔案所有者或組的上下文中運行。如果可能的話,禁止SUID和SGID使用特定的命令行參數來降低容器的功能。

docker run -it --rm --cap-drop SETUID --cap-drop SETGID ...

另一選擇,可以考慮運用通過安裝有nosuid屬性的檔案系統來移除掉SUID能力。

最後一個選擇是從系統中徹底刪除不需要的SUID和GUID二進制檔案。這些類型的二進制檔案可以在Linux系統中運行以下命令而找到:

find / -perm -4000 -exec ls -l {} \; 2>/dev/null

find / -perm -2000 -exec ls -l {} \; 2>/dev/null

可以使用類似於下面的命令將SUID和GUID檔案許可權刪除然後:

sudo chmod u-s filename sudo chmod -R g-s directory

8.設備控制組(/dev/*)

如果需要,使用內置的設備選項(不使用-v與--privileged參數)。此功能在推出1.2版本。

例如(音效卡使用):

docker run --device=/dev/snd:/dev/snd …

9.服務和套用

如果一個Docker容器有可能被泄露,為了減少橫向運動的潛力,考慮隔離極易受影響的服務(如在主機或虛擬機上運行SSH服務)。此外,不要運行容器內不受信任的特許操作的應用程式。

10.安裝項

當使用本機容器庫時(即libcontainer)Docker就會自動處理這個。

但是,使用LXC容器庫時,敏感的安裝點最好通過運用唯讀許可權來手動安裝,其中包括:

/sys

/proc/sys

/proc/sysrq-trigger

/proc/irq

/proc/bus

安裝許可權應在以後刪除,以防止重新掛載。

11.Linux核心

使用系統提供的更新工具來確保核心是實最新的(如apt-get,yum,等)。過時的核心可能更脆弱,並且被暴露一些漏洞。使用GRSEC或PAX來加強核心,即例如對記憶體破壞的漏洞來提供更高的安全性。

12.用戶命名空間

Docker不支持用戶的命名空間,但是一個開發功能。UID映射由LXC程式驅動,但在本機libcontainer庫中不被支持。該功能將允許Docker程式像一個沒有特權的用戶在主機上運行,但顯示出來的是和在容器中運行的一樣。

13.libseccomp(和seccomp-bpf 擴展)

libseccomp庫允許在基於一個白名單的方法上限制Linux核心的系統調用程式的使用。對於系統操作來說,不是很重要的系統調用程式,最好被禁用,以防止被破壞的容器被濫用或誤用。

此功能工作正在進行中(LXC驅動程式中已經有了,但是在libcontainer中海沒有完成,雖然現在是默認值)。使用LXC驅動程式來重啟Docker程式:

docker -d -e lxc

如何生成seccomp配置的說明都在“的contrib”資料夾中的Docker GitHub的資源庫。以後可以用下面的命令來創建一個基於Docker容器的LXC:

docker run --lxc-conf="lxc.seccomp=$file"

14.性能

只要可能,就將Linux性能降低到最小。Docker默認的功能包括:chown, dac_override, fowner, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, setfcap, and audit_write.

從控制行來啟動容器時,可以通過下述來進行控制:

--cap-add=[] 或者--cap-drop=[].

例如:

docker run --cap-drop setuid --cap-drop setgid -ti/bin/sh

15.多租環境

由於Docker容器核心的共享性質,責任分離在多租戶環境中不能安全地實現。建議容器在那些沒有其它的目的,也不用於敏感操作的主機上運行。可以考慮通過Docker控制來將所有服務移動到容器中。如果可能的話,通過使用--icc= false將跨容器通信降到最低,並必要時指定-link與Docker運行,或通過—export=port,不在主機上發布,而在容器上暴露一個連線埠。相互信任的容器的映像組來分離機器。

16.完全虛擬化

使用一個完整的虛擬化解決方案包含Docker,如KVM。這將阻止一個核心漏洞在Docker鏡像中被利用導致容器擴為主系統。

Docker鏡像能夠嵌套來提供該KVM虛擬層,參考Docker-in-Docker utility 中所示。

17.安全審核

對你的主系統和容器定期進行安全審核以查明錯誤配置或漏洞,這些能使你的系統損壞。

工具實踐

Docker推出的一個名為DockerContentTrust(DCT)的新功能,它可幫助IT專業人士確保Docker的安全性。DCT使用了一個公共密鑰基礎設施(PKI)的方法,它提供了兩個不同的密鑰:一個離線(root)密鑰和一個標記(每次入庫)密鑰,當第一次發布者推出鏡像時它可創建和存儲客戶端。

此舉有助於彌補正在使用惡意容器這一最大的漏洞。DCT還生成了一個時間戳密鑰,它可保護系統免受重放攻擊,即運行過期的標記內容。這解決了上面提及容器具有不同安全補丁等級的問題。

為了解決針對容器安全性的問題,包括Docker在內的眾多公司都為Docker發布了安全基準。這套標準為確保Docker容器的安全性提供了指導。全篇118頁的文檔囊括了部署Docker容器的84個最佳實踐以及一個涉及所有內容的檢查清單。

那么,如果你決定自行負責確保Docker容器的安全性,但又不知道從何入手,我們在這裡為你提供了一些建議:

閱讀上面提及的Docker安全基準檔案。重點關注與如何部署基於容器的應用程式相關的建議和最佳實踐。這真的是有助於緩解你的財務壓力,認真考慮大部分因糟糕設計而導致的Docker安全性問題。

考慮你的特定安全性需求。這將促使你選擇正確的工具和方法。很多使用容器技術的企業對於他們基於容器的應用程式要么安全措施不足,要么安全措施過足。

儘可能多地進行測試。容器技術是新技術,因此我們需要搞清楚哪些是能夠發揮作用,哪些是無用的,而要做到這一點的唯一方法就是進行安全性方面的測試,例如滲透測試。

容器安全性的發展趨勢可能會與虛擬化安全性一樣。雖然安全性從第一台虛擬機部署開始就是一個問題,但是多年以來積累下來的良好安全性實踐、架構和工具都證明了其有效性。我們相信,Docker容器安全性的問題也同樣能夠得到較好解決。

使用案例

D

DockerDocker
ocker是一個命令行工具,它提供了中央“docker”執行過程中所需的所有工具。這使得Docker的操作非常簡單。一些例子可以檢查運行中的容器的狀態:

或檢查可用的鏡像及其版本的列表:

另一個例子是顯示一個鏡像的歷史:

上面的命令顯示了命令行界面操作的方便快捷。你只需要指定鏡像ID的前幾個字元就可以。你可以看到只需要“d95”就能顯示d95238078ab0鏡像的所有歷史。

你可能會注意到該鏡像非常小。這是因為Docker從父鏡像建立增量鏡像,只存儲每個容器的更改。因此,如果你有一個300MB的父鏡像,如果你在容器中安裝了50MB的額外套用或服務,你的容器和生成鏡像可能只有50MB。

你可以用Dockerfiles自動化Docker容器的創建過程。Dockerfiles是含有單個容器性能規範的檔案。例如,你可以創建一個Dockerfiles來建立一個Ubuntu容器,在新容器內運行一些命令、安裝軟體或執行其他任務,然後啟動容器。

容器網路

Docker早期版本中的網路基於主機橋接,但是Docker1.0包含了一種新形式的網路,允許容器直接連線到主機乙太網接口。默認情況下,一個容器有一個迴路以及一個連線到默認內部橋接的接口,但是如果需要的話也可以配製成直接訪問。通常,直接訪問比橋接的速度更快。

然而,橋接方法在許多情況下是非常有用的。橋接是通過主機自動創建一個內部網路適配器並為其分配一個主機本身尚未使用的子網。然後,當新的容器連線到這座橋,它們的地址進行自動分配。容器啟動時你可以將其連線到主機接口或連線埠,因此運行Apache的容器可能啟動並連線到主機上的TCP連線埠8080(或隨機連線埠)。通過使用腳本和管理控制,你可以在任何地方啟動Docker,連線連線埠並將其傳達到需要使用該服務的套用或服務堆疊的其他部分。

在Hyper-V伺服器上Docker主機備份方法

要在Hyper-V伺服器上創建Docker主機,您需要下載並且安裝OpenSSH以及Windows版本的DockerMachine。您還應該將OpenSSH二進制檔案添加到您的Hyper-V伺服器路徑以便DockerMachine可以找到它們。

一旦所需的組件就緒,創建Docker主機如同運行一條命令行一樣輕而易舉。打開命令提示符視窗,定位到包含DockerMachine的資料夾,然後輸入執行檔名稱(Docker-machine_windows-amd64.exe),其後輸入-d開關、驅動程式的名稱(在本例中是Hyper-V)以及您想分配給您正在創建的虛擬機(VM)的名稱。例如,該命令可能如下所示:

Docker-machine_windows-amd64.exe-dhyper-vDocker

當運行這個命令的時候,DockerMachine完成幾個不同的任務。其中一些更重要的任務(從備份的角度來看)包括:

使用命令行中指定的名稱創建虛擬硬碟(virtualharddisk,VHD);

下載名為Boot2Docker.ISO的DVD映像;

創建虛擬機;

把Boot2Docker.ISO檔案與新創建的VM關聯,作為虛擬DVD光碟機;

把VHD與VM關聯;

啟動VM;

向VM分配IP位址和連線埠號。

技術發展

Docker解決的問題

雲計算、大數據,移動技術的快速發展,加之企業業務需求的不斷變化,導致企業架構要隨時更改以適合業務需求,跟上技術更新的步伐。毫無疑問,這些重擔都將壓在企業開發人員身上;團隊之間如何高效協調,快速交付產品,快速部署套用,以及滿足企業業務需求,是開發人員亟需解決的問題。Docker技術恰好可以幫助開發人員解決這些問題。
為了解決開發人員和運維人員之間的協作關係,加快套用交付速度,越來越多的企業引入了DevOps這一概念。但是,傳統的開發過程中,開發、測試、運維是三個獨立運作的團隊,團隊之間溝通不暢,開發運維之間衝突時有發生,導致協作效率低下,產品交付延遲,影響了企業的業務運行。Docker技術將套用以貨櫃的方式打包交付,使套用在不同的團隊中共享,通過鏡像的方式套用可以部署於任何環境中。這樣避免了各團隊之間的協作問題的出現,成為企業實現DevOps目標的重要工具。以容器方式交付的Docker技術支持不斷地開發疊代,大大提升了產品開發和交付速度。
此外,與通過Hypervisor把底層設備虛擬化的虛擬機不同,Docker直接移植於Linux核心之上,通過運行Linux進程將底層設備虛擬隔離,這樣系統性能的損耗也要比虛擬機低的多,幾乎可以忽略。同時,Docker套用容器的啟停非常高效,可以支持大規模的分布系統的水平擴展,真正給企業開發帶來福音。
正如中國惠普雲計算集成雲技術首席專家劉艷凱所說的那樣:“任何一項技術的發展和它受到的追捧,都是因為它能夠解決困擾人們的問題,”Docker正是這樣的一種技術。Docker的解決問題能力雖然很強,但在企業中的實際套用卻並不多,那么是什麼問題阻礙了Docker在企業中的實踐?

Docker未來發展

任何一項新技術的出現,都需要一個發展過程,比如雲計算為企業所接受用了將近五年左右時間,OpenStack技術也經歷了兩、三年才受到人們的認可。因此,雖然Docker技術發展很快,但技術還不夠成熟,對存儲的靈活的支持、網路的開銷和兼容性方面還存在限制,這是Docker沒有被企業大範圍使用的一個主要原因。另外一個原因是企業文化是否與DevOps運動一致,只有企業支持DevOps,才能更大地發揮Docker的價值。最後一個原因就是安全性問題,Docker對於Linux這一層的安全的隔離還有待改進,才能進一步得到企業的認可。惠普劉艷凱認為,這也是Docker需要在下一步中改進的一方面。
實現企業價值
目前,雖然Docker還未在企業中大規模採用,但已有不少企業進入了嘗試階段;而Docker作為工具,如何讓它快速幫助開發人員達到他們期望的目標,才是最重要的,惠普在Docker支持方面都做了哪些努力?
Docker價值的最大體現在於對企業DevOps的支持,對原生雲套用大規模水平擴展的支持。在惠普Helion雲戰略中包括了對DevOps服務和原生雲套用的支持,而這一戰略的具體落地,與Docker技術有著緊密的聯繫。因此,惠普團隊一直積極地參與OpenStack社區中和Docker項目相關的開發活動中,努力改進Docker技術中存在的不足。同時,惠普產品中也集成了Docker,例如,惠普開發平台產品集成了Docker,使用Docker作為套用的容器;以及惠普最新發布的CloudSystem9.0也增加對Docker的支持,用戶可以像使用其它的虛擬化資源一樣,選擇Docker作為套用的承載容器。劉艷凱認為,惠普非常認可Docker給用戶帶來的一些價值,那也希望通過自己努力,使更多的用戶使用到Docker這樣的先進的技術。

DockerHub服務

雙方在開源容器技術以及發展方向上共同努力,並提供本地化的Docker服務。Docker公司選擇阿里雲平台作為其DockerHub在中國運營的基礎服務。阿里雲也獲得DockerEngine商用版以及DockerDataCenter運營權,並為Docker客戶提供企業級支持和諮詢服務。同時,阿里雲將成為Docker官方支持的雲服務提供商。
阿里雲總裁胡曉明表示,通過和Docker的戰略合作,阿里雲將更好地為企業級客戶提供完善的雲服務,使能客戶,並實現時代轉型。

技術局限

網路限制:容器網路(DockerNetwork)讓你可以方便地在同一主機下對容器進行網路連線。加上一些其他的工作,你就可以跨主機使用疊加網路功能。然而,也就到此為止了。網路配置操作是受限的,而且到目前為止可以說這些手段都是人工的。儘管容器腳本化可以規模化,因為你必須給網路定義增加預分配實例,每次提供容器時還需要額外步驟,這容易引起錯誤。
庫控制受限:庫已經成為任何容器會話的中心議題。公共庫是最有價值的,因為他貢獻了大量的預置容器,節省了許多的配置時間。然而,在沙盒裡使用它是有風險的。在不知道誰以及如何創建鏡像的情況下,可能會存在任意數量的有意或無意的穩定性和安全性風險。對於企業來說,有必要建立和維護一個私有庫,這個庫的建立挑戰不大,但管理是個問題。Docker為大型庫的鏡像管理提供了一個有限的元數據模型,確保未來實例如預期的能力受限,也沒有疊加功能。
沒有清晰的審計跟蹤:提供容器是很簡單的,但知道提供容器的時間、原因、方式以及提供方卻不容易。因此,在提供之後,你並不掌握多少出於審計目的的歷史。
運行實例的低可見性:如果沒有經過深思熟慮的行動,實例提供後很難接觸到運行容器的對象,也很難知道哪些應該出現在那裡,哪些不應該出現在那裡。

Docker環境安全

Docker的勢頭在過去的12個月裡十分火熱,很多人表示很少見如此能夠吸引行業興趣的新興技術。然而,當興奮轉化為實際部署時,企業需要注意Docker的安全性。
了解Docker的人都知道,Docker利用容器將資源進行有效隔離。因此容器相當於與LinuxOS和hypervisor有著幾乎相同的安全運行管理和配置管理級別。但當Docker涉及到安全運營與管理,以及具有保密性、完整性和可用性的通用控制項的支持時,Docker可能會讓你失望。
當Docker運行在雲提供商平台上時,Docker安全性變得更加複雜。你需要知道雲提供商正在做什麼,或許你正在於別人共享一台機器。
Docker雖然容器沒有內置的安全因素,而且像Docker這樣的新興技術很難有比較全面的安全措施,但這並不意味著以後也不會出現。

容器部署安全

也有專家將Docker安全問題的實質定位於配置安全,認為Docker目前的問題是很難配置一個安全的容器。雖然現在Docker的開發人員通過創建非常小的容器來降低攻擊面,但問題在於大型企業內部在生產環境中運行Docker容器的員工需要有更多的可見性和可控性。
企業在部署數千或數萬台Docker容器時,能夠確保這些Docker容器都遵守企業安全策略進行配置是至關重要的事情。
Docker為解決這個問題,就需要增加Docker容器部署的實時可見性,同時實施企業制定的安全策略。也有一些廠商為此推出解決方案,給運營商提供了實時可見性並幫助他們執行容器級別的虛擬基礎設施的安全策略。

公司領導

2015年9月,此前擔任Twitter CFO、風投機構負責人的麥克•古普塔(Mike Gupta)出任Docker的CFO。

相關詞條

相關搜尋

熱門詞條

聯絡我們