小知识:彻底搞懂Docker镜像分层的实现

创建测试镜像

我们创建一个最简单的镜像

1.构建测试镜像v1.0:docker build -t image_test:1.0 .

FROM alpine:3.15.0 #除了继承基础镜像,啥也不做

2.构建测试镜像v2.0:docker build -t image_test:2.0 .

FROM alpine:3.15.0 RUN dd if=/dev/zero of=file1 bs=10M count=1 #添加一个10M的文件file1

3.构建测试镜像v3.0:docker build -t image_test:3.0 .

FROM alpine:3.15.0 RUN dd if=/dev/zero of=file1 bs=10M count=1 #添加一个10M的文件file1 RUN dd if=/dev/zero of=file2 bs=10M count=1 #添加一个10M的文件file2

这样本地就构建了3个测试镜像:

%小知识:彻底搞懂Docker镜像分层的实现-猿站网-插图

查看镜像

我们有2种方法查看镜像:

使用docker inspect:获取镜像的元数据 使用docker history:查看镜像的构建历史 使用docker inspect

使用docker inspect

查看镜像的元数据。

其中Parent可以看到父镜像, Layers这一项下面可以看到镜像的所有层。

使用docker history

使用docker history可以看到镜像的构建历史。

我们每一行列出了镜像包含的层。

%小知识:彻底搞懂Docker镜像分层的实现-1猿站网-插图

使用docker history我们看到有一行很特别,就是镜像ID为的行,这一行是什么呢?

看了 官方文档 的描述,我们知道这些构建步骤要么是构建在另一个系统,要么是镜像的部分是从Docker

Hub上拉取下来的,要么是使用的是另一种构建工具BuildKit构建的。

很显然,我们这里就是第二种情况,因为我们的Dockerfile中第一句指令就是FROM alpine:3.15.0.

镜像分层

根据上面的docker history命令,我们可以轻松的画出三个镜像的分层图:

%小知识:彻底搞懂Docker镜像分层的实现-2猿站网-插图

从上面的图可以看到,我们的镜像是分层的,我们的Dockerfile中新增一条指令,就会新增一层!

如果我们将多个命令合成一个,那么也只会生成一层。修改一下上面的image_test:3.0,把两条RUN合并成一条:
FROM alpine:3.15.0 RUN dd if=/dev/zero of=file1 bs=10M count=1 && dd if=/dev/zero of=file2 bs=10M count=1

使用docker history 查看image_test:4.0,可以看到,只有2层了!

%小知识:彻底搞懂Docker镜像分层的实现-3猿站网-插图

镜像分层的好处

知道了镜像是分层的,那么我们是不是好奇为啥要这么设计呢?

试想一下我们如果不分层会有什么问题?

以拉取镜像为例!

拉取镜像的镜像很大,比如Redis的镜像有100多M

第一次我们拉取6.2版本的Redis,下载了完成的100M到本地,下次我要下载6.2.6版本的,是不是又得下载100M。

尽管可能两个版本之间就改了几行配置文件。

这样是非常低效的。如果能只下载有差异的部分就好了!

这个痛点,也就是镜像分层要解决的问题。实际上,Docker也是这么实现的。

第一次下载redis:6.2时,因为之前没有下载过,所以下载了所有的层,总共113M。网络慢点的话还是需要花一些时间的!

%小知识:彻底搞懂Docker镜像分层的实现-4猿站网-插图

第二次下载redis:7.0-rc,就变得快了很多!因为前面3层是redis:6.2是一样的,这些层已经下载过了!

%小知识:彻底搞懂Docker镜像分层的实现-5猿站网-插图

这种思想和我们常用的版本管理工具git也是一样的! 如果版本2是基于版本1的基础上,那么版本2不需要copy一份全量的数据,只需一份和版本1差异化的增量数据即可!

这样的最终好处是,可以体现在以下方面:

拉取更快:因为分层了,只需拉取本地不存在的层即可! 存储更少:因为共同的层只需存储一份即可! 运行时存储更少:容器运行时可以共享相同的层!

对于第3点,多个基于相同镜像运行的容器,都可以直接使用相同的镜像层,每个容器只需一个自己的可写层即可:

%小知识:彻底搞懂Docker镜像分层的实现-6猿站网-插图

镜像分层的实现

前面说过,Docker镜像分层和Git的版本很像!我们不妨以此类比!只是为了方便我们理解:

Git Docker 版本 分层 在前一个版本的基础上 在前一层的基础上 修改了代码 Dockerfile中加了RUN等指令 commit了修改,新增了一个版本 新建一个镜像层 新版本只包含差异 新镜像只包含了差异修改 已提交的commit是不能修改的 旧的镜像层是不能修改的

总而言之,镜像层是只读的,新的镜像层是基于前一个镜像层的修改,只保留了增量修改的部分!

使用了联合文件系统,对文件系统的修改作为一次提交来一层层的叠加!

容器本质上也是在镜像的基础上加了一层可写层!这个在另外的章节再详细讨论!

%小知识:彻底搞懂Docker镜像分层的实现-7猿站网-插图

Copy-on-write策略

Copy-on-write是一种提高文件共享和复制效率的策略。

如果一个文件和目录在低一层的镜像层中存在,并且其它层想要读取这个文件,就直接使用这个文件。

如果其它层想要修改这个文件(不管是构建镜像时,还是在容器运行的过程中),这个文件都会被先拷贝到新的一层中,然后再修改它。

%小知识:彻底搞懂Docker镜像分层的实现-8猿站网-插图

这样做的好处是可以大大减少每一层的大小!

更具体的实现请参考官方文档中:Copy-on-write策略

到此这篇关于彻底搞懂Docker镜像分层的文章就介绍到这了,更多相关Docker镜像分层内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/postnull/article/details/123031740

声明: 猿站网有关资源均来自网络搜集与网友提供,任何涉及商业盈利目的的均不得使用,否则产生的一切后果将由您自己承担! 本平台资源仅供个人学习交流、测试使用 所有内容请在下载后24小时内删除,制止非法恶意传播,不对任何下载或转载者造成的危害负任何法律责任!也请大家支持、购置正版! 。本站一律禁止以任何方式发布或转载任何违法的相关信息访客发现请向站长举报,会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。本网站的资源部分来源于网络,如有侵权烦请发送邮件至:2697268773@qq.com进行处理。
建站知识

小知识:Docker 安装Jenkins全过程及踩坑指南

2023-3-9 17:11:00

建站知识

小知识:树莓派系列之使用docker安装青龙面板和改端口号的配置问题

2023-3-9 17:25:57

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索