什么是Docker图像"图层"?

sme*_*eeb 137 docker

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

从官方Docker文档:

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

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

Dav*_*llo 109

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

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

考虑以下Dockerfile:

FROM rails:onbuild
ENV RAILS_ENV production
ENTRYPOINT ["bundle", "exec", "puma"]
Run Code Online (Sandbox Code Playgroud)

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

层的概念在构建图像时派上用场.因为图层是中间图像,所以如果对Dockerfile进行更改,则docker将构建已更改的图层以及之后的图层.这称为层缓存.

你可以在这里阅读更多相关信息.

  • 如果您更改或添加图层,Docker还将构建之后出现的任何图层,因为它们可能会受到更改的影响. (9认同)

ash*_*ain 67

使用dockerfile创建docker容器映像.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 
Run Code Online (Sandbox Code Playgroud)

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

  • 虽然我没有投票,但我的猜测是,这解释了如何*创建*层,但绝不会回答关于*层是什么的问题. (28认同)
  • "dockerfile中的每一行都会创建一个图层" - 这对我来说非常有帮助 (5认同)
  • @akirekadu 这不是完整的故事。大多数行都会创建一个层,但只有 ADD、COPY 或 RUN 指令才会创建层,从而增加生成的容器映像的大小。我说大多数行是因为如果您将命令链接在一起,或者使用反斜杠转义换行符,则链接命令/转义换行符的序列将形成单个命令。 (3认同)
  • 我能否知道这次否决的原因? (2认同)
  • 我同意@ LasseV.Karlsen,ashishjain.我没有向你投票,事实上我正在试图帮助我(所以+1) - 但为了让我能够给你绿色支票,我需要了解一个层实际上是什么!再次感谢,继续前进! (2认同)
  • 最佳答案imo。对于许多进入“利用docker”的人来说,它为我们提供了层如何工作的要点。 (2认同)

BMi*_*tch 23

他们用一个例子对我最有意义......

使用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
Run Code Online (Sandbox Code Playgroud)

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

docker image build --rm=false .
Run Code Online (Sandbox Code Playgroud)

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

...
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
Run Code Online (Sandbox Code Playgroud)

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

$ 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
Run Code Online (Sandbox Code Playgroud)

每个前缀为a的行A都是添加文件,C表示对现有文件的更改,并D表示删除.

这是TL; DR部分

上面的每个容器文件系统差异进入一个"层",当您将图像作为容器运行时,它将被组合.当存在添加或更改时,整个文件位于每个层中,因此chmod尽管只更改了权限位,但每个命令都会导致整个文件被复制到下一层.删除的/ data/one文件仍然在前面的图层中,实际上是3次,并且当您拉动图像时,它将通过网络复制并存储在磁盘上.

检查现有图像

您可以使用该命令查看创建现有图像图层的docker history命令.您还可以docker image inspect在图像上运行a 并查看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
Run Code Online (Sandbox Code Playgroud)

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

为什么分层?

这些层有几个优点.首先,它们是不可改变的.一旦创建,由sha256哈希标识的那个层将永远不会改变.这种不变性允许图像安全地构建并彼此分离.如果两个dockerfiles具有相同的初始行集,并且构建在同一服务器上,则它们将共享同一组初始层,从而节省磁盘空间.这也意味着如果重建一个图像,只有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
Run Code Online (Sandbox Code Playgroud)

如果您比较结果图像:

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

仅仅通过合并设计示例中的一些行,我们在图像中得到了相同的结果内容,并将我们的图像从5MB缩小到您在最终图像中看到的1MB文件.

  • @SergiyKolesnikov 取决于您想花多少时间过早地进行优化。风险在于花费开发人员数小时的时间、大量的额外带宽和存储来节省几毫秒的运行时间。与许多与性能相关的事情一样,也存在极端情况,需要在努力解决问题之前先衡量问题。 (2认同)
  • @未知,这取决于主机上其他图像(包括同一图像的先前版本)通过压缩层而丢失的层重用程度。层重用可以节省大量的网络和磁盘使用量,但当图像只有一个层时,层重用就会丢失。 (2认同)

Rui*_* Ma 17

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

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

docker inspect <image_id> | jq -r '.[].RootFS'
Run Code Online (Sandbox Code Playgroud)

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

  1. 图像与其组成层之间的对应关系
  2. 磁盘上图层的实际位置和大小

这篇博客提供了很多见解.


Adi*_*ari 10

Per Docker的图像规格

图像由图层组成.每一层都是一组文件系统更改.图层没有配置元数据,例如环境变量或默认参数 - 这些是图像的整体属性,而不是任何特定图层.

因此,从本质上讲,图层只是对文件系统所做的一组更改.


ciz*_*ixs 9

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

图像由通常从Dockerfile生成的许多层组成,Dockerfile中的每一行都将创建一个新图层,结果是一个图像,由表单表示repo:tag,如ubuntu:15.04.

有关更多信息,请考虑阅读上面的官方文档.


ole*_*nto 6

我曾经认为它们就像之前层的差异。读完这里的一些答案后,我不太确定;它们被描述为文件系统的一组更改。我编写了一些 Dockerfile 来表明它们更像是差异,即它们确实依赖于先前的层。

\n\n

给定这两个 Dockerfile

\n\n
FROM bash\nRUN mkdir /data\nRUN dd if=/dev/zero bs=1024 count=1024 of=/data/one\nRUN dd if=/dev/zero bs=1024 count=1024 of=/data/two\nRUN dd if=/dev/zero bs=1024 count=1024 of=/data/three\n
Run Code Online (Sandbox Code Playgroud)\n\n

\n\n
FROM bash\nRUN mkdir /data\nRUN dd if=/dev/zero bs=1024 count=1024 of=/data/three\nRUN dd if=/dev/zero bs=1024 count=1024 of=/data/two\nRUN dd if=/dev/zero bs=1024 count=1024 of=/data/one\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果它们只是关于文件系统的更改,人们会期望相同的层集,但事实并非如此:

\n\n
$ docker history img_1\nIMAGE               CREATED             CREATED BY                                      SIZE\n30daa166a9c5        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102\xe2\x80\xa6   1.05MB\n4467d16e79f5        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102\xe2\x80\xa6   1.05MB\nc299561fd031        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102\xe2\x80\xa6   1.05MB\n646feb178431        6 minutes ago       /bin/sh -c mkdir /data                          0B\n78664daf24f4        2 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B\n<missing>           2 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry\xe2\x80\xa6   0B\n<more missing...>\n
Run Code Online (Sandbox Code Playgroud)\n\n

\n\n
$ docker history img_2\nIMAGE               CREATED             CREATED BY                                      SIZE\nf55c91305f8c        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102\xe2\x80\xa6   1.05MB\n29b3b627c76f        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102\xe2\x80\xa6   1.05MB\n18360be603aa        6 minutes ago       /bin/sh -c dd if=/dev/zero bs=1024 count=102\xe2\x80\xa6   1.05MB\n646feb178431        6 minutes ago       /bin/sh -c mkdir /data                          0B\n78664daf24f4        2 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B\n<missing>           2 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry\xe2\x80\xa6   0B\n<more missing...>\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n