什么是Docker映像“层”?


164

我是Docker的新手,正在尝试确切地了解Docker 映像是什么。Docker映像的每个定义都使用术语“ layer”,但似乎并未定义layer的含义。

来自官方Docker文档

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

所以我问,什么是层(完全)?有人可以举几个具体的例子吗?这些图层如何“对齐”以形成图像?

Answers:


131

我可能会迟到,但这是我的10美分(与ashishjain的回答相辅相成):

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

考虑以下Dockerfile:

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

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

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

您可以在此处了解更多信息。


13
如果您更改或添加了一个层,则Docker还将构建之后出现的任何层,因为它们可能会受到更改的影响。
亚当

感谢您解释其他答案中缺少的层概念背后的原因。
Seeta Somagani

@David,在上面的示例中,将添加几层?2?还是1?
Gourav Singla '17

1
@GouravSingla应该是2。更改ENV也是更改。看起来该层是git的提交。
PokerFace

最后一个网络链接(https://labs.ctl.io/caching-docker-images/)已损坏。有人建议更换吗?
约翰尼·犹他州

72

使用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 

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


32
虽然我没有投票,但我的猜测是这可以解释如何创建图层,但绝不能回答有关什么是图层的问题。
Lasse V. Karlsen

2
我同意Ashishjain的@ LasseV.Karlsen。我并未对您投反对票,实际上是在对您尝试帮助我(因此+1)表示支持-但是为了使我能够给您绿色支票,我需要了解实际上是一层!再次感谢,继续前进!
smeeb

3
最佳答案imo。对于许多进入“利用docker”的人来说,它为我们提供了各层如何工作的要点。
dtc

6
“ dockerfile中的每一行都会创建一个图层” –这对我了解很有帮助
akirekadu

2
@akirekadu这不是全部。大多数行会创建一个图层,但是只有ADD,COPY或RUN指令会创建图层,这些图层会增加生成的容器映像的大小。我说大多数行是因为如果您将命令链接在一起,或者用反斜杠将换行符转义,则链接命令/转义换行符的序列将形成单个命令。
Scott Simontis,

41

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

使用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

如果您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

每行前面带有,表示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文件。


在文件读取期间遍历图层会带来一些开销,对吗?为了节省这些开销,在一个RUN中组合多个命令(无论如何都必须一起执行)是否有意义?
SergiyKolesnikov

@SergiyKolesnikov取决于您要花多少时间过早优化。风险在于花费数小时的开发人员时间,大量带宽和存储空间来节省毫秒级的运行时间。与性能相关的许多事物一样,存在极端情况,因此需要在努力解决该问题之前对其进行衡量。
BMitch

19

从Docker v1.10开始,随着内容可寻址存储的引入,“层”的概念变得非常不同。层没有图像的概念或不属于图像,它们只是可以在图像之间共享的文件和目录的集合。层和图像变得分离。

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

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

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

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

博客提供了很多见识。


在此SO条目中,我发布了一种相当幼稚的方式来回答我发布的两个问题。
瑞丰马

13

根据Docker 通过The Moby Project图像规范

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

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


我只花了几个小时就找到了它,但是有了这个简洁的答案,我终于明白了一层是什么"Each [Docker] layer is a set of filesystem changes."(假设这是真的。)出于某种原因,当我阅读许多其他文档/时,我不理解这一基本要点。博客/ Q + A / etc,我怀疑限制是他们的,而不是我的。不管怎样,布拉迪·阿迪亚(Brity Aditya)赢得了问题的关注。
约翰尼·犹他州


2

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

有关更多信息,您可以看到如下所示的“ docker history”:

码头工人图像-树
警告:'-tree'已过时,它将很快被删除。查看用法。
└─511136ea3c5a虚拟尺寸:0 B标签:刮擦:最新
  └─59e359cb35ef虚拟大小:85.18 MB
    └─e8d37d9e3476虚拟大小:85.18 MB标签:debian:wheezy
      └─c58b36b8f285虚拟大小:85.18 MB
        └─90ea6e05b074虚拟大小:118.6 MB
          dc─5dc74cffc471虚拟大小:118.6 MB标签:vim:latest


5
找到了有关的新信息:当Docker挂载rootfs时,它像传统的Linux引导一样以只读方式启动,但是随后,它代替了将文件系统更改为读写模式,而是利用联合挂载来添加只读文件系统上的读写文件系统。实际上,可能有多个只读文件系统相互堆叠。我们将这些文件系统中的每一个视为一个层
hiproz

1

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


0

我曾经认为它们就像前几层的差异。在阅读了一些答案之后,我不太确定。它们被描述为文件系统的一组更改。我已经写了一些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...>

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.