镜像分层
Docker
镜像是由一系列层来构成的,每层代表Dockerfile
中的一条指令,依下面Dockerfile
为例:
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py
该Dockerfile
包含四个命令,每个命令都会新创建一个层。FROM
语句会从ubuntu:18.04
镜像创建一个层。COPY
指令会从Docker
客户端的当前目录下添加一些文件。RUN
指令使用了make
指令来构建。最后CMD
是☞在容器中运行什么命令。
而对于Docker
来说,创建新容器时,每一层都会彼此堆叠,可以在基础层的基础上添加新的可写容器层。对容器的所做的所有更改都将写入到该可写容器层中。下图显示了基于Ubuntu 15.04
的容器。对于Image layers
都是Read Only
的。
容器和层
对于容器和镜像(container
和image
)的主要区别就是顶部的可写层(the top writable layer
),在容器中添加数据或者修改现有数据的所有读写操作都会存储在此可写层中。删除容器后,可写层也会被删除,而基础镜像则保持不变。
每个容器都会有自己的可写层,所有的改变都存储在该容器层中。多个容器可以共享对同一基础镜像的访问,但可以拥有自己的数据状态。
如果需要对完全相同的数据的访问权限,需要将该数据存储在
Docker volume
中并且装入到容器中。
Copy-on-write策略
写时复制是一种共享和复制文件的策略,可以最大程度地提高效率。
当我们使用docker
pull`指令时会从仓库拉下一个镜像例如下面的例子
$ docker pull ubuntu:18.04
18.04: Pulling from library/ubuntu
f476d66f5408: Pull complete
8882c27f669e: Pull complete
d9af21273955: Pull complete
f5029279ec12: Pull complete
Digest: sha256:ab6cb8de3ad7bb33e2534677f865008535427390b117d7939193f8d1a6613e34
Status: Downloaded newer image for ubuntu:18.04
所有的这些层都会在Docker
主机本地存储区域内存储,可以通过以下指令来列出
$ ls /var/lib/docker/overlay2
16802227a96c24dcbeab5b37821e2b67a9f921749cd9a2e386d5a6d5bc6fc6d3
377d73dbb466e0bc7c9ee23166771b35ebdbe02ef17753d79fd3571d4ce659d7
3f02d96212b03e3383160d31d7c6aeca750d2d8a1879965b89fe8146594c453d
ec1ec45792908e90484f7e629330666e7eee599f08729c93890a7205a6ba35f5
l
依下面的Dockerfile
为例,创建一个名为acme/my-base-image:1.0
的镜像。
FROM ubuntu:18.04
COPY . /app
第二个Dockerfile
依第一个Dockerfile
为例,但是有用一个增加的层。
FROM acme/my-base-image:1.0
CMD /app/hello.sh
第二个镜像包含第一个镜像的所有层,再加上带有CMD
指令的新层,以及一个可读写的容器层。因为Docker
已经拥有第一个镜像的所有层,所以不需要再次将其pull
下来。
构建这两个Dockerfile
镜像,可以通过docker history
和docker image ls
指令来验证共享层的加密ID
是否相同。
- 创建一个新文件夹
cow-test/
,并切换到该目录下。 - 在
cow-test/
目录下,创建一个名为hello.sh
的文件内容如下:
#!/bin/sh
echo "Hello world"
然后设置可执行
chmod +x hello.sh
- 将上面第一个镜像复制为
Dockerfile.base
。 - 将上面第二个镜像复制为
Dockerfile
- 在
cow-test/
目录下,构建第一个镜像
$ docker build -t acme/my-base-image:1.0 -f Dockerfile.base .
Sending build context to Docker daemon 812.4MB
Step 1/2 : FROM ubuntu:18.04
---> d131e0fa2585
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 image ls
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
- 查看每个镜像中层的大小
$ docker history bd09118bcef6
IMAGE CREATED CREATED BY SIZE COMMENT
bd09118bcef6 4 minutes ago /bin/sh -c #(nop) COPY dir:35a7eb158c1504e... 100B
d131e0fa2585 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
d131e0fa2585 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 ps -s
指令来查看容器的大小。
size
表示每个容器的可写层使用的大小。virtual size
表示容器使用的用于只读镜像的数据的数据量加上容器可写层的大小。多个容器可以共享一些或者所有的只读镜像数据。
因此每个正在运行的容器所使用磁盘总空间大小是virtual size
和size
的某种组合。如果多个容器从相同的基础镜像开始,所以这些容器在磁盘上的总大小为所有容器的大小size
(SUM of all container size
)加上一个镜像的大小(virtual size
–size
)
本文简单介绍了docker
镜像分层。
- 镜像如果共享了相同的层只会在
/var/lib/docker
下存储一个,通过这个可以大大减少容器编译、推送的时长。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/309752.html