談到 Docker 我們不得不說以下三個(gè)技術(shù)
- Linux namespace
- CGroups
- UnionFS
這些技術(shù)是 docker 實(shí)現(xiàn)了容器隔離和文件資源共享一些關(guān)鍵技術(shù)。只有理解這些技術(shù)我們才能夠真正
在從鏡像倉庫中 pull 一個(gè)鏡像(Image)時(shí)候,通常會(huì)列出 pull 很多鏡像而非只是 pull 我們想要的依賴鏡像惧浴。從這我們就很清楚這些鏡像是有依賴關(guān)系叛买。依賴關(guān)系目的是什么匹摇,就是為了資源共享槐脏,將一些可以功能獨(dú)立出來進(jìn)行共享喘垂,這是我們?cè)谠O(shè)計(jì)應(yīng)用废离、語言甚至系統(tǒng)都需要考慮關(guān)系侄泽。
任何程序運(yùn)行時(shí)都會(huì)有依賴,無論是開發(fā)語言層的依賴庫蜻韭,還是各種系統(tǒng) lib悼尾、操作系統(tǒng)等,不同的系統(tǒng)上這些庫可能是不一樣的肖方,或者有缺失的闺魏。為了讓容器運(yùn)行時(shí)一致,docker將依賴的操作系統(tǒng)俯画、各種 lib 依賴整合打包在一起(即鏡像)析桥,然后容器啟動(dòng)時(shí),作為的根目錄(根文件系統(tǒng)rootfs),使得容器進(jìn)程的各種依賴調(diào)用都在這個(gè)根目錄里泡仗,這樣就做到了環(huán)境的一致性
unionFS
聯(lián)合文件系統(tǒng)(Union File System):聯(lián)合文件系統(tǒng)可以把多個(gè)目錄(也叫分支)內(nèi)容聯(lián)合掛載(注意這里是掛載)到同一個(gè)目錄下埋虹,而目錄的物理位置是分開的。
寫時(shí)復(fù)制 (copy-on-write) 功能娩怎,寫時(shí)復(fù)制也叫隱式共享搔课,首先我們要明確寫時(shí)復(fù)制是一種技術(shù), UnionFS 可以把只讀和可讀寫文件系統(tǒng)合并在一起截亦,虛擬上允許只讀文件系統(tǒng)的修改可以保存到可寫文件系統(tǒng)當(dāng)中爬泥。
AUFS
AUFS(Advanced multi-layered unification filesystem): AUFS 完全重寫了早期的 UnionFS 1.x,其主要目的是為了可靠性和性能魁巩,并且引入了一些新的功能急灭,比如可寫分支的負(fù)載均衡。AUFS 的一些實(shí)現(xiàn)已經(jīng)被納入U(xiǎn)nionFS 2.x版本谷遂。
層的概念(Layers)
想到層葬馋,每一個(gè)鏡像其實(shí)都是通過一層一層鏡像(從基礎(chǔ))嵌套而來,所以是有先后順序肾扰,有順序數(shù)據(jù)結(jié)構(gòu)我們就自然會(huì)想到棾胨唬或者隊(duì)列這樣數(shù)據(jù)結(jié)構(gòu),他們都是按一定順序存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)
(Tomcat(JDK8(kernel)))集晚。
每一個(gè) Docker image 都是由一系列的read-only layers
組成窗悯。image layers
的內(nèi)容都存儲(chǔ)在Docker hosts filesystem
的/var/lib/docker/aufs/diff
目錄下。而/var/lib/docker/aufs/layers
目錄則存儲(chǔ)著image layer
如何堆棧這些laye
r的metadata
偷拔。
準(zhǔn)備一臺(tái)安裝了Docker 1.11.2
的ECS
蒋院。在沒有拉取任何鏡像,啟動(dòng)任何容器的情況下莲绰,執(zhí)行ls /var/lib/docker/aufs/diff
命令欺旧,發(fā)現(xiàn)目錄沒有存儲(chǔ)任何內(nèi)容。拉取Ubuntu:15.04
鏡像蛤签,然后再次執(zhí)行ls /var/lib/docker/aufs/diff
命令辞友。我們可以看到在docker pull
的結(jié)果顯示ubuntu:15.04
鏡像一共有 4 個(gè) layers
,在執(zhí)行ls /var/lib/docker/aufs/diff
命令的結(jié)果中也有四個(gè)對(duì)應(yīng)的存儲(chǔ)文件目錄震肮。這里有一點(diǎn)需要說明的是称龙,自從Docker 1.10
之后,diff
目錄下的存儲(chǔ)鏡像layer
文件夾不再與鏡像ID相同戳晌。最后cat /var/lib/docker/aufs/layers/6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea
命令列出來的是堆棧里位于6bb19cb345da470e015ba3f1ca049a1c27d2c57ebc205ec165d2ad8a44e148ea layer
下方的layers鲫尊。
func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.Store) (daemon *Daemon, err error) {
//...
for operatingSystem, gd := range d.graphDrivers {
layerStores[operatingSystem], err = layer.NewStoreFromOptions(layer.StoreOptions{
Root: config.Root,
MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
GraphDriver: gd,
GraphDriverOptions: config.GraphOptions,
IDMapping: idMapping,
PluginGetter: d.PluginStore,
ExperimentalEnabled: config.Experimental,
OS: operatingSystem,
})
}
//...
}
func NewStoreFromOptions(options StoreOptions) (Store, error) {
driver, err := graphdriver.New(options.GraphDriver, options.PluginGetter, graphdriver.Options{
Root: options.Root,
DriverOptions: options.GraphDriverOptions,
UIDMaps: options.IDMapping.UIDs(),
GIDMaps: options.IDMapping.GIDs(),
ExperimentalEnabled: options.ExperimentalEnabled,
})
//...
}
// New creates the driver and initializes it at the specified root.
func New(name string, pg plugingetter.PluginGetter, config Options) (Driver, error) {
//...
driversMap := scanPriorDrivers(config.Root)
list := strings.Split(priority, ",")
logrus.Debugf("[graphdriver] priority list: %v", list)
for _, name := range list {
if name == "vfs" {
// don't use vfs even if there is state present.
continue
}
if _, prior := driversMap[name]; prior {
driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.UIDMaps, config.GIDMaps)
//...
return driver, nil
}
}
//...
}