?
Dieses Dokument verwendet PHP-Handbuch für chinesische Websites Freigeben
要有效地使用存儲驅(qū)動程序,您必須了解Docker如何構(gòu)建和存儲圖像。然后,您需要了解容器如何使用這些圖像。最后,您需要簡單介紹啟用圖像和容器操作的技術(shù)。
了解Docker如何管理圖像和容器中的數(shù)據(jù)將幫助您理解設(shè)計容器和Dockerize應(yīng)用程序的最佳方式,并避免之后的性能問題。
Docker鏡像是由一系列圖層構(gòu)建而成的。每個圖層代表圖像的Dockerfile中的指令。除最后一層以外的每個圖層都是只讀的??紤]以下Dockerfile:
FROM ubuntu:15.04COPY . /app RUN make /app CMD python /app/app.py
這個Dockerfile包含四個命令,每個命令創(chuàng)建一個圖層。 FROM語句從ubuntu:15.04映像創(chuàng)建一個圖層開始。 COPY命令添加Docker客戶端當前目錄中的一些文件。 RUN命令使用make命令構(gòu)建您的應(yīng)用程序。 最后,最后一層指定在容器內(nèi)運行的命令。
每個圖層只是與之前圖層的一組差異。這些圖層堆疊在一起。當你創(chuàng)建一個新的容器時,你在底層上添加一個新的可寫層。這個層通常被稱為“容器層”。對正在運行的容器所做的所有更改(如寫入新文件,修改現(xiàn)有文件和刪除文件)都會寫入此可寫容器層。下圖顯示了一個基于Ubuntu 15.04映像的容器。
一個存儲驅(qū)動程序處理這些層相互交互的方式的細節(jié)。不同的存儲驅(qū)動程序是可用的,這在不同的情況下具有優(yōu)點和缺點。
容器和圖像之間的主要區(qū)別是最高的可寫層。所有寫入容器的添加新的或修改現(xiàn)有數(shù)據(jù)的內(nèi)容都存儲在該可寫層中。當容器被刪除時,可寫層也被刪除。底層圖片保持不變。
由于每個容器都有自己的可寫容器層,并且所有更改都存儲在此容器層中,因此多個容器可以共享對相同基礎(chǔ)映像的訪問權(quán)限,并且擁有自己的數(shù)據(jù)狀態(tài)。下圖顯示了共享相同Ubuntu 15.04映像的多個容器。
注意:如果您需要多個映像才能共享訪問完全相同的數(shù)據(jù),請將這些數(shù)據(jù)存儲在Docker卷中并將其掛載到容器中。
Docker使用存儲驅(qū)動程序來管理圖像層和可寫容器層的內(nèi)容。每個存儲驅(qū)動程序都以不同方式處理實現(xiàn),但所有驅(qū)動程序都使用可堆疊的圖像層和寫入時復制(CoW)策略。
要查看正在運行的容器的大小,可以使用該docker ps -s
命令。兩個不同的列與大小有關(guān)。
size
:用于每個容器的可寫層的數(shù)據(jù)量(在磁盤上)
virtual size
:用于容器使用的只讀圖像數(shù)據(jù)的數(shù)據(jù)量。多個容器可能共享部分或全部只讀圖像數(shù)據(jù)。從同一圖像開始的兩個容器共享只讀數(shù)據(jù)的100%,而具有不同圖像的兩個容器共享這些共同層。因此,你不能只總計虛擬尺寸。這會通過一個可能不重要的數(shù)量來高估整個磁盤使用量。
磁盤上所有正在運行的容器使用的總磁盤空間是每個容器大小和虛擬大小值的組合。 如果多個容器具有完全相同的虛擬大小,則它們可能從相同的精確圖像開始。
這也不會計算容器占用磁盤空間的以下其他方式:
如果使用json-file
日志記錄驅(qū)動程序,則用于日志文件的磁盤空間。如果您的容器生成大量日志數(shù)據(jù)并且未配置日志輪轉(zhuǎn),這可能不平凡。
容器使用的卷和綁定掛載。
用于容器配置文件的磁盤空間,通常很小。
寫入磁盤的內(nèi)存(如果啟用了交換)。
檢查點,如果您使用的是實驗性檢查點/恢復功能。
寫入時復制是一種共享和復制文件以實現(xiàn)最高效率的策略。如果文件或目錄存在于映像的較低層中,而另一層(包括可寫層)需要對其進行讀取訪問,則它只使用現(xiàn)有文件。第一次需要修改文件時(構(gòu)建圖像或運行容器時),該文件將被復制到該圖層并進行修改。這最大限度地減少了每個后續(xù)層的I / O和大小。下面將更深入地解釋這些優(yōu)點。
當您使用docker pull
從存儲庫中下拉圖像時,或者當您從本地還不存在的圖像創(chuàng)建容器時,每個圖層都單獨下拉,并存儲在Docker的本地存儲區(qū)域中,該區(qū)域通常/var/lib/docker/
位于Linux主機上。在這個例子中你可以看到這些圖層被拉出來了:
$ 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
每個層都存儲在Docker主機本地存儲區(qū)內(nèi)的自己的目錄中。要檢查文件系統(tǒng)上的圖層,請列出內(nèi)容/var/lib/docker/<storage-driver>/layers/
。以下示例使用aufs
默認存儲驅(qū)動程序:
$ ls /var/lib/docker/aufs/layers 1d6674ff835b10f76e354806e16b950f91a191d3b471236609ab13a930275e24 5dbb0cbe0148cf447b9464a358c1587be586058d9a4c9ce079320265e2bb94e7 bef7199f2ed8e86fa4ada1309cfad3089e0542fec8894690529e4c04a7ca2d73 ebf814eccfe98f2704660ca1d844e4348db3b5ccc637eb905d4818fbfb00a06a
目錄名稱不對應(yīng)于層ID(自從Docker 1.10以來,都為true)。
現(xiàn)在想象你有兩個不同的Docker文件。您使用第一個來創(chuàng)建一個名為acme/my-base-image:1.0
的圖像
FROM ubuntu:16.10COPY . /app
第二個是基于acme/my-base-image:1.0
的,但有一些額外的層次:
FROM acme/my-base-image:1.0CMD /app/hello.sh
第二個圖像包含第一個圖像的所有圖層,以及一個帶有CMD
指令的新圖層和一個讀寫容器圖層。Docker已經(jīng)擁有了第一張圖片中的所有圖層,因此不需要再次拖動它們。這兩個圖像將共享他們共有的任何圖層。
如果您從兩個Dockerfiles構(gòu)建圖像,則可以使用docker images
和docker history
命令來驗證共享圖層的加密ID是否相同。
建立一個新的目錄cow-test/
并改變它。
其中cow-test/
,創(chuàng)建一個包含以下內(nèi)容的新文件:#!/ bin / sh echo“Hello world”保存文件并使其可執(zhí)行:chmod + x hello.sh
將上面第一個Dockerfile的內(nèi)容復制到一個名為Dockerfile.base
的新文件中。
將上面第二個Dockerfile的內(nèi)容復制到一個名為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
建立第二個圖像。$ 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)成每個圖像的圖層:$ 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
請注意,除第二個圖像的頂層外,所有圖層都相同。 所有其他圖層都在兩個圖像之間共享,并且只在/ var / lib / docker /中存儲一次。 新層實際上并不占用任何空間,因為它不會更改任何文件,而只是運行一個命令。
注意:docker歷史輸出中的<missing>行表明這些圖層是在另一個系統(tǒng)上構(gòu)建的,并且在本地不可用。 這可以被忽略。
當你啟動一個容器時,一個薄的可寫容器層被添加到其他層的頂部。容器對文件系統(tǒng)所做的任何更改都存儲在此處。容器不會更改的任何文件都不會被復制到此可寫層中。這意味著可寫層盡可能小。
當容器中的現(xiàn)有文件被修改時,存儲驅(qū)動程序執(zhí)行寫入時復制操作。涉及的具體步驟取決于具體的存儲驅(qū)動程序。對于默認的aufs
驅(qū)動程序和overlay
和overlay2
驅(qū)動程序時,寫入時復制操作遵循以下順序粗糙:
通過圖像層搜索要更新的文件。該過程從最新層開始,一次處理一層基礎(chǔ)層。找到結(jié)果后,它們將被添加到緩存中以加速未來的操作。
copy_up
對找到的文件的第一個副本執(zhí)行操作,將文件復制到容器的可寫層。
對該文件的副本進行任何修改,并且該容器不能看到存在于較低層中的文件的只讀副本。
Btrfs,ZFS和其他驅(qū)動程序以不同方式處理寫入時復制。稍后您可以在詳細說明中閱讀有關(guān)這些驅(qū)動程序方法的更多信息。
寫入大量數(shù)據(jù)的容器會比不容器的容器消耗更多的空間。這是因為大多數(shù)寫入操作會在容器的可寫頂層中占用新空間。
注意:對于大量寫入的應(yīng)用程序,您不應(yīng)將數(shù)據(jù)存儲在容器中。相反,請使用Docker卷,這些卷獨立于正在運行的容器,并且設(shè)計為對I / O有效。另外,卷可以在容器間共享,不會增加容器可寫層的大小。
一個copy_up
操作可能導致性能顯著的開銷。這個開銷根據(jù)使用的存儲驅(qū)動程序而不同。大文件,大量圖層和深層目錄樹可以使影響更加明顯。這可以通過每個copy_up
操作只在第一次修改給定文件時發(fā)生。
為了驗證寫入時復制的工作方式,下面的過程根據(jù)acme/my-final-image:1.0
我們之前構(gòu)建的映像加速了5個容器,并檢查它們占用的空間。
注意:此過程在Docker for Mac或Docker for Windows上不起作用。
從Docker主機的終端上運行以下docker run
命令。最后的字符串是每個容器的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
運行docker ps命令以驗證5個容器正在運行。 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
列出本地存儲區(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 這些容器中的每一個只占用文件系統(tǒng)上的32k空間。
不僅寫入時復制節(jié)省空間,而且還縮短了啟動時間。當你啟動一個容器(或者來自同一個圖像的多個容器)時,Docker只需要創(chuàng)建可寫的容器層。
如果Docker在每次啟動一個新容器時都必須制作底層映像堆棧的整個副本,則容器啟動時間和使用的磁盤空間將顯著增加。這與虛擬機的工作方式類似,每個虛擬機具有一個或多個虛擬磁盤。
當容器被刪除時,寫入容器的任何未存儲在數(shù)據(jù)卷中的數(shù)據(jù)將與容器一起被刪除。
數(shù)據(jù)體積是直接安裝到容器中的Docker主機文件系統(tǒng)中的目錄或文件。數(shù)據(jù)卷不受存儲驅(qū)動程序的控制。讀取和寫入數(shù)據(jù)卷繞過存儲驅(qū)動程序并以本地主機速度運行。您可以將任意數(shù)量的數(shù)據(jù)卷裝入容器。多個容器也可以共享一個或多個數(shù)據(jù)卷。
下圖顯示了一個運行兩個容器的Docker主機。每個容器都存在于Docker主機本地存儲區(qū)(/var/lib/docker/...
)內(nèi)自己的地址空間內(nèi)。/data
Docker主機上還有一個共享數(shù)據(jù)卷。這被直接安裝到兩個容器中。
數(shù)據(jù)積位于Docker主機本地存儲區(qū)域之外,進一步增強了它們與存儲驅(qū)動程序控制的獨立性。當容器被刪除時,存儲在數(shù)據(jù)卷中的任何數(shù)據(jù)都會保留在Docker主機上。
有關(guān)數(shù)據(jù)積的詳細信息,請參閱管理容器中的數(shù)據(jù)。
選擇存儲驅(qū)動程序
AUFS存儲驅(qū)動程序在實踐中
Btrfs存儲驅(qū)動程序在實踐中
Device Mapper存儲驅(qū)動程序在實踐中