Overlay2

Overlay驱动只工作在一个lower OverlayFS层之上,因此需要硬链接来实现多层镜像,但Overlay2驱动原生地支持多层lower OverlayFS镜像(最多128层)。 因此overlay2驱动在合层相关的命令(如build和commit)中提供了更好的性能,与overlay驱动对比,消耗更少的inode。

为了能够更清楚地看到 overlay2 中镜像和容器的磁盘结构,我用一台全新的机器。

查看 /var/lib/docker/overlay2 目录



[root@docker ~]# ls /var/lib/docker/overlay2/
l



接下来我 pull 一个镜像下来,再查看此目录的变化



[root@docker ~]# docker pull alpine
[root@docker ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
alpine              latest              a187dde48cd2        2 days ago          5.6MB

# 查看/var/lib/docker/overlay2
[root@docker ~]# ls /var/lib/docker/overlay2/
afa26158b08d9eb55ba28b32b0e7bc5deeac34e2f21512cfc2adac97549b4d73  l



可以看到,多了个afa2615...目录,我们进一步查看此目录中的内容



[root@docker ~]# cd /var/lib/docker/overlay2/
[root@docker overlay2]# ls afa26158b08d9eb55ba28b32b0e7bc5deeac34e2f21512cfc2adac97549b4d73/diff/
bin  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var



发现在这个目录下的 diff 目录中,有着一个完整文件系统环境。这便是这个镜像的内容了。

Docker容器

接下来我们使用此镜像运行一个容器



[root@docker overlay2]# docker run -itd --name alpine alpine



再查看镜像目录



[root@docker overlay2]# ls
afa26158b08d9eb55ba28b32b0e7bc5deeac34e2f21512cfc2adac97549b4d73  f407007886f381e9c99676ab651c411876f2ee4048f70d3b677003919fa01302-init
f407007886f381e9c99676ab651c411876f2ee4048f70d3b677003919fa01302  l



发现,在原来的基础上多了 f40700788...1302f40700788...1302-init 目录。

我们进一步查看容器信息



[root@docker overlay2]# docker inspect alpine
...
 "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/f407007886f381e9c99676ab651c411876f2ee4048f70d3b677003919fa01302-init/diff:/var/lib/docker/overlay2/afa26158b08d9eb55ba28b32b0e7bc5deeac34e2f21512cfc2adac97549b4d73/diff",
                "MergedDir": "/var/lib/docker/overlay2/f407007886f381e9c99676ab651c411876f2ee4048f70d3b677003919fa01302/merged",
                "UpperDir": "/var/lib/docker/overlay2/f407007886f381e9c99676ab651c411876f2ee4048f70d3b677003919fa01302/diff",
                "WorkDir": "/var/lib/docker/overlay2/f407007886f381e9c99676ab651c411876f2ee4048f70d3b677003919fa01302/work"
            },
            "Name": "overlay2"
        },
...



注意观察:

  • LowerDir由 f40700788...1302-init/diffafa26158b...b4d73/diff 组成
  • UpperDir为 f40700788...1302/diff 目录
  • 对外统一展示目录为 f40700788...1302/merged

实际上,我把它们画出来,如下图:




Containerd overlay containerd overlay2_docker 容器保存为镜像


lower 层

这个容器的 rootfs 最下面的层,可能由一到多层组成,挂载方式是只读的。它们以增量的形式分别包含了操作系统及应用程序的一部分内容。

upper 层

产生于运行容器时,它是这个容器的 rootfs 最上面的一层,挂载方式为读写。在没有写入文件之前,这个目录是空的。一旦在容器里做了写操作,你修改产生的内容就会以增量的方式保存在这个层中。

我们可以使用 docker commit 和 export 指令,保存这个被修改过的可读写层,供其他人使用;同时,原先的只读层里的内容则不会有任何变化。保存后这一层将变成新镜像 lower 层的增量层。

init 层

产生于运行容器时,这是一个以"-init”"尾的层,夹在只读层和读写层之间。Init 层是 Docker 项目专门用来存放 /etc/hosts、/etc/resolv.conf 等信息。 需要这样一层的原因是,这些文件本来属于只读镜像的一部分,但是用户往往需要在启动容器时传入一些指定的值(比如 主机名),所以就需要在可读写层对它们进行修改。 可是,这些修改往往只是临时的,我们并不希望执行 docker commit 时把这些信息也保存起来。

简单来说,这层中的内容是在运行容器时传入的临时修改,这些修改不会被commit这样的命令保存下来。

理解了以上这些层后 Docker 的镜像与容器原理就理解了。 我画了个总结图,方便大家理解镜像与容器之间的关系:


Containerd overlay containerd overlay2_Containerd overlay_02


注: 在基础 image 中,可能没有 upper 这层。