什么是Docker图像“图层”?

时间:2015-07-04 15:38:08

标签: docker

我是Docker的新手,我正试图准确理解Docker 图像是什么。 Docker镜像的每个定义都使用术语“图层”,但似乎并未定义图层的含义。

来自官方Docker docs

  

我们已经看到Docker镜像是从中启动Docker容器的只读模板。每个图像由一系列图层组成。 Docker利用联合文件系统将这些层组合成一个图像。联合文件系统允许透明地覆盖单独文件系统的文件和目录(称为分支),形成单个连贯的文件系统。

所以我问,什么是层(确切地说);有人能给出一些具体的例子吗?这些图层如何“拼合”形成图像?

9 个答案:

答案 0 :(得分:98)

我可能会迟到,但这是我的10美分(补充了ashishjain的回答):

基本上,图层或图像图层是对图像的更改,或中间图像。您在Dockerfile中指定的每个命令(FROMRUNCOPY等)都会导致上一个图像发生更改,从而创建一个新图层。当您使用git时,您可以将其视为暂存更改:您添加文件的更改,然后添加另一个,然后另一个...

考虑以下Dockerfile:

FROM rails:onbuild
ENV RAILS_ENV production
ENTRYPOINT ["bundle", "exec", "puma"]

首先,我们选择一张起始图片:rails:onbuild,后者又有很多layers。 我们在起始图像的顶部添加另一个图层,使用RAILS_ENV命令设置环境变量ENV。然后,我们告诉docker运行bundle exec puma(它启动rails服务器)。那是另一层。

在构建图像时,图层的概念很方便。由于图层是中间图像,如果您对Dockerfile进行了更改,则docker将构建已更改的图层以及之后的图层。这称为层缓存。

您可以详细了解here

答案 1 :(得分:61)

使用dockerfile创建泊坞窗容器图像。 dockerfile中的每一行都会创建一个图层。请考虑以下虚拟示例:

FROM ubuntu             #This has its own number of layers say "X"
MAINTAINER FOO          #This is one layer 
RUN mkdir /tmp/foo      #This is one layer 
RUN apt-get install vim #This is one layer 

这将创建一个最终图像,其中总图层数将为 X + 3

答案 2 :(得分:17)

自Docker v1.10以来,随着内容可寻址存储的引入,'layer'的概念变得非常不同。图层没有图像概念或属于图像,它们只是文件和目录的集合,可以跨图像共享。图层和图像分离。

例如,在基础图像的本地构建图像上,假设ubuntu:14.04docker history命令产生图像链,但某些图像ID将显示为“缺失”因为不再加载构建历史记录。组成这些图像的图层可以通过

找到
docker inspect <image_id> | jq -r '.[].RootFS'

如果存储驱动程序选择为/var/lib/docker/aufs/diff,则图层内容将存储在aufs。但这些图层是使用随机生成的缓存ID命名的,出于安全原因,图层与其缓存ID之间的链接似乎只为Docker Engine所知。我仍在寻找找出方法

  1. 图像与其组成层之间的对应关系
  2. 磁盘上图层的实际位置和大小
  3. blog提供了很多见解。

答案 3 :(得分:17)

通过一个例子,它们对我来说最有意义...

使用docker diff检查您自己构建的层

让我们举一个人为的Dockerfile示例:

FROM busybox

RUN mkdir /data
# imagine this is downloading source code
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one 
RUN chmod -R 0777 /data
# imagine this is compiling the app
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/two 
RUN chmod -R 0777 /data
# and now this cleans up that downloaded source code
RUN rm /data/one 

CMD ls -alh /data

每个dd命令都将一个1M文件输出到磁盘。让我们使用额外的标记来构建图像以保存临时容器:

docker image build --rm=false .

在输出中,您将看到每个正在运行的命令都发生在我们现在保留的临时容器中,而不是自动删除:

...
Step 2/7 : RUN mkdir /data
 ---> Running in 04c5fa1360b0
 ---> 9b4368667b8c
Step 3/7 : RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one
 ---> Running in f1b72db3bfaa
1024+0 records in
1024+0 records out
1048576 bytes (1.0MB) copied, 0.006002 seconds, 166.6MB/s
 ---> ea2506fc6e11

如果您在每个容器ID上运行docker diff,则会看到在这些容器中创建了哪些文件:

$ docker diff 04c5fa1360b0  # mkdir /data
A /data
$ docker diff f1b72db3bfaa  # dd if=/dev/zero bs=1024 count=1024 of=/data/one
C /data
A /data/one
$ docker diff 81c607555a7d  # chmod -R 0777 /data
C /data
C /data/one
$ docker diff 1bd249e1a47b  # dd if=/dev/zero bs=1024 count=1024 of=/data/two
C /data
A /data/two
$ docker diff 038bd2bc5aea  # chmod -R 0777 /data
C /data/one
C /data/two
$ docker diff 504c6e9b6637  # rm /data/one
C /data
D /data/one

每行以A为前缀的行都在添加文件,C表示对现有文件的更改,而D则表示删除。

这是TL; DR部分

以上每个容器文件系统差异都进入一个“层”,当您将图像作为容器运行时,该“层”将被组装。进行添加或更改后,整个文件位于每个层中,因此,这些chmod命令中的每个命令(尽管只是更改了权限位)都导致整个文件被复制到下一层。删除的/ data / one文件仍然在前一层中,实际上是3次,并且在拉取映像时将通过网络复制并存储在磁盘上。

检查现有图像

您可以看到使用docker history命令创建现有图像图层的命令。您还可以在图像上运行docker image inspect,然后在RootFS部分下查看图层列表。

以下是上图的历史记录:

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
a81cfb93008c        4 seconds ago       /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "ls -…   0B
f36265598aef        5 seconds ago       /bin/sh -c rm /data/one                         0B
c79aff033b1c        7 seconds ago       /bin/sh -c chmod -R 0777 /data                  2.1MB
b821dfe9ea38        10 seconds ago      /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
a5602b8e8c69        13 seconds ago      /bin/sh -c chmod -R 0777 /data                  1.05MB
08ec3c707b11        15 seconds ago      /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
ed27832cb6c7        18 seconds ago      /bin/sh -c mkdir /data                          0B
22c2dd5ee85d        2 weeks ago         /bin/sh -c #(nop)  CMD ["sh"]                   0B
<missing>           2 weeks ago         /bin/sh -c #(nop) ADD file:2a4c44bdcb743a52f…   1.16MB

最新层在顶部列出。值得注意的是,底部有两层相当古老的层。它们来自busybox映像本身。构建一幅图像时,您将继承在FROM行中指定的图像的所有层。还添加了用于更改图像元数据的图层,例如CMD行。它们几乎不占用任何空间,而更多用于记录哪些设置适用于您正在运行的图像。

为什么要分层?

这些层具有几个优点。首先,它们是不可变的。创建后,由sha256哈希标识的图层将永远不会更改。这种不变性使图像可以安全地建立和分叉。如果两个dockerfile具有相同的初始行集并且构建在同一服务器上,则它们将共享同一组初始层,从而节省磁盘空间。这也意味着,如果您仅在Dockerfile的最后几行发生更改的情况下重建映像,则仅需重建这些层,其余部分可以从层缓存中重用。这样可以非常快速地重建docker映像。

在容器内,您可以看到映像文件系统,但是该文件系统未复制。在这些图像层的顶部,容器将安装其自己的读写文件系统层。每次读取文件都会遍历各个层,直到它到达标记了要删除的文件的层,在该层中具有该文件的副本或者该读取用完了各个层以进行搜索为止。每次写操作都会在特定于容器的读写层中进行修改。

减少肿胀

这些层的一个缺点是构建复制文件的图像,或运送在下一层删除的文件。解决方案通常是将多个命令合并为一个RUN命令。特别是在修改现有文件或删除文件时,希望这些步骤在最初创建它们的同一命令中运行。上面的Dockerfile的重写如下:

FROM busybox

RUN mkdir /data \
 && dd if=/dev/zero bs=1024 count=1024 of=/data/one \
 && chmod -R 0777 /data \
 && dd if=/dev/zero bs=1024 count=1024 of=/data/two \
 && chmod -R 0777 /data \
 && rm /data/one

CMD ls -alh /data

如果您比较结果图像:

  • busybox:〜1MB
  • 第一张图片:〜6MB
  • 第二张图片:〜2MB

仅通过将人为设计的示例中的几行合并在一起,我们就在图像中得到了相同的内容,并将图像从5MB缩小到最终图像中看到的1MB文件。

答案 4 :(得分:9)

每个Docker的image spec

  

图像由图层组成。每一层都是一组文件系统   变化。图层没有环境等配置元数据   变量或默认参数 - 这些是图像的属性   整个而不是任何特定的层。

因此,实际上,layer只是对文件系统进行的一组更改。

答案 5 :(得分:7)

我认为官方文件给出了非常详细的解释: https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/

图像由通常从Dockerfile生成的许多层组成,Dockerfile中的每一行都将创建一个新图层,结果是一个图像,由 if (files.Count == 0) { Dts.Variables["files_present"].Value = false; } else { Dts.Variables["file_list"].Value =files; Dts.Variables["files_present"].Value = true; }` 表示,如{{1} }。

如需了解更多信息,请参阅上述官方文档。

答案 6 :(得分:2)

感谢@David Castillo提供有用的information。 我认为该层是一些二进制更改或图像指令,可以轻松完成或撤消。 它们是逐步完成的,与层上的层相同,因此我们称之为“层”。

有关详细信息,您可以看到“docker history”,如下所示:

docker images --tree
Warning: '--tree' is deprecated, it will be removed soon. See usage.
└─511136ea3c5a Virtual Size: 0 B Tags: scratch:latest
  └─59e359cb35ef Virtual Size: 85.18 MB
    └─e8d37d9e3476 Virtual Size: 85.18 MB Tags: debian:wheezy
      └─c58b36b8f285 Virtual Size: 85.18 MB
        └─90ea6e05b074 Virtual Size: 118.6 MB
          └─5dc74cffc471 Virtual Size: 118.6 MB Tags: vim:latest

答案 7 :(得分:1)

我个人的理解是,我们可以将docker层与github commit进行比较。对于您的基础映像(您的新主存储库),您进行了几次提交,每次提交都在更改您的主状态,在docker中是相同的,每层都基于先前的中间层进行一些操作。然后,该层成为下一层的新中间层。

答案 8 :(得分:1)

我以前认为它们就像上一层的 diffs 一样。在阅读了一些答案之后,我不太确定。它们称为文件系统更改集。我已经写了一些Dockerfile来显示它们更像是diff,即它们确实取决于以前的层。

给出这两个Dockerfile

FROM bash
RUN mkdir /data
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/two
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/three

FROM bash
RUN mkdir /data
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/three
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/two
RUN dd if=/dev/zero bs=1024 count=1024 of=/data/one

如果只是为了更改文件系统,人们会期望使用同一组图层,但事实并非如此:

$ docker history img_1
IMAGE               CREATED             CREATED BY                                      SIZE
30daa166a9c5        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
4467d16e79f5        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
c299561fd031        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
646feb178431        6 minutes ago       /bin/sh -c mkdir /data                          0B
78664daf24f4        2 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<more missing...>

$ docker history img_2
IMAGE               CREATED             CREATED BY                                      SIZE
f55c91305f8c        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
29b3b627c76f        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
18360be603aa        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102…   1.05MB
646feb178431        6 minutes ago       /bin/sh -c mkdir /data                          0B
78664daf24f4        2 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<more missing...>

即使两种情况下对文件系统的更改相同,您也可以看到顺序很重要。