docker 镜像管理

什么是docker镜像

Docker镜像是一个只读的Docker容器模板,含有启动Docker容器所需的文件系统结构及其内容,因此是启动一个Docker容器的基础。Docker镜像的文件内容以及一些运行Docker容器的配置文件组成了Docker容器的静态文件系统运行环境一rootfs。可以这么理解,Docker镜像是Docker容器的静态视角,Docker容器是Docker像的运行状态。

  1. rootfs

    ​ rootfs是Docker容器在启动时内部进程可见的文件系统,即Docker容器的根目录。rootfs通常包含一个操作系统运行所需的文件系统,例如可能包含典型的类Unix操作系统中的目录系统,如/dev, /proc, /bin, /etc, /lib, /usr, /tmp及运行Docke溶器所需的配置文件、工具等。

    ​ 在Docker架构中,当Docker daemon为Docker容器挂载rootfs时,沿用了Linux内核启动时的方法,即将rootfs设为只读模式。在挂载完毕之后,利用联合挂载(union mount )技术在已有的只读rootfs上再挂载一个读写层。这样,可读写层处于Docker容器文件系统的最顶层,其下可能联合挂载多个只读层,只有在Docker容器运行过程中文件系统发生变化时,才会把变化的文件内容写到可读写层,并隐藏只读层中的老版本文件。

  2. Docker镜像的主要特点

    • 分层

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

    • 写时复制

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

    • 内容寻址

      ​ 内容寻址存储( content-addressable storage)的机制,根据文件内容来索引镜像和镜像层。docker对镜像层的内容计算校验和,生成一个内容哈希值,并以此哈希值代替之前的UUID作为镜像层的唯一标志。该机制主要提高了镜像的安全性,并在pull, push, load和save操作后检测数据的完整性。另外,基于内容哈希来索引镜像层,在一定程度上减少了ID的冲突并且增强了镜像层的共享。对于来自不同构建的镜像层,只要拥有相同的内容哈希,也能被不同的镜像共享。

    • 联合挂载

      ​ 通俗地讲,联合挂载技术可以在一个挂载点同时挂载多个文件系统,将挂载点的原目录与被挂载内容进行整合,使得最终可见的文件系统将会包含整合之后的各层的文件和目录。实现这种联合挂载技术的文件系统通常被称为联合文件系统(union filesystem )。如图3-11所示,以运行Ubuntu:14.04镜像后容器中的aufs文件系统为例。由于初始挂载时读写层为空,所以从用户的角度看,该容器的文件系统与底层的rootfs没有差别;然而从内核的角度来看,则是显式区分开来的两个层次。当需要修改镜像内的某个文件时,只对处于最上方的读写层进行了变动,不覆写下层已有文件系统的内容,已有文件在只读层中的原始版本仍然存在,但会被读写层中的新版文件所隐藏,当docker commit这个修改过的容器文件系统为一个新的镜像时,保存的内容仅为最上层读写文件系统中被更新过的文件。

    docker 镜像管理_第1张图片

    • Docker镜像存储组织方式

      ​ 综合考虑镜像的层级结构,以及volume, init-layer、可读写层这些概念,一个完整的、在运行的容器的所有文件系统结构可以用图3-12来描述。从图中我们不难看到,除了echo hello进程所在的cgroups和namespace环境之外,容器文件系统其实是一个相对独立的组织。可读写部分( read-write layer以及volumes)、init-layer、只读层(read-only layer)这3部分结构共同组成了一个容器所需的下层文件系统,它们通过联合挂载的方式巧妙地表现为一层,使得容器进程对这些层的存在一点都不知道。

      docker 镜像管理_第2张图片

Docker镜像关键概念

  1. registry

    ​ registry用以保存Docker镜像,其中还包括镜像层次结构和关于镜像的元数据。可以将registry简单地想象成类似于Git仓库之类的实体。

  2. repository

    ​ repository即由具有某个功能的Docker镜像的所有迭代版本构成的镜像组。一言以蔽之,registry是repository的集合,repository是镜像的集合。

  3. manifest

    ​ manifest(描述文件)主要存在于registry中作为Docker镜像的元数据文件,在pull, push,save和load中作为镜像结构和基础信息的描述文件。在镜像被pull或者load到Docker宿主机时,manifest被转化为本地的镜像配置文件config。

  4. image和layer

    ​ Docker内部的image概念是用来存储一组镜像相关的元数据信息,主要包括镜像的架构(如amd64)、镜像默认配置信息、构建镜像的容器配置信息、包含所有镜像层信息的rootfs. Docker利用rootfs中的diff_id计算出内容寻址的索引(chainID)来获取layer相关信息,进而获取每一个镜像层的文件内容。
    ​ layer(镜像层)是一个Docker用来管理镜像层的中间概念,前面提到镜像是由镜像层组成的,而单个镜像层可能被多个镜像共享,所以Docker:将layer与image的概念分离。Docker像管理中的layer主要存放了镜像层的diff_id, size, cache-id和parent等内容,实际的文件内容则是由存储驱动来管理,并可以通过cache-id在本地索引到。

  5. Dockerfile

    ​ Dockerfile是在通过docker build命令构建自己的Docker像时需要使用到的定义文件。它允许用
    户使用基本的DSL语法来定义Docker镜像,每一条指令描述了构建镜像的步骤。

Docker镜像构建操作

​ Docker提供了比较简单的方式来构建镜像或者更新现有的镜像—docker build和docker commit。不同的是,docker commit是将容器提交为一个镜像,也就是从容器更新或者构建镜像;而docker build是在一个镜像的基础上构建镜像。

  1. commit镜像

    ​ docker commit命令只提交容器镜像发生变更了的部分,即修改后的容器镜像与当前仓库中对应镜像之间的差异部分,这使得该操作实际需要提交的文件往往并不多。
    ​ Docker daemon接收到对应的HTTP请求后,需要执行的步骤如下。
    ​ (1)根据用户输人pause参数的设置确定是否暂停该Docker容器的运行。
    ​ (2)将容器的可读写层导出打包,该读写层代表了当前运行容器的文件系统与当初启动该容器的镜像之间的差异。
    ​ (3)在层存储(layerStore)中注册可读写层差异包。
    ​ (4)更新镜像历史信息和rootfs,并据此在镜像存储(imageStore)中创建一个新的镜像,记录其元数据。
    ​ (5)如果指定了repository信息,则给上述镜像添加tag信息。

  2. build镜像

    ​ 一般来说,用户主要使用Dockerfile和docker build命令来完成一个新镜像的构建。这条命令的格式如下:

    docker build [OPTIONS] PATH | URL | -
    

    其中PAT日或URL所指向的文件称为context(上下文),context包含build Docker镜像过程中需要的
    Dockerfile以及其他的资源文件。

    下面介绍该命令的执行流程:

    • 当Docker client接收到用户命令,首先解析命令行参数。根据第一个参数的不同,将分为不同情况分别处理。完成了相关信息的设置之后,Docker client向Docker serve泼送POS下/build的HTTP请求,包含了所需的context信息。

    • Docker server接收到相应的HTTP请求后,需要做的工作如下:

      (1)创建一个临时目录,并将context指定的文件系统解压到该目录下。

      (2)读取并解析Dockerfile 。

      (3)根据解析出的Dockerfile遍历其中的所有指令,并分发到不同的模块去执行。Dockerfile每条指令的格式均为INSTRUCTION arguments, INSTRUCTION是一些特定的关键词,包括FROM ,RUN, USER等,都会映射到不同的parser进行处理。

      (4) parser为上述每一个指令创建一个对应的临时容器,在临时容器中执行当前指令,然后通过commit使用此容器生成一个镜像层。

      (5) Dockerfile中所有的指令对应的层的集合,就是此次build的结果。如果指定了tag参数,便给镜像打上对应的tag。最后一次。ommit生成的镜像ID就会作为最终的镜像ID返回。

    Docker镜像的分发方法

    ​ Docker术兴起的原动力之一,是在不同的机器上创造无差别的应用运行环境。因此,能够方便地实现“在某台机器上导出一个Docke溶器并且在另外一台机器上导人”这一操作,就显得非常必要。docker exportdocker import命令实现了这一功能。当然,由于Docker容器与镜像的天然联系性,容器迁移的操作也可以通过镜像分发的方式达成,这里可以用到的方法是docker pushdocker pull,或者docker savedocker load命令进行镜像的分发,不同的是docker push通过线上Docker Hub的方式迁移,而docker save则是通过线下包分发的方式迁移。

你可能感兴趣的:(docker)