我是Docker的新手,我正在努力理解Docker图像到底是什么。Docker映像的每个定义都使用术语“层”,但似乎并没有定义层的含义。
来自Docker官方文档:
我们已经看到Docker镜像是启动Docker容器的只读模板。每张图像都由一系列图层组成。Docker利用联合文件系统将这些层组合成单个映像。联合文件系统允许透明地覆盖独立文件系统(称为分支)的文件和目录,从而形成一个统一的文件系统。
所以我问,什么是层;有人能举几个具体的例子吗?这些图层是如何“合在一起”形成图像的呢?
我是Docker的新手,我正在努力理解Docker图像到底是什么。Docker映像的每个定义都使用术语“层”,但似乎并没有定义层的含义。
来自Docker官方文档:
我们已经看到Docker镜像是启动Docker容器的只读模板。每张图像都由一系列图层组成。Docker利用联合文件系统将这些层组合成单个映像。联合文件系统允许透明地覆盖独立文件系统(称为分支)的文件和目录,从而形成一个统一的文件系统。
所以我问,什么是层;有人能举几个具体的例子吗?这些图层是如何“合在一起”形成图像的呢?
当前回答
我个人的理解是我们可以将docker层与github提交进行比较。对于你的base image(你的fresh master repo),你做了几次提交,每一次提交都在改变你的master状态,这在docker中也是一样的,每一层都在做一些基于之前中间层的操作。然后,这一层成为下一层的新中间层。
其他回答
根据Docker通过The Moby Project的图像规范:
图像由层组成。每一层都是一组文件系统 的变化。层没有配置元数据,比如环境 变量或默认参数-这些是图像的属性 一个整体,而不是任何特定的层。
因此,从本质上讲,一层只是对文件系统所做的一组更改。
我认为官方文件给出了非常详细的解释: https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/。
(来源:docker.com)
image由很多层组成,这些层通常是从Dockerfile中生成的,Dockerfile中的每一行都将创建一个新的层,结果是一个image,用repo:tag的形式表示,如ubuntu:15.04。
欲了解更多信息,请考虑阅读上面的官方文档。
自Docker v1.10以来,随着内容可寻址存储的引入,“层”的概念变得非常不同。图层没有图像或属于图像的概念,它们只是可以在图像之间共享的文件和目录的集合。图层和图像被分离。
例如,在一个从基础映像本地构建的映像上,比如说ubuntu:14.04, docker history命令会生成映像链,但是有些映像id会显示为'missing',因为构建历史不再加载。组成这些图像的图层可以通过
docker inspect <image_id> | jq -r '.[].RootFS'
如果存储驱动选择为aufs,则层内容存储在/var/lib/docker/aufs/diff。但是这些层是用一个随机生成的缓存ID命名的,似乎只有出于安全考虑,Docker引擎才知道层和它的缓存ID之间的链接。我还在想办法弄清楚
图像与其构成层之间的对应关系 磁盘上层的实际位置和大小
这个博客提供了很多见解。
举个例子,它们对我来说最有意义……
使用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映像检查,并查看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
最新的层被列在最上面。值得注意的是,底部有两层相当古老。它们来自于忙碌的形象本身。当您构建一个图像时,您将继承在FROM行中指定的图像的所有层。还添加了用于更改图像元数据的层,如CMD行。它们几乎不占用任何空间,更多的是用于记录应用于您正在运行的映像的设置。
为什么层?
The layers have a couple advantages. First, they are immutable. Once created, that layer identified by a sha256 hash will never change. That immutability allows images to safely build and fork off of each other. If two dockerfiles have the same initial set of lines, and are built on the same server, they will share the same set of initial layers, saving disk space. That also means if you rebuild an image, with just the last few lines of the Dockerfile experiencing changes, only those layers need to be rebuilt and the rest can be reused from the layer cache. This can make a rebuild of docker images very fast.
在容器中,可以看到映像文件系统,但是没有复制该文件系统。在这些映像层之上,容器装载它自己的读写文件系统层。文件的每次读取都沿着层向下,直到它到达一个标记了该文件要删除的层,在该层中有该文件的副本,或者读取耗尽了可以搜索的层。每次写入都在容器特定的读写层中进行修改。
减少层膨胀
分层的一个缺点是构建的图像会复制文件或传送在后面的层中删除的文件。解决方案通常是将多个命令合并为一个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: ~ 1 mb 第一张图片:约6MB 第二张图片:约2MB
通过合并这个虚构示例中的一些行,我们在图像中得到了相同的结果内容,并将图像从5MB缩小到最终图像中所看到的1MB文件。
我曾经认为它们就像前一层的差别。在阅读了这里的一些答案后,我不太确定;它们被描述为对文件系统的更改集。我写了一些dockerfile来说明它们更像差异文件,也就是说,它们确实依赖于前面的层。
给定这两个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
and
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...>
and
$ 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...>
您可以看到,即使在这两种情况下对文件系统的更改是相同的,顺序也很重要。