位置: 首页 > 资讯 > > 正文

镜像的在节点上的存储结构是怎么样的?

2023-07-10 20:38:36 来源:博客园
每日一问系列镜像的在节点上的存储结构是怎么样的?

我们经常会使用 docker 或者其他 cri 工具拉取镜像来运行容器,却没有去实际了解 pull 下来的镜像在机器上是怎么存储的。以下以常用的 overlay2 存储驱动为例,解析镜像的存储结构,其他存储驱动也是类似


【资料图】

编写如下 Dockerfile 文件

FROMubuntu:latestENVauthorjlzRUNecho"x1">>/tmp/testRUNecho"x2">>/tmp/test2RUNecho"x3">>/tmp/test3ENTRYPOINT["/bin/bash","-c","sh"]

通过 docker build 命令构建一个镜像

dockerbuild-tmy-ubuntu:0,1.
镜像存储目录结构

在 overlay2 存储驱动中,镜像层之间的关系可以通过 LowerDir、UpperDir、MergedDir 目录结构表示 对应上面 inspect 出来的镜像 GraphDriver 字段

通过 docker inspect {image id} 命令查看镜像信息,如下

"Config":{"Env":["author=jlz"],"Entrypoint":["/bin/bash","-c","cat/tmp/test"]},"GraphDriver":{"Data":{"LowerDir":"/mnt/datadisk0/docker/overlay2/dff0bddcffaaa428ea232b202275d48845c11783ea428e9cfa335987cf91805c/diff:/mnt/datadisk0/docker/overlay2/3b5766ed7c43b9417311635ec98d844a98586b9854538975bc4ef12d22edfe1c/diff:/mnt/datadisk0/docker/overlay2/51798d33e8f37ed44c79b7ed5626e95936dd60b8269328557bb6d09f3e353356/diff","MergedDir":"/mnt/datadisk0/docker/overlay2/492b8eb5dba9dbb4c72616fe0f8e9423a552d42e5ffe017cbd2e2fb60b3e20a7/merged","UpperDir":"/mnt/datadisk0/docker/overlay2/492b8eb5dba9dbb4c72616fe0f8e9423a552d42e5ffe017cbd2e2fb60b3e20a7/diff","WorkDir":"/mnt/datadisk0/docker/overlay2/492b8eb5dba9dbb4c72616fe0f8e9423a552d42e5ffe017cbd2e2fb60b3e20a7/work"},"Name":"overlay2"},"RootFS":{"Type":"layers","Layers":["sha256:cdd7c73923174e45ea648d66996665c288e1b17a0f45efdbeca860f6dafdf731","sha256:120009c8f50a6cc9022bf7b9fcc7b4f7ef5ba8ea3736dfe974e11780d1a840a0","sha256:b6f2b52c36d89acd2e8ce8d85c178c722501dad0ee64de2aa4d15ac18c1cf0fc","sha256:7949cc4bef953bb279a2b9b3c27def2a9399706bb1344461299ac4c01c4692df"]},

如上 RootFS.Layers 表示这个镜像只有 4 层,因为上面的 Dockerfile 中 base 镜像 ubuntu 本身只有一层,RUN 指令分别对应一层,而 ENV 和 ENTRYPOINT 由于没有涉及到文件系统修改,所以不会有对应的镜像层,他们直接存在于镜像的元数据信息中,如上面的 Config.Env 和 Config.Entrypoint

UpperDir:最新的一层镜像层的变更信息(第 n 层),这里对应为 第 4 层,即 RUN echo "x3" >> /tmp/test3

LowerDir: 除最新镜像层的所有层(第 1 ~n-1 层),格式为 {n-1}:{n-2}...{1}

MergedDir:LowerDir 和 UpperDir 的合并,形成最终的镜像的 rootfs 结构

容器存储目录结构

通过这个镜像创建一个容器

dockerrun-it--entrypointsh{imageid}

注意这里的 --entrypoint 参数用于修改容器的 entrypoint

在容器中执行命令 echo "hahaha" test4 创建新文件,并通过 docker inspect {container_id} 查看容器存储结构

"Config":{"Entrypoint":["sh"]},"GraphDriver":{"Data":{"LowerDir":"/mnt/datadisk0/docker/overlay2/f2a196d05ccbae06927091297ea503ce59ddf6bc01b8edd686358ca9a41b9abd-init/diff:/mnt/datadisk0/docker/overlay2/492b8eb5dba9dbb4c72616fe0f8e9423a552d42e5ffe017cbd2e2fb60b3e20a7/diff:/mnt/datadisk0/docker/overlay2/dff0bddcffaaa428ea232b202275d48845c11783ea428e9cfa335987cf91805c/diff:/mnt/datadisk0/docker/overlay2/3b5766ed7c43b9417311635ec98d844a98586b9854538975bc4ef12d22edfe1c/diff:/mnt/datadisk0/docker/overlay2/51798d33e8f37ed44c79b7ed5626e95936dd60b8269328557bb6d09f3e353356/diff","MergedDir":"/mnt/datadisk0/docker/overlay2/f2a196d05ccbae06927091297ea503ce59ddf6bc01b8edd686358ca9a41b9abd/merged","UpperDir":"/mnt/datadisk0/docker/overlay2/f2a196d05ccbae06927091297ea503ce59ddf6bc01b8edd686358ca9a41b9abd/diff","WorkDir":"/mnt/datadisk0/docker/overlay2/f2a196d05ccbae06927091297ea503ce59ddf6bc01b8edd686358ca9a41b9abd/work"},"Name":"overlay2"},

可以看到 Config.Entrypoint 被修改为 sh,此时 GraphDriver 中的目录相比 inspect 镜像的结果也发生了变化

UpperDir:这个目录包含了容器的可写层,可以看到在容器中创建的 test4 文件。这个目录中的文件可以被修改,但是它们只存在于容器的生命周期中。

LowerDir:这个目录包含了镜像的只读层,也就是镜像的文件系统。结合上面镜像的存储结构可以发现,这里包含了所有的 n 层镜像目录。这些文件是只读的,不能被修改

WorkDir:这个目录是 overlay2 文件系统的工作目录,也就是容器内部的工作目录。当你在容器中运行一个命令时,Docker会将该命令的工作目录设置为WorkDir指定的目录。

MergedDir:LowerDir 和 UpperDir 的合并结果,也就是镜像只读层和容器可写层的合并结果。

init 层的作用

如果细心的话可以发现 inpect 容器的结果中, LowerDir 除了所有的镜像只读层外,还有一个 init 层

“init”结尾的层,夹在只读层和读写层之间。Init 层是 Docker 项目单独生成的一个内部层,专门用来存放 /etc/hosts、/etc/resolv.conf 等信息

需要这样一层的原因是,用户往往需要在启动容器时写入一些指定的值比如在/etc/hosts中写入hostname,所以就需要在可读写层对它们进行修改。可是,这些修改往往只对当前的容器有效,我们并不希望执行 docker commit 时,把这些信息连同可读写层一起提交掉。

所以,Docker 做法是,在修改了这些文件之后,以一个单独的层挂载了出来。而用户执行 docker commit 只会提交可读写层,所以是不包含这些内容的。

标签: