Docker镜像的内部机制
镜像就是一个打包文件,里面包含了应用程序还有它运行所依赖的环境,例如文件系统、环境变量、配置参数等等。
环境变量、配置参数这些东西还是比较简单的,随便用一个 manifest
清单就可以管理,真正麻烦的是文件系统。为了保证容器运行环境的一致性,镜像必须把应用程序所在操作系统的根目录,也就是 rootfs
,都包含进来。
虽然这些文件里不包含系统内核(因为容器共享了宿主机的内核),但如果每个镜像都重复做这样的打包操作,仍然会导致大量的冗余。可以想象,如果有一千个镜像,都基于 Ubuntu
系统打包,那么这些镜像里就会重复一千次 Ubuntu
根目录,对磁盘存储、网络传输都是很大的浪费。
很自然的,我们就会想到,应该把重复的部分抽取出来,只存放一份 Ubuntu
根目录文件,然后让这一千个镜像以某种方式共享这部分数据。
这个思路,也正是容器镜像的一个重大创新点:分层,术语叫“Layer”。
容器镜像内部并不是一个平坦的结构,而是由许多的镜像层组成的,每层都是只读不可修改的一组文件,相同的层可以在镜像之间共享,然后多个层像搭积木一样堆叠起来,再使用一种叫“Union FS 联合文件系统”的技术把它们合并在一起,就形成了容器最终看到的文件系统。
命令 docker inspect
来查看镜像的分层信息,比如 nginx:alpine
镜像:
docker inspect nginx:alpine
它的分层信息在“RootFS”部分:
通过这张截图就可以看到,nginx:alpine 镜像里一共有 6 个 Layer
。
现在你也就明白,之前在使用 docker pull
、docker rmi
等命令操作镜像的时候,那些“奇怪”的输出信息是什么了,其实就是镜像里的各个 Layer
。Docker 会检查是否有重复的层,如果本地已经存在就不会重复下载,如果层被其他镜像共享就不会删除,这样就可以节约磁盘和网络成本。