专注Java教育14年 全国咨询/投诉热线:400-8080-105
动力节点LOGO图
始于2009,口口相传的Java黄埔军校
首页 hot资讯 Docker镜像的技术原理

Docker镜像的技术原理

更新时间:2021-06-16 16:40:07 来源:动力节点 浏览1341次

docker镜像是一个只读的docker容器模板,含有启动docker容器所需的文件系统结构及其内容,因此是启动一个docker容器的基础。docker镜像的文件内容以及一些运行docker容器的配置文件组成了docker容器的静态文件系统运行环境:rootfs。可以这么理解,docker镜像是docker容器的静态视角,docker容器是docker镜像的运行状态。我们可以通过下图来理解docker daemon、docker镜像以及docker容器三者的关系

docker镜像

从上图中我们可以看到,当由ubuntu:14.04镜像启动容器时,ubuntu:14.04镜像的镜像层内容将作为容器的rootfs;而ubuntu:14.04镜像的json文件,会由docker daemon解析,并提取出其中的容器执行入口CMD信息,以及容器进程的环境变量ENV信息,最终初始化容器进程。当然,容器进程的执行入口来源于镜像提供的rootfs。

rootfs

rootfs是docker容器在启动时内部进程可见的文件系统,即docker容器的根目录。rootfs通常包含一个操作系统运行所需的文件系统,例如可能包含典型的类Unix操作系统中的目录系统,如/dev、/proc、/bin、/etc、/lib、/usr、/tmp及运行docker容器所需的配置文件、工具等。在传统的Linux操作系统内核启动时,首先挂载一个只读的rootfs,当系统检测其完整性之后,再将其切换为读写模式。而在docker架构中,当docker daemon为docker容器挂载rootfs时,沿用了Linux内核启动时的做法,即将rootfs设为只读模式。在挂载完毕之后,利用联合挂载(union mount)技术在已有的只读rootfs上再挂载一个读写层。这样,可读写的层处于docker容器文件系统的最顶层,其下可能联合挂载了多个只读的层,只有在docker容器运行过程中文件系统发生变化时,才会把变化的文件内容写到可读写层,并隐藏只读层中的旧版本文件。

Docker镜像的主要特点

为了更好的理解docker镜像的结构,下面介绍一下docker镜像设计上的关键技术。

分层docker镜像是采用分层的方式构建的,每个镜像都由一系列的"镜像层"组成。分层结构是docker镜像如此轻量的重要原因。当需要修改容器镜像内的某个文件时,只对处于最上方的读写层进行变动,不覆写下层已有文件系统的内容,已有文件在只读层中的原始版本仍然存在,但会被读写层中的新版本所隐藏。当使用docker commit提交这个修改过的容器文件系统为一个新的镜像时,保存的内容仅为最上层读写文件系统中被更新过的文件。分层达到了在不的容器同镜像之间共享镜像层的效果。

写时复制docker镜像使用了写时复制(copy-on-write)的策略,在多个容器之间共享镜像,每个容器在启动的时候并不需要单独复制一份镜像文件,而是将所有镜像层以只读的方式挂载到一个挂载点,再在上面覆盖一个可读写的容器层。在未更改文件内容时,所有容器共享同一份数据,只有在docker容器运行过程中文件系统发生变化时,才会把变化的文件内容写到可读写层,并隐藏只读层中的老版本文件。写时复制配合分层机制减少了镜像对磁盘空间的占用和容器启动时间。

内容寻址在docker 1.10版本后,docker镜像改动较大,其中最重要的特性便是引入了内容寻址存储(content-addressable storage)的机制,根据文件的内容来索引镜像和镜像层。与之前版本对每个镜像层随机生成一个UUID不同,新模型对镜像层的内容计算校验和,生成一个内容哈希值,并以此哈希值代替之前的UUID作为镜像层的唯一标识。该机制主要提高了镜像的安全性,并在pull、push、load和save操作后检测数据的完整性。另外,基于内容哈希来索引镜像层,在一定程度上减少了ID的冲突并且增强了镜像层的共享。对于来自不同构建的镜像层,主要拥有相同的内容哈希,也能被不同的镜像共享。

联合挂载通俗地讲,联合挂载技术可以在一个挂载点同时挂载多个文件系统,将挂载点的原目录与被挂载内容进行整合,使得最终可见的文件系统将会包含整合之后的各层的文件和目录。实现这种联合挂载技术的文件系统通常被称为联合文件系统(union filesystem)。以下图所示的运行Ubuntu:14.04镜像后的容器中的aufs文件系统为例:

docker镜像

由于初始挂载时读写层为空,所以从用户的角度看,该容器的文件系统与底层的rootfs没有差别;然而从内核的角度看,则是显式区分开来的两个层次。当需要修改镜像内的某个文件时,只对处于最上方的读写层进行了变动,不复写下层已有文件系统的内容,已有文件在只读层中的原始版本仍然存在,但会被读写层中的新版本文件所隐藏,当docker commit这个修改过的容器文件系统为一个新的镜像时,保存的内容仅为最上层读写文件系统中被更新过的文件。联合挂载是用于将多个镜像层的文件系统挂载到一个挂载点来实现一个统一文件系统视图的途径,是下层存储驱动(aufs、overlay等)实现分层合并的方式。所以严格来说,联合挂载并不是docker镜像的必需技术,比如在使用device mapper存储驱动时,其实是使用了快照技术来达到分层的效果。

Docker镜像的存储组织方式

综合考虑镜像的层级结构,以及volume、init-layer、可读写层这些概念,一个完整的、在运行的容器的所有文件系统结构可以用下图来描述:

docker镜像

  从图中我们不难看到,除了 echo hello 进程所在的 cgroups 和 namespace 环境之外,容器文件系统其实是一个相对独立的组织。可读写部分(read-write layer 以及 volumes)、init-layer、只读层(read-only layer) 这 3 部分结构共同组成了一个容器所需的下层文件系统,它们通过联合挂载的方式巧妙地表现为一层,使得容器进程对这些层的存在一无所知。

以上就是动力节点小编介绍的"Docker镜像的技术原理",希望对大家有帮助,如有疑问,请在线咨询,有专业老师随时为您服务。

提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>