Author:rab
目录
前言
首先我们要清楚,Docker 的镜像结构是分层的,镜像本身是只读的(不管任何一层),当我们基于某镜像运行一个容器时,会有一个新的可写层被加载到镜像的顶部,我们通常将这一层称之为容器层
,容器层
之下的都称之为镜像层
。我们所有对容器的增删操作都只会发生在容器层
中,因此,容器层
保存的是从容器运行时开始到当前的数据变化部分,不会对镜像层
本身进行任何修改。镜像的其他特性就不在一一举例了,我们现在的目标是镜像的缓存特性
,镜像的缓存有什么优势?它在哪方面实现缓存?接下来我们来细看一下。
一、构建缓存
1、什么是构建?
Docker 镜像构建使用分层文件系统的概念,每个 Dockerfile 指令都会生成一个新的文件系统层,这些层通过联合文件系统(UnionFS)技术叠加在一起,形成最终的镜像。
2、什么是缓存?
Docker 使用缓存来提高构建效率,当构建镜像时,如果之前的层已经存在并且没有发生更改(顺序上的更改),那 Docker 将重用这些层,而不是重新生成它们,这可以显著减少构建时间和服务器带宽使用。
3、案例演示缓存特性
-
创建 Dockerfile 文件并构建镜像
shmkdir -p /root/dist && cd /root/dist # 创建构建上下文目录 vim Dockerfile # 添加下面内容
shFROM centos:centos7.9.2009 RUN yum install -y wget
shdocker build -t centos:v1 .
shSending build context to Docker daemon 2.048kB ① Step 1/2 : FROM centos:centos7.9.2009 ② ---> eeb6ee3f44bd Step 2/2 : RUN yum install -y wget ③ ---> Running in a51051b0f4a5 Loaded plugins: fastestmirror, ovl Determining fastest mirrors * base: mirrors.ustc.edu.cn * extras: mirrors.ustc.edu.cn * updates: mirrors.huaweicloud.com Resolving Dependencies --> Running transaction check ---> Package wget.x86_64 0:1.14-18.el7_6.1 will be installed --> Finished Dependency Resolution ... ... Installed: wget.x86_64 0:1.14-18.el7_6.1 Complete! Removing intermediate container a51051b0f4a5 ④ ---> 9a4b27a6d88d Successfully built 9a4b27a6d88d ⑤ Successfully tagged centos:v1 ⑥
镜像构建流程分析:
①:向 Docker 守护进程发送构建上下文,即将
/root/dist
目录下的所有文件发送给Docker daemon
,因此不要将/
、/usr
、/etc
等目录作为构建上下文
发送给Docker daemon
,否则构建出的镜像将会很臃肿,且构建会很缓慢甚至有可能会构建失败,所以为什么一开始我就创建了一个空目录(/root/dist
)作为我上下文的存储目录。②:步骤1(Step 1/2),将
centos:centos7.9.2009
作为构建的基础(base)镜像,该 base 镜像 ID 为eeb6ee3f44bd
。③:步骤1(Step 2/2),开始执行 Dockerfile 中的指令
RUN yum install -y wget
,而且是在一个中间(临时)容器上执行的指令,该临时容器的 ID 为a51051b0f4a5
,为什么要使用临时容器?因为 base 镜像是只读的,想要进行增删只能在容器层实现。④:当
RUN yum install -y wget
指令在临时容器a51051b0f4a5
完成安装后,就会将临时容器删除(即构建信息中的Removing intermediate container a51051b0f4a5
部分)并最终将该临时容器保存为镜像,且镜像 ID 为9a4b27a6d88d
。⑤:镜像
9a4b27a6d88d
构建成功。⑥:将镜像
9a4b27a6d88d
打上 Tag 为centos:v1
。
查看本地镜像详情:可见,图中的 TAG、IMAGE ID 与我们 ⑤、⑥ 是一一对应的,而且你会发现构建后的镜像的容量比基础镜像更大了。
-
Docker 镜像缓存应用
在构建上下文目录中创建一个测试文件(test.txt)进行缓存测试验证。
shtouch test.txt # 创建测试文件 vim Dockerfile # 添加下面内容
shFROM centos:centos7.9.2009 RUN yum install -y wget COPY test.txt /usr/local
shdocker build -t centos:v2 .
shSending build context to Docker daemon 2.56kB Step 1/3 : FROM centos:centos7.9.2009 ---> eeb6ee3f44bd Step 2/3 : RUN yum install -y wget ① ---> Using cache ---> 9a4b27a6d88d Step 3/3 : COPY test.txt /usr/local ② ---> 8f421058bfd8 Successfully built 8f421058bfd8 Successfully tagged centos:v2
镜像构建流程分析:
①:当执行
RUN yum install -y wget
指令时,就使用了缓存Using cache
,且是镜像9a4b27a6d88d
层的缓存,不难看出,这镜像就是上面我们刚构建而成的。②:同样
COPY test.txt /usr/local
指令在临时容器中构建完成并最终生成镜像8f421058bfd8
。查看本地镜像详情:
如果你不想使用镜像缓存可添加 --no-cache 选项:
docker build -t centos:v2 --no-cache .
此时构建镜像就不会使用本地镜像缓存了。
4、注意
这里要注意的是,缓存生效的前提是 Dockerfile 的指令未发生位置顺序上的变动,否则缓存失效(会重新生成新的镜像层,而不会使用缓存),具体案例如下。
-
修改 Dockerfile 文件中的指令顺序并构建镜像
shFROM centos:centos7.9.2009 COPY test.txt /usr/local RUN yum install -y wget
shdocker build -t centos:v3 .
镜像构建流程分析:
此时就没使用缓存了,构建流程与上面是一致的。
查看本地镜像详情:
-
构建说明
尽管从逻辑上来说改动指令顺序对镜像的内容没有改变,但由于镜像的分层结构特性,改动 Dockerfile 指令顺序后 Docker 必须重建受影响的镜像层。
看看分层:图中更清晰地像我们展示了镜像的分层结构(可见这些镜像都是在基础镜像层上继续叠加新的镜像层成为新的镜像),每一层由上至下排列,且上层依赖于下层。
二、Pull 缓存
Docker 除了在镜像构建时可使用缓存,在拉取(pull)镜像时也会使用缓存,具体案例如下。
1、先 pull 一个基础镜像(如:debian)
sh
docker pull debian
2、再 pull 一个应用镜像(如:httpd)
sh
docker pull httpd
Pull 分析:怎么证明本次的 pull 使用了缓存?
下图中,Already exists,说明该 httpd 镜像使用的 base 镜像是 debian 镜像,而 debian 镜像我们已经提前 pull 了,因此就不需额外地再次 pull,提升了镜像的构建效率。
总结
综上案例我们要知道,镜像的缓存可以是在构建时发生,也会在 pull 镜像时发生。
而且我们不难归纳出 Docker 镜像缓存在实际应用中的优势:
-
构建速度提升:使用缓存可以显著提高镜像构建的速度,减少了不必要的工作,节省时间;
-
减少带宽使用:因为构建可以从本地缓存中获取,所以减少了从其他远程仓库下载的数据量,从而减少了带宽的使用;
-
提高可重复性:镜像缓存确保了构建的可重复性,这点毋庸置疑,这也是 Docker 镜像的精髓所在;
-
降低资源消耗:因为缓存特性,Docker Daemon 不必重新执行 Dockerfile 中已经缓存的指令,从而降低了系统资源的消耗;
-
减少互联网依赖:如果缓存中已经存在所需的镜像层,那么构建过程则不依赖于互联网上的外部资源,尤其是在网络不稳定或有限的环境中,更体现出来其可靠性。
---END