本章讲解知识点
-
- Docker 镜像
-
- Union File System(联合文件系统)技术
-
- 回说 Docker 镜像分层
-
- Docker 镜像分层原理
1. Docker 镜像
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时的库、环境变量和配置文件。
镜像是一个只读模板,带有创建 Docker 容器的说明。通常,一个镜像基于另一个镜像,并带有一些额外的定制。例如,可以构建一个基于 ubuntu 镜像的镜像,安装 Apache web 服务器和自己的应用程序,以及运行应用程序所需的配置细节,最终服务运行或项目运行都是在容器中的。
我们可以从开源镜像仓库里拉取镜像,我们在拉取镜像时,会发现 Docker 给我们的回显中,镜像似乎是分很多层的:
[root@master mtuser]# docker pull quay.io/coreos/flannel:v0.14.0
v0.14.0: Pulling from coreos/flannel
801bfaa63ef2: Pull complete
e4264a7179f6: Pull complete
bc75ea45ad2e: Pull complete
78648579d12a: Pull complete
3393447261e4: Pull complete
071b96dd834b: Pull complete
4de2f0468a91: Pull complete
Digest: sha256:4a330b2f2e74046e493b2edc30d61fdebbdddaaedcb32d62736f25be8d3c64d5
Status: Downloaded newer image for quay.io/coreos/flannel:v0.14.0
quay.io/coreos/flannel:v0.14.0
这是一个很有意思的现象,因为它引出了容器领域十分重要的技术:Union File System(联合文件系统)技术。正是Union File System(联合文件系统)技术的利用,容器技术才得以大行其道。
2. Union File System(联合文件系统)技术
联合文件系统(Union File System)是一种将多个文件系统挂载到同一目录下的技术,使得这些文件系统中的内容可以像一个文件系统一样被访问。在联合文件系统中,多个不同的文件系统以层次结构的形式被组织在一起,从而使得用户可以通过单一的挂载点来访问多个不同的文件系统。
联合文件系统的实现通常使用一些特殊的技术,比如链接(Link)和重定向(Redirect)。具体来说,联合文件系统使用链接技术将多个不同的文件系统的内容组合在一起,从而实现了文件系统的叠加。在联合文件系统中,每个文件系统都有一个挂载点,通过在这个挂载点上叠加其他的文件系统,就可以将它们的内容融合在一起。而重定向技术则用于实现对文件和目录的访问,使得用户可以在联合文件系统中访问多个不同文件系统的内容,同时还能够自由地添加、删除或更改这些文件系统中的文件和目录。
联合文件系统的优点包括可以将多个不同的文件系统整合在一起,从而方便用户管理和访问这些文件系统中的内容;可以在不同的文件系统之间共享文件和目录;可以根据需要动态地添加、删除和更改文件系统,从而提高了系统的灵活性。不过,联合文件系统的缺点也很明显,其中包括可能会导致性能下降、出现一些安全问题,以及需要特殊的文件系统支持等问题。
联合文件系统可以把只读和可读写文件系统合并在一起,具有 Copy-on-Write 功能,允许只读文件系统的修改可以保存到可写文件系统当中。
假设有两个文件系统:A 和 B,其中 A 包含文件 /A/foo
和目录 /A/bar
,B 包含文件 /B/bar
和目录 /B/foo
。使用联合文件系统技术可以将这两个文件系统合并成一个单独的文件系统 C。具体操作如下:
创建一个目录 /C
,作为联合文件系统的挂载点。
将文件系统 A 挂载到 /C
上,使得文件 /A/foo
和目录 /A/bar
可以在 /C
目录下访问。
将文件系统 B 挂载到 /C
上,由于 /C
目录已经被 A 挂载,因此 B 将被挂载到 /C
目录下的一个新的子目录,比如 /C/B
。
现在,/C
目录下的内容将包括 A 和 B 两个文件系统的内容。因此,用户可以通过 /C/foo
访问文件 /A/foo
,通过 /C/bar
访问目录 /A/bar
,通过 /C/B/bar
访问文件 /B/bar
,通过 /C/B/foo
访问目录 /B/foo
。
3. 回说 Docker 镜像分层
3.1. 基础概念
联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像,可以制作各种具体的应用镜像。
Docker 镜像是由多个分层(layers)组成的,每个分层都包含了文件系统的一部分。这种分层的设计使得 Docker 镜像非常轻量级,因为多个镜像可以共享相同的分层。当一个新的镜像被创建时,Docker 只需要为新镜像添加一些额外的分层,而不是重新创建整个文件系统。
每个 Docker 镜像都由一个或多个分层组成,这些分层从基础镜像开始,一步步地添加文件系统的修改。每个分层都有一个唯一的 ID,并且可以在其他镜像中被共享。当你启动一个容器时,Docker 会将这个容器的文件系统组合成一个只读的联合文件系统,其中包含了容器所使用的所有分层。这种方式可以让容器在启动时非常快速地创建。
联合文件系统一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
3.2. 基础镜像
在 Docker 中,基础镜像是构建其他镜像的起点。基础镜像是一个包含操作系统和一些最基本软件包的最小镜像。它提供了一个最小的运行环境,使得应用程序可以在 Docker 容器中运行。
基础镜像可以由用户自己创建,也可以从 Docker Hub 或其他 Docker 镜像仓库中获取。例如,一个常见的基础镜像是 ubuntu,它提供了一个基于 Ubuntu 操作系统的最小环境。在此基础镜像上,用户可以构建自己的应用程序或服务的镜像,添加所需的软件包和配置文件。
使用基础镜像的好处是可以减少构建镜像的时间和大小,因为基础镜像已经提供了必要的操作系统和软件包。此外,基础镜像通常已经经过了优化和测试,可以提供更高的稳定性和安全性。
3.3 镜像的内核与发行版
我们之前创建虚拟机时用到的 ISO 镜像文件来安装操作系统,它包含了两个部分:
1.Linux 内核版本:
[root@node1 docker]# uname -r
3.10.0-1160.el7.x86_64
2.系统发行版本(Ubuntu、CentOS)
[root@node1 docker]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
而 Docker 镜像则不一样,它不包含 Linux 内核,只是系统发行版本:
[root@node1 docker]# docker images | grep centos
centos latest 5d0da3dc9764 18 months ago 231MB
从它们的文件大小也可能看出 ISO 镜像文件和 Docker 镜像的差别。
所以 Docker 基础镜像不包含 Linux 内核,而是共享主机操作系统的 Linux 内核,这是一个很重要的结论。
3.4 内核空间和用户空间的文件系统
Linux 操作系统由内核空间和用户空间组成。
- 内核空间是 kernel,Linux 刚启动时会加载 bootfs 文件系统,之后 bootfs 会被卸载掉。
- 用户空间是 rootfs 文件系统,包含我们熟悉的
/dev, /proc, /bin
等目录。
对于基础镜像来说,底层直接共享主机操作系统的 Linux 内核,自己只需要提供 rootfs 就行了。而对于一个精简的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了。基础镜像提供的是最小安装的 Linux 发行版。
然后利用联合文件系统技术,就等于将 bootfs 文件系统和 rootfs 文件系统叠加起来利用了。
4. Docker 镜像分层原理
4.1 基本原理
Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的。比如我们现在构建一个新的镜像,Dockerfile 如下:
# Version: 0.0.1
FROM debian 1.新镜像是从 Debian base 镜像上构建。
RUN apt-get update && apt-get install -y emacs 2.安装 emacs 编辑器。
RUN apt-get install -y apache2 3.安装 apache2。
CMD ["/bin/bash"] 4.容器启动时运行 bash。
可以看到,Docker 镜像都起始于一个基础镜像层,新镜像是从基础镜像一层一层叠加生成的。当进行修改或增加新的内容时,就会在当前镜像层上,创建新的镜像层。
4.2 为什么要镜像分层
Docker 采用镜像分层的机制,主要是为了提高镜像的可重复性和可维护性,同时也能节省存储空间。具体来说,镜像分层可以实现以下几点:
-
减少存储空间:Docker 镜像的每一层都是只读的,只有最上层的容器层可以被写入。这意味着多个镜像可以共享相同的底层镜像层,从而减少存储空间。
-
支持版本控制:每一个镜像层都可以被认为是一个单独的版本,这使得镜像可以进行版本控制和管理,方便维护和升级。
-
提高可重复性:每个容器都可以由多个层次构成,这使得容器的创建和销毁变得更加灵活。镜像分层也可以确保容器的可重复性,因为每个容器都可以使用相同的基础镜像层和组件。
-
支持增量更新:由于每个镜像层都是只读的,更新镜像时只需要对需要更新的层进行修改,而不需要重新构建整个镜像,这提高了镜像的更新效率。
4.3 容器 Copy-on-Write(COW) 特性
容器 Copy-on-Write (COW) 特性是 Docker 中非常重要的一个特性。它的作用是让容器创建时尽可能地复用镜像层,从而节省存储空间并提高容器的创建速度。
当一个容器启动时,Docker 会创建一个容器层作为容器的可写层,并将该容器层与镜像层进行联合挂载。在容器运行时,所有的写操作都会被记录到容器层中,而不会对镜像层进行任何修改。因此,如果多个容器都使用同一个镜像,它们会共享相同的镜像层,而容器层则会针对每个容器单独记录写入操作。这种方式可以避免重复复制相同的数据,从而提高存储效率,并且能够更快地启动新的容器。
当容器层中的文件被修改时,Docker 会使用 Copy-on-Write (COW) 技术来创建新的文件副本,而不是在原始镜像层上直接修改文件。这意味着对文件的修改不会影响其他容器或镜像的使用,同时也能够更好地支持多个容器同时使用同一个镜像的场景。
4.4 可写的容器层
Docker 镜像都是只读的,当你创建一个新的容器时,你会在基础层之上添加一个新的可写层,该层通常都称为容器层。
对正在运行的容器所做的所有更改,例如写入新文件、修改现有文件和删除文件,都被写入这个可写的容器层。
在容器运行时,所有的写操作都会被记录到容器层中,而不会对镜像层进行任何修改,其余的镜像层都是只读的。
对容器层文件的操作规则:
- 添加文件时,新文件直接被添加到容器层中
- 读取文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后打开并读入内存。
- 修改文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改。
- 删除文件时,Docker 也是从上往下依次在各镜像层中查找此文件。找到后,会在容器层中记录下此删除操作,而不是直接删除文件(仅仅是记录删除操作)。
4.5 小示例
1.查看 flannel 镜像
[root@node1 docker]# docker images | grep flan
quay.io/coreos/flannel v0.14.0 8522d622299c 22 months ago 67.9MB
2.查看 flannel 镜像分层
[root@node1 docker]# docker inspect 8522d622299c | grep "RootFS" -A 12
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:777b2c648970480f50f5b4d0af8f9a8ea798eea43dbcf40ce4a8c7118736bdcf",
"sha256:815dff9e0b5742e2a1b9c06fc748a4181a349d638bd4d66e5072df83f5fba336",
"sha256:2e16188127c8c7ccc2d27f3ecb0c9de6ce109fbaeb3600d060a52b024dbabcc2",
"sha256:eb738177d102ee90366379b7a67d6f8005b24d28a7049156f4c12da0c7321480",
"sha256:b613d890216ce3ba102eb169e30d768aefcb0536f95fc60812d9566aac64ce78",
"sha256:8a984b390686f05792e045c24dc98042be50334e94fb46990fed1646f52d3399",
"sha256:814fbd599e1fb4f5a522ffedff9f9dfee491e3b566c1a550dfdf908e04c9bc3d"
]
},
3.进入 /var/lib/docker/image/overlay2/layerdb/sha256
目录查看分层
[root@node1 sha256]# ll | grep 777b2c648970480f50f5b4d0af8f9a8ea798eea43dbcf40ce4a8c7118736bdcf
drwx------. 2 root root 71 Feb 19 01:20 777b2c648970480f50f5b4d0af8f9a8ea798eea43dbcf40ce4a8c7118736bdcf
[root@node1 sha256]# ll | grep 815dff9e0b5742e2a1b9c06fc748a4181a349d638bd4d66e5072df83f5fba336
[root@node1 sha256]# ll | grep 2e16188127c8c7ccc2d27f3ecb0c9de6ce109fbaeb3600d060a52b024dbabcc2
[root@node1 sha256]# ll | grep eb738177d102ee90366379b7a67d6f8005b24d28a7049156f4c12da0c7321480
[root@node1 sha256]# ll | grep b613d890216ce3ba102eb169e30d768aefcb0536f95fc60812d9566aac64ce78
[root@node1 sha256]# ll | grep 8a984b390686f05792e045c24dc98042be50334e94fb46990fed1646f52d3399
[root@node1 sha256]# ll | grep 814fbd599e1fb4f5a522ffedff9f9dfee491e3b566c1a550dfdf908e04c9bc3d
除了 777b2c648970480f50f5b4d0af8f9a8ea798eea43dbcf40ce4a8c7118736bdcf
以外,其他分层在其他目录下。我们来找找
4.利用 find
命令找寻其他分层的位置
[root@node1 sha256]# find / -name 815dff9e0b5742e2a1b9c06fc748a4181a349d638bd4d66e5072df83f5fba336
/var/lib/docker/image/overlay2/distribution/v2metadata-by-diffid/sha256/815dff9e0b5742e2a1b9c06fc748a4181a349d638bd4d66e5072df83f5fba336
[root@node1 sha256]# find / -name 2e16188127c8c7ccc2d27f3ecb0c9de6ce109fbaeb3600d060a52b024dbabcc2
/var/lib/docker/image/overlay2/distribution/v2metadata-by-diffid/sha256/2e16188127c8c7ccc2d27f3ecb0c9de6ce109fbaeb3600d060a52b024dbabcc2
[root@node1 sha256]# find / -name eb738177d102ee90366379b7a67d6f8005b24d28a7049156f4c12da0c7321480
/var/lib/docker/image/overlay2/distribution/v2metadata-by-diffid/sha256/eb738177d102ee90366379b7a67d6f8005b24d28a7049156f4c12da0c7321480
[root@node1 sha256]# find / -name b613d890216ce3ba102eb169e30d768aefcb0536f95fc60812d9566aac64ce78
/var/lib/docker/image/overlay2/distribution/v2metadata-by-diffid/sha256/b613d890216ce3ba102eb169e30d768aefcb0536f95fc60812d9566aac64ce78
[root@node1 sha256]# find / -name 8a984b390686f05792e045c24dc98042be50334e94fb46990fed1646f52d3399
/var/lib/docker/image/overlay2/distribution/v2metadata-by-diffid/sha256/8a984b390686f05792e045c24dc98042be50334e94fb46990fed1646f52d3399
[root@node1 sha256]# find / -name 814fbd599e1fb4f5a522ffedff9f9dfee491e3b566c1a550dfdf908e04c9bc3d
/var/lib/docker/image/overlay2/distribution/v2metadata-by-diffid/sha256/814fbd599e1fb4f5a522ffedff9f9dfee491e3b566c1a550dfdf908e04c9bc3d
可以看到,其他分层在另外的目录下,你看,利用 UFS 联合文件系统将不同位置的分层合并成了一个镜像文件,是不是很有意思。
5.用 docker history
查看镜像分层
[root@node1 mtuser]# docker history quay.io/coreos/flannel:v0.14.0 --no-trunc > text
IMAGE CREATED CREATED BY SIZE COMMENT
sha256:8522d622299ca431311ac69992419c956fbaca6fa8289c76810c9399d17c69de 22 months ago /bin/sh -c #(nop) ENTRYPOINT ["/opt/bin/flanneld"] 0B
<missing> 22 months ago /bin/sh -c /iptables-wrapper-installer.sh 1.93kB
<missing> 22 months ago /bin/sh -c #(nop) COPY file:1a43b192bfd032f1dbb35156e5e82a505618eb82ae8dd58b3cdec45882d7e102 in / 7.66kB
<missing> 22 months ago /bin/sh -c #(nop) COPY file:8f67962c12e58671bee18c93535885714647b91271193b7dc5832188905ec9d1 in /opt/bin/ 2.14kB
<missing> 22 months ago /bin/sh -c #(nop) COPY file:d4fe84b0a6b8e73f9c28b314a51706f4a65ab0fde83db87b000ba72e6efbacf6 in /opt/bin/flanneld 49.3MB
<missing> 2 years ago /bin/sh -c apk add wireguard-tools --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/community 2.18MB
<missing> 2 years ago /bin/sh -c apk add --no-cache iproute2 net-tools ca-certificates iptables strongswan && update-ca-certificates 10.8MB
<missing> 2 years ago /bin/sh -c #(nop) ENV FLANNEL_ARCH=amd64 0B
<missing> 2 years ago /bin/sh -c #(nop) LABEL maintainer=Tom Denham <tom@tigera.io> 0B
<missing> 2 years ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 2 years ago /bin/sh -c #(nop) ADD file:ec475c2abb2d46435286b5ae5efacf5b50b1a9e3b6293b69db3c0172b5b9658b in / 5.58MB
flannel 的镜像分层就是通过上面的命令一层一层叠加的。
面试题
1. 什么是 Docker 镜像分层?⭐⭐⭐⭐
Docker 将一个 Docker 镜像分解成多个不同的层(Layer),每个层都是只读的文件系统,层与层之间是相互独立的。这种分层的设计可以提高 Docker 镜像的复用性和共享性。每个 Docker 镜像都是由一个或多个只读层(镜像层)和一个可读写层(容器层)组成。只读层是可以在不同镜像之间共享的,因此 Docker 的镜像可以通过利用这种分层的设计来尽可能地减少重复的数据存储,提高镜像的复用性。同时,Docker 镜像分层的设计还可以方便地管理和维护镜像。例如,在更新镜像时,只需要修改镜像的某个层,而不需要对整个镜像重新构建,从而节省了时间和资源。
2. Docker 镜像分层的优势是什么?⭐⭐⭐⭐
-
减小镜像大小:因为每个层都是只读的,所以在创建新镜像时,只需要新增或修改一层即可,不会影响到已有的层,从而减小了镜像的大小。同时,不同的镜像可以共享相同的层,从而减少了重复的数据存储,进一步减小了镜像的大小。
-
提高镜像的复用性:由于Docker镜像分层的设计,不同的镜像可以共享相同的层,这就使得镜像的复用性得到了极大地提高。例如,当多个容器需要使用同一份镜像时,它们可以共享相同的只读层,这样就可以节省存储空间,并且不需要重复下载和构建镜像。
-
方便镜像的管理和维护:由于每个层都可以单独更新和管理,因此 Docker 镜像的管理和维护变得更加容易。例如,在更新镜像时,只需要修改镜像的某个层,而不需要对整个镜像重新构建,这样就可以节省时间和资源,并且降低出错的风险。
-
提高镜像的构建效率:利用 Docker 镜像分层的特性,可以将常用的层缓存下来,从而减少重复的构建过程,加快镜像构建的速度。同时,通过多阶段构建的方式,将构建过程拆分成多个阶段,每个阶段都可以利用不同的Docker镜像分层,从而进一步提高构建速度。
3. Docker 镜像的分层是如何实现的?⭐⭐⭐⭐
Docker 镜像分层的实现是通过 UFS(Union File System)联合文件系统来实现的。联合文件系统(Union File System)是一种将多个文件系统挂载到同一目录下的技术,使得这些文件系统中的内容可以像一个文件系统一样被访问。在联合文件系统中,多个不同的文件系统以层次结构的形式被组织在一起,从而使得用户可以通过单一的挂载点来访问多个不同的文件系统。
在 Docker 中,每个镜像都是由多个只读层(镜像层)和一个可读写层(容器层)组成。Docker 通过 UFS 联合文件系统将这些只读层和可读写层合并在一起,形成一个联合文件系统。
4. 如何利用 Docker 镜像分层优化镜像构建过程?⭐⭐⭐
利用 Docker 镜像分层优化镜像构建过程可以从以下几个方面入手:
-
利用缓存:在构建 Docker 镜像时,Docker 会利用缓存来避免重复的构建过程。如果某个镜像层已经被构建过,Docker 就可以直接从缓存中获取该层,从而加快构建速度。因此,为了利用缓存,我们应该尽量减少构建过程中的变化,例如在 Dockerfile 中将不经常修改的指令放在前面,将经常修改的指令放在后面。
-
利用多阶段构建:Docker 从 17.05 版本开始支持多阶段构建。利用多阶段构建,可以将构建过程拆分成多个阶段,并在每个阶段中利用不同的 Docker 镜像分层,从而减少重复的构建过程。
-
选择合适的基础镜像:选择合适的基础镜像可以减少构建过程中的变化,从而提高构建速度。通常情况下,我们应该选择官方的基础镜像,因为官方的基础镜像已经被优化过,同时也得到了广泛的测试和验证。
-
减小镜像大小:减小镜像大小可以加快构建速度,同时也可以减少存储空间的占用。为了减小镜像大小,我们可以采用一些常用的优化技巧,例如将多个指令合并成一个、使用多阶段构建、删除无用文件和缓存等。
5. 镜像层是可写的吗?怎么实现可写⭐⭐⭐⭐
Docker 镜像层是只读的,这是为了保证镜像的不可变性,也就是说,一个镜像被创建之后,它的每一层都是只读的,不能被修改。这样可以保证每个容器都以相同的镜像层为基础,从而保证容器之间的环境一致性。
然而,在有些情况下,我们需要对容器进行一些修改,例如安装软件、修改配置文件等。此时,我们可以通过在容器层上创建一个新的可写层来实现这个目的。Docker 中的可写层也称为容器层(container layer)或工作层(working layer),它是在只读的镜像层之上创建的一个可写层。
当我们启动一个容器时,Docker 会在镜像层之上创建一个可写层,并将这个可写层挂载到容器的文件系统中。容器层中的所有修改都将保存在这个可写层中,而镜像层则保持不变。这样,即使多个容器都使用同一个镜像,它们之间也可以有不同的可写层。
6. Docker 中基础镜像的作用⭐⭐⭐⭐
Docker 中基础镜像是用来构建 Docker 镜像的基础,也是构建整个 Docker 镜像的起点。它是一个只读的文件系统,包含了一些基本的操作系统和软件组件,例如操作系统内核、文件系统、运行时环境、库文件、工具等。
使用基础镜像可以避免从头开始构建 Docker 镜像,从而节省时间和精力。基础镜像已经被优化过,并且得到了广泛的测试和验证,因此可以保证镜像的稳定性和可靠性。
7. 容器层删除文件时,文件本体会被删除吗?⭐⭐⭐⭐
当容器层中的文件被删除时,文件本体不会被删除,因为容器层是一个可写层,它是在只读的镜像层之上创建的一个可写层,容器层中的所有修改都将保存在这个可写层中,而镜像层则保持不变。因此,当容器中的文件被删除时,实际上是删除了容器层中的文件记录,而不是删除了镜像层中的文件本体。
8. centos 镜像几个 G,但是 docker centos 镜像才几百 M,这是为什么?⭐⭐⭐⭐
这是因为 CentOS 镜像是完整的操作系统镜像,包含了所有的软件包和文件,而 Docker 镜像则是基于操作系统的一层层的增量更新,每一层都只包含一个或几个操作,可以共用基础镜像,因此其大小较小。
9. 讲一下镜像的分层结构以及为什么要使用镜像的分层结构?⭐⭐⭐⭐
一个新的镜像其实是从基础镜像一层一层叠加生成的。每安装一个软件,dockerfile 中使用 RUM 命令,就会在现有镜像的基础上增加一层,这样一层一层的叠加最后构成整个镜像。
分层结构最大的一个好处就是:共享资源。比如:有多个镜像都从相同的基础镜像构建而来,那么只需在磁盘上保存一份基础镜像;同时内存中也只需加载一份基础镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
10. 说说容器 Copy-on-Write(COW) 特性⭐⭐⭐⭐
容器 Copy-on-Write (COW) 特性是 Docker 中非常重要的一个特性。它的作用是让容器创建时尽可能地复用镜像层,从而节省存储空间并提高容器的创建速度。
当一个容器启动时,Docker 会创建一个容器层作为容器的可写层,并将该容器层与镜像层进行联合挂载。在容器运行时,所有的写操作都会被记录到容器层中,而不会对镜像层进行任何修改。因此,如果多个容器都使用同一个镜像,它们会共享相同的镜像层,而容器层则会针对每个容器单独记录写入操作。这种方式可以避免重复复制相同的数据,从而提高存储效率,并且能够更快地启动新的容器。
当容器层中的文件被修改时,Docker 会使用 Copy-on-Write (COW) 技术来创建新的文件副本,而不是在原始镜像层上直接修改文件。这意味着对文件的修改不会影响其他容器或镜像的使用,同时也能够更好地支持多个容器同时使用同一个镜像的场景。