?
This document uses PHP Chinese website manual Release
要有效地使用存儲(chǔ)驅(qū)動(dòng)程序,您必須了解Docker如何構(gòu)建和存儲(chǔ)圖像。然后,您需要了解容器如何使用這些圖像。最后,您需要簡(jiǎn)單介紹啟用圖像和容器操作的技術(shù)。
了解Docker如何管理圖像和容器中的數(shù)據(jù)將幫助您理解設(shè)計(jì)容器和Dockerize應(yīng)用程序的最佳方式,并避免之后的性能問(wèn)題。
Docker鏡像是由一系列圖層構(gòu)建而成的。每個(gè)圖層代表圖像的Dockerfile中的指令。除最后一層以外的每個(gè)圖層都是只讀的??紤]以下Dockerfile:
FROM ubuntu:15.04COPY . /app RUN make /app CMD python /app/app.py
這個(gè)Dockerfile包含四個(gè)命令,每個(gè)命令創(chuàng)建一個(gè)圖層。 FROM語(yǔ)句從ubuntu:15.04映像創(chuàng)建一個(gè)圖層開(kāi)始。 COPY命令添加Docker客戶端當(dāng)前目錄中的一些文件。 RUN命令使用make命令構(gòu)建您的應(yīng)用程序。 最后,最后一層指定在容器內(nèi)運(yùn)行的命令。
每個(gè)圖層只是與之前圖層的一組差異。這些圖層堆疊在一起。當(dāng)你創(chuàng)建一個(gè)新的容器時(shí),你在底層上添加一個(gè)新的可寫層。這個(gè)層通常被稱為“容器層”。對(duì)正在運(yùn)行的容器所做的所有更改(如寫入新文件,修改現(xiàn)有文件和刪除文件)都會(huì)寫入此可寫容器層。下圖顯示了一個(gè)基于Ubuntu 15.04映像的容器。
一個(gè)存儲(chǔ)驅(qū)動(dòng)程序處理這些層相互交互的方式的細(xì)節(jié)。不同的存儲(chǔ)驅(qū)動(dòng)程序是可用的,這在不同的情況下具有優(yōu)點(diǎn)和缺點(diǎn)。
容器和圖像之間的主要區(qū)別是最高的可寫層。所有寫入容器的添加新的或修改現(xiàn)有數(shù)據(jù)的內(nèi)容都存儲(chǔ)在該可寫層中。當(dāng)容器被刪除時(shí),可寫層也被刪除。底層圖片保持不變。
由于每個(gè)容器都有自己的可寫容器層,并且所有更改都存儲(chǔ)在此容器層中,因此多個(gè)容器可以共享對(duì)相同基礎(chǔ)映像的訪問(wèn)權(quán)限,并且擁有自己的數(shù)據(jù)狀態(tài)。下圖顯示了共享相同Ubuntu 15.04映像的多個(gè)容器。
注意:如果您需要多個(gè)映像才能共享訪問(wèn)完全相同的數(shù)據(jù),請(qǐng)將這些數(shù)據(jù)存儲(chǔ)在Docker卷中并將其掛載到容器中。
Docker使用存儲(chǔ)驅(qū)動(dòng)程序來(lái)管理圖像層和可寫容器層的內(nèi)容。每個(gè)存儲(chǔ)驅(qū)動(dòng)程序都以不同方式處理實(shí)現(xiàn),但所有驅(qū)動(dòng)程序都使用可堆疊的圖像層和寫入時(shí)復(fù)制(CoW)策略。
要查看正在運(yùn)行的容器的大小,可以使用該docker ps -s
命令。兩個(gè)不同的列與大小有關(guān)。
size
:用于每個(gè)容器的可寫層的數(shù)據(jù)量(在磁盤上)
virtual size
:用于容器使用的只讀圖像數(shù)據(jù)的數(shù)據(jù)量。多個(gè)容器可能共享部分或全部只讀圖像數(shù)據(jù)。從同一圖像開(kāi)始的兩個(gè)容器共享只讀數(shù)據(jù)的100%,而具有不同圖像的兩個(gè)容器共享這些共同層。因此,你不能只總計(jì)虛擬尺寸。這會(huì)通過(guò)一個(gè)可能不重要的數(shù)量來(lái)高估整個(gè)磁盤使用量。
磁盤上所有正在運(yùn)行的容器使用的總磁盤空間是每個(gè)容器大小和虛擬大小值的組合。 如果多個(gè)容器具有完全相同的虛擬大小,則它們可能從相同的精確圖像開(kāi)始。
這也不會(huì)計(jì)算容器占用磁盤空間的以下其他方式:
如果使用json-file
日志記錄驅(qū)動(dòng)程序,則用于日志文件的磁盤空間。如果您的容器生成大量日志數(shù)據(jù)并且未配置日志輪轉(zhuǎn),這可能不平凡。
容器使用的卷和綁定掛載。
用于容器配置文件的磁盤空間,通常很小。
寫入磁盤的內(nèi)存(如果啟用了交換)。
檢查點(diǎn),如果您使用的是實(shí)驗(yàn)性檢查點(diǎn)/恢復(fù)功能。
寫入時(shí)復(fù)制是一種共享和復(fù)制文件以實(shí)現(xiàn)最高效率的策略。如果文件或目錄存在于映像的較低層中,而另一層(包括可寫層)需要對(duì)其進(jìn)行讀取訪問(wèn),則它只使用現(xiàn)有文件。第一次需要修改文件時(shí)(構(gòu)建圖像或運(yùn)行容器時(shí)),該文件將被復(fù)制到該圖層并進(jìn)行修改。這最大限度地減少了每個(gè)后續(xù)層的I / O和大小。下面將更深入地解釋這些優(yōu)點(diǎn)。
當(dāng)您使用docker pull
從存儲(chǔ)庫(kù)中下拉圖像時(shí),或者當(dāng)您從本地還不存在的圖像創(chuàng)建容器時(shí),每個(gè)圖層都單獨(dú)下拉,并存儲(chǔ)在Docker的本地存儲(chǔ)區(qū)域中,該區(qū)域通常/var/lib/docker/
位于Linux主機(jī)上。在這個(gè)例子中你可以看到這些圖層被拉出來(lái)了:
$ docker pull ubuntu:15.0415.04: Pulling from library/ubuntu 1ba8ac955b97: Pull complete f157c4e5ede7: Pull complete 0b7e98f84c4c: Pull complete a3ed95caeb02: Pull complete Digest: sha256:5e279a9df07990286cce22e1b0f5b0490629ca6d187698746ae5e28e604a640e Status: Downloaded newer image for ubuntu:15.04
每個(gè)層都存儲(chǔ)在Docker主機(jī)本地存儲(chǔ)區(qū)內(nèi)的自己的目錄中。要檢查文件系統(tǒng)上的圖層,請(qǐng)列出內(nèi)容/var/lib/docker/<storage-driver>/layers/
。以下示例使用aufs
默認(rèn)存儲(chǔ)驅(qū)動(dòng)程序:
$ ls /var/lib/docker/aufs/layers 1d6674ff835b10f76e354806e16b950f91a191d3b471236609ab13a930275e24 5dbb0cbe0148cf447b9464a358c1587be586058d9a4c9ce079320265e2bb94e7 bef7199f2ed8e86fa4ada1309cfad3089e0542fec8894690529e4c04a7ca2d73 ebf814eccfe98f2704660ca1d844e4348db3b5ccc637eb905d4818fbfb00a06a
目錄名稱不對(duì)應(yīng)于層ID(自從Docker 1.10以來(lái),都為true)。
現(xiàn)在想象你有兩個(gè)不同的Docker文件。您使用第一個(gè)來(lái)創(chuàng)建一個(gè)名為acme/my-base-image:1.0
的圖像
FROM ubuntu:16.10COPY . /app
第二個(gè)是基于acme/my-base-image:1.0
的,但有一些額外的層次:
FROM acme/my-base-image:1.0CMD /app/hello.sh
第二個(gè)圖像包含第一個(gè)圖像的所有圖層,以及一個(gè)帶有CMD
指令的新圖層和一個(gè)讀寫容器圖層。Docker已經(jīng)擁有了第一張圖片中的所有圖層,因此不需要再次拖動(dòng)它們。這兩個(gè)圖像將共享他們共有的任何圖層。
如果您從兩個(gè)Dockerfiles構(gòu)建圖像,則可以使用docker images
和docker history
命令來(lái)驗(yàn)證共享圖層的加密ID是否相同。
建立一個(gè)新的目錄cow-test/
并改變它。
其中cow-test/
,創(chuàng)建一個(gè)包含以下內(nèi)容的新文件:#!/ bin / sh echo“Hello world”保存文件并使其可執(zhí)行:chmod + x hello.sh
將上面第一個(gè)Dockerfile的內(nèi)容復(fù)制到一個(gè)名為Dockerfile.base
的新文件中。
將上面第二個(gè)Dockerfile的內(nèi)容復(fù)制到一個(gè)名為Dockerfile
的新文件中。
在cow-test/
目錄中,建立第一張圖片。$ docker build -t acme/my-base-image:1.0 -f Dockerfile.base . Sending build context to Docker daemon 4.096kB Step 1/2 : FROM ubuntu:16.10 ---> 31005225a745 Step 2/2 : COPY . /app ---> Using cache ---> bd09118bcef6 Successfully built bd09118bcef6 Successfully tagged acme/my-base-image:1.0
建立第二個(gè)圖像。$ docker build -t acme/my-final-image:1.0 -f Dockerfile . Sending build context to Docker daemon 4.096kB Step 1/2 : FROM acme/my-base-image:1.0 ---> bd09118bcef6 Step 2/2 : CMD /app/hello.sh ---> Running in a07b694759ba ---> dbf995fc07ff Removing intermediate container a07b694759ba Successfully built dbf995fc07ff Successfully tagged acme/my-final-image:1.0
檢查圖像的大?。? docker images REPOSITORY TAG IMAGE ID CREATED SIZE acme/my-final-image 1.0 dbf995fc07ff 58 seconds ago 103MB acme/my-base-image 1.0 bd09118bcef6 3 minutes ago 103MB
查看構(gòu)成每個(gè)圖像的圖層:$ docker history bd09118bcef6 IMAGE CREATED CREATED BY SIZE COMMENT bd09118bcef6 4 minutes ago /bin/sh -c #(nop) COPY dir:35a7eb158c1504e... 100B 31005225a745 3 months ago /bin/sh -c #(nop) CMD "/bin/bash" 0B <missing> 3 months ago /bin/sh -c mkdir -p /run/systemd && echo '... 7B <missing> 3 months ago /bin/sh -c sed -i 's/^#\s*(deb.*universe... 2.78kB <missing> 3 months ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B <missing> 3 months ago /bin/sh -c set -xe && echo '#!/bin/sh' >... 745B <missing> 3 months ago /bin/sh -c #(nop) ADD file:eef57983bd66e3a... 103MB$ docker history dbf995fc07ff IMAGE CREATED CREATED BY SIZE COMMENT dbf995fc07ff 3 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/a... 0B bd09118bcef6 5 minutes ago /bin/sh -c #(nop) COPY dir:35a7eb158c1504e... 100B 31005225a745 3 months ago /bin/sh -c #(nop) CMD "/bin/bash" 0B <missing> 3 months ago /bin/sh -c mkdir -p /run/systemd && echo '... 7B <missing> 3 months ago /bin/sh -c sed -i 's/^#\s*(deb.*universe... 2.78kB <missing> 3 months ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B <missing> 3 months ago /bin/sh -c set -xe && echo '#!/bin/sh' >... 745B <missing> 3 months ago /bin/sh -c #(nop) ADD file:eef57983bd66e3a... 103MB
請(qǐng)注意,除第二個(gè)圖像的頂層外,所有圖層都相同。 所有其他圖層都在兩個(gè)圖像之間共享,并且只在/ var / lib / docker /中存儲(chǔ)一次。 新層實(shí)際上并不占用任何空間,因?yàn)樗粫?huì)更改任何文件,而只是運(yùn)行一個(gè)命令。
注意:docker歷史輸出中的<missing>行表明這些圖層是在另一個(gè)系統(tǒng)上構(gòu)建的,并且在本地不可用。 這可以被忽略。
當(dāng)你啟動(dòng)一個(gè)容器時(shí),一個(gè)薄的可寫容器層被添加到其他層的頂部。容器對(duì)文件系統(tǒng)所做的任何更改都存儲(chǔ)在此處。容器不會(huì)更改的任何文件都不會(huì)被復(fù)制到此可寫層中。這意味著可寫層盡可能小。
當(dāng)容器中的現(xiàn)有文件被修改時(shí),存儲(chǔ)驅(qū)動(dòng)程序執(zhí)行寫入時(shí)復(fù)制操作。涉及的具體步驟取決于具體的存儲(chǔ)驅(qū)動(dòng)程序。對(duì)于默認(rèn)的aufs
驅(qū)動(dòng)程序和overlay
和overlay2
驅(qū)動(dòng)程序時(shí),寫入時(shí)復(fù)制操作遵循以下順序粗糙:
通過(guò)圖像層搜索要更新的文件。該過(guò)程從最新層開(kāi)始,一次處理一層基礎(chǔ)層。找到結(jié)果后,它們將被添加到緩存中以加速未來(lái)的操作。
copy_up
對(duì)找到的文件的第一個(gè)副本執(zhí)行操作,將文件復(fù)制到容器的可寫層。
對(duì)該文件的副本進(jìn)行任何修改,并且該容器不能看到存在于較低層中的文件的只讀副本。
Btrfs,ZFS和其他驅(qū)動(dòng)程序以不同方式處理寫入時(shí)復(fù)制。稍后您可以在詳細(xì)說(shuō)明中閱讀有關(guān)這些驅(qū)動(dòng)程序方法的更多信息。
寫入大量數(shù)據(jù)的容器會(huì)比不容器的容器消耗更多的空間。這是因?yàn)榇蠖鄶?shù)寫入操作會(huì)在容器的可寫頂層中占用新空間。
注意:對(duì)于大量寫入的應(yīng)用程序,您不應(yīng)將數(shù)據(jù)存儲(chǔ)在容器中。相反,請(qǐng)使用Docker卷,這些卷獨(dú)立于正在運(yùn)行的容器,并且設(shè)計(jì)為對(duì)I / O有效。另外,卷可以在容器間共享,不會(huì)增加容器可寫層的大小。
一個(gè)copy_up
操作可能導(dǎo)致性能顯著的開(kāi)銷。這個(gè)開(kāi)銷根據(jù)使用的存儲(chǔ)驅(qū)動(dòng)程序而不同。大文件,大量圖層和深層目錄樹(shù)可以使影響更加明顯。這可以通過(guò)每個(gè)copy_up
操作只在第一次修改給定文件時(shí)發(fā)生。
為了驗(yàn)證寫入時(shí)復(fù)制的工作方式,下面的過(guò)程根據(jù)acme/my-final-image:1.0
我們之前構(gòu)建的映像加速了5個(gè)容器,并檢查它們占用的空間。
注意:此過(guò)程在Docker for Mac或Docker for Windows上不起作用。
從Docker主機(jī)的終端上運(yùn)行以下docker run
命令。最后的字符串是每個(gè)容器的ID。
$ docker run -dit --name my_container_1 acme/my-final-image:1.0 bash \ && docker run -dit --name my_container_2 acme/my-final-image:1.0 bash \ && docker run -dit --name my_container_3 acme/my-final-image:1.0 bash \ && docker run -dit --name my_container_4 acme/my-final-image:1.0 bash \ && docker run -dit --name my_container_5 acme/my-final-image:1.0 bash c36785c423ec7e0422b2af7364a7ba4da6146cbba7981a0951fcc3fa0430c409 dcad7101795e4206e637d9358a818e5c32e13b349e62b00bf05cd5a4343ea513 1e7264576d78a3134fbaf7829bc24b1d96017cf2bc046b7cd8b08b5775c33d0c 38fa94212a419a082e6a6b87a8e2ec4a44dd327d7069b85892a707e3fc818544 1a174fc216cccf18ec7d4fe14e008e30130b11ede0f0f94a87982e310cf2e765
運(yùn)行docker ps命令以驗(yàn)證5個(gè)容器正在運(yùn)行。 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1a174fc216cc acme/my-final-image:1.0 "bash" About a minute ago Up About a minute my_container_5 38fa94212a41 acme/my-final-image:1.0 "bash" About a minute ago Up About a minute my_container_4 1e7264576d78 acme/my-final-image:1.0 "bash" About a minute ago Up About a minute my_container_3 dcad7101795e acme/my-final-image:1.0 "bash" About a minute ago Up About a minute my_container_2 c36785c423ec acme/my-final-image:1.0 "bash" About a minute ago Up About a minute my_container_1
列出本地存儲(chǔ)區(qū)的內(nèi)容。 $ sudo ls /var/lib/docker/containers 1a174fc216cccf18ec7d4fe14e008e30130b11ede0f0f94a87982e310cf2e765 1e7264576d78a3134fbaf7829bc24b1d96017cf2bc046b7cd8b08b5775c33d0c 38fa94212a419a082e6a6b87a8e2ec4a44dd327d7069b85892a707e3fc818544 c36785c423ec7e0422b2af7364a7ba4da6146cbba7981a0951fcc3fa0430c409 dcad7101795e4206e637d9358a818e5c32e13b349e62b00bf05cd5a4343ea513
現(xiàn)在檢查大?。? $ sudo du -sh /var/lib/docker/containers/* 32K /var/lib/docker/containers/1a174fc216cccf18ec7d4fe14e008e30130b11ede0f0f94a87982e310cf2e765 32K /var/lib/docker/containers/1e7264576d78a3134fbaf7829bc24b1d96017cf2bc046b7cd8b08b5775c33d0c 32K /var/lib/docker/containers/38fa94212a419a082e6a6b87a8e2ec4a44dd327d7069b85892a707e3fc818544 32K /var/lib/docker/containers/c36785c423ec7e0422b2af7364a7ba4da6146cbba7981a0951fcc3fa0430c409 32K /var/lib/docker/containers/dcad7101795e4206e637d9358a818e5c32e13b349e62b00bf05cd5a4343ea513 這些容器中的每一個(gè)只占用文件系統(tǒng)上的32k空間。
不僅寫入時(shí)復(fù)制節(jié)省空間,而且還縮短了啟動(dòng)時(shí)間。當(dāng)你啟動(dòng)一個(gè)容器(或者來(lái)自同一個(gè)圖像的多個(gè)容器)時(shí),Docker只需要?jiǎng)?chuàng)建可寫的容器層。
如果Docker在每次啟動(dòng)一個(gè)新容器時(shí)都必須制作底層映像堆棧的整個(gè)副本,則容器啟動(dòng)時(shí)間和使用的磁盤空間將顯著增加。這與虛擬機(jī)的工作方式類似,每個(gè)虛擬機(jī)具有一個(gè)或多個(gè)虛擬磁盤。
當(dāng)容器被刪除時(shí),寫入容器的任何未存儲(chǔ)在數(shù)據(jù)卷中的數(shù)據(jù)將與容器一起被刪除。
數(shù)據(jù)體積是直接安裝到容器中的Docker主機(jī)文件系統(tǒng)中的目錄或文件。數(shù)據(jù)卷不受存儲(chǔ)驅(qū)動(dòng)程序的控制。讀取和寫入數(shù)據(jù)卷繞過(guò)存儲(chǔ)驅(qū)動(dòng)程序并以本地主機(jī)速度運(yùn)行。您可以將任意數(shù)量的數(shù)據(jù)卷裝入容器。多個(gè)容器也可以共享一個(gè)或多個(gè)數(shù)據(jù)卷。
下圖顯示了一個(gè)運(yùn)行兩個(gè)容器的Docker主機(jī)。每個(gè)容器都存在于Docker主機(jī)本地存儲(chǔ)區(qū)(/var/lib/docker/...
)內(nèi)自己的地址空間內(nèi)。/data
Docker主機(jī)上還有一個(gè)共享數(shù)據(jù)卷。這被直接安裝到兩個(gè)容器中。
數(shù)據(jù)積位于Docker主機(jī)本地存儲(chǔ)區(qū)域之外,進(jìn)一步增強(qiáng)了它們與存儲(chǔ)驅(qū)動(dòng)程序控制的獨(dú)立性。當(dāng)容器被刪除時(shí),存儲(chǔ)在數(shù)據(jù)卷中的任何數(shù)據(jù)都會(huì)保留在Docker主機(jī)上。
有關(guān)數(shù)據(jù)積的詳細(xì)信息,請(qǐng)參閱管理容器中的數(shù)據(jù)。
選擇存儲(chǔ)驅(qū)動(dòng)程序
AUFS存儲(chǔ)驅(qū)動(dòng)程序在實(shí)踐中
Btrfs存儲(chǔ)驅(qū)動(dòng)程序在實(shí)踐中
Device Mapper存儲(chǔ)驅(qū)動(dòng)程序在實(shí)踐中