Docker的graph driver主要用于管理和维护镜像,包括把镜像从仓库下载下来,到运行时把镜像挂载起来可以被容器访问等,都是graph driver做的。涉及的docker命令有
- Docker pull
- Docker push
- Docker import
- Docker export
- Docker load
- Docker save
- Docker build
Docker的graph driver做的事情,基本上是对docker的image定义的实现,而OCI的image-spec是基于docker的image spec的,所以理论dockergraph driver做的事情,都应该被image-spec的实现所覆盖。
目前docker支持的graph driver有:
- Overlay
- Aufs
- Devicemapper
- Btrfs
- Zfs
- Vfs
这些driver各有优劣,以后会慢慢补充对比。
Overlay driver介绍
Graph driver中最复杂的部门就是梳理清楚在构建各种镜像层关系时涉及的各种id以及他们的组织关系。特别是docker镜像在支持content addressable之后,镜像的id组织关系变得异常复杂。Overlay driver中,对镜像的id会涉及到多种类型:
- 对一个镜像生成的唯一id值,下文称为image-id
- 运行容器时把镜像挂载起来后生成的一个唯一id值,下文称为mount-id #docke ps查看到的id
- 对每一个镜像层生成的一个唯一的随机id,下文中称为cache-id
- 根据每一个镜像层的内容单独生成的content addressable id,下文称为layer-id
- 根据每一层镜像,以及该镜像的所有底层镜像的内容生成的content addressable id,下文称为chain-id
对于一个镜像来说,其最底层的layer,其layer-id和chain-id是一样的。
Layer-id和chain-id的存在比较好理解,其中chain-id的概念也是image-spec中定义必须有的,cache-id的存在感觉必要性不大,目前不清楚为什么还要搞一个cache-id,猜测可能是为了向前兼容的原因。Image-id,mount-id和cache-id都是随机生成的,可以合起来跟content addressable id对比理解。
关键目录介绍
/var/lib/docker/overlay
这里存放的是镜像的每一层layer解压后的结果,以及基于每一个镜像生成容器后,对镜像合并挂载后的目录和对应的init目录。这里的id就是cache-id和mount-id.
对于容器的挂载目录,比如对某个容器docker inspect后查询到的“GraphDriver”中的挂载目录,里面又分几个子目录:
- /var/lib/docker/overlay/<id>/merged 这是所有镜像层合并后的结果,就是容器中进程看到的结果
- /var/lib/docker/overlay/<id>/upper 这是上面的只读层
- /var/lib/docker/overlay/<id>/work 这是overlay生成用来做cow相关操作的
- /var/lib/docker/overlay/<id>/lower-id 这里存放了镜像层中最上层的cache-id
/var/lib/docker/image/overlay/imaged/content/sha256
这里存放的是每一个镜像的配置信息,其id都是image-id,里面是json格式,其内容跟docker inspect查看镜像的信息是一样的,这里面会涉及到一些id:
[root@localhostimagedb]# docker inspectc5f1cf30c96b5b55c0e6385f2ecb791790eacfdc874500ec3dd865789e358dd1
[
{
"Id":"sha256:c5f1cf30c96b5b55c0e6385f2ecb791790eacfdc874500ec3dd865789e358dd1",
…
"Container":"47dc052ce6dc564733ec57eb0133693731d20383f1db037c578f6330a78ca643",
"ContainerConfig": {
…
"Image":"cdc870605343a807ec3bb9da56f84249c846b5ba7dba18bb226a4af9f5e1451a",
…
},
…
"Config": {
…
"Image":"cdc870605343a807ec3bb9da56f84249c846b5ba7dba18bb226a4af9f5e1451a",
…
},
…
"GraphDriver": {
"Name":"overlay",
"Data": {
"RootDir":"/var/lib/docker/overlay/9aaabd8f23f938937269563a119307e4f8859e8cabc274c63568e3e473cd2d0b/root"
}
},
"RootFS": {
"Type":"layers",
"Layers": [
"sha256:7aae4540b42d10456f8fdc316317b7e0cf3194ba743d69f82e1e8b10198be63c",
"sha256:3ce512daaf78307e3a2c5adef7741d9ce9d61449a9a642cafd9f474a50e8c5d0",
"sha256:7f18b442972bf737eadfff445088375d38f0f455f25ea94277b70050de3ae4b1",
"sha256:a3b5c80a4ebaf7a0a1b92219b3dfb7109a14b38bebd6b55870a3aec90743a263",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
]
}
}
]
对以上id的解释:
- Id:镜像的image-id
- Container:创建该镜像的容器id
- ContainerConfig.Image:创建该镜像的容器基于的镜像的image-id
- Config.Image:同ContainerConfig.Image
- GraphDriver.Data.RootDir:路径中的id为该镜像最上面的layer的cache-id
- RootFS.Layers:该镜像的所有层的id,这里的id是layer-id
/var/lib/docker/image/overlay/layerdb/mounts
这里记录的是当前存在的容器的id,里面的文件:
- mount-id:就是容器运行起来后的挂载mount-id
- init-id:跟mount-id一样,只是后面加了-init,这个层的存在是为了解决一些容器的其他目录的挂载问题,具体原因待分析
- parent:这里存放的就是容器使用的镜像层的最上层layer的id,是一个chain-id,跟/var/lib/docker/overlay/<mount-id>/lower-id里指的是同一个,但lower-id里的是cache-id。
/var/lib/docker/image/overlay/layerdb/sha256
该目录下存放的就是容器镜像的每一层的源数据了,这里的id都是chain-id,每个文件夹下面就是该镜像层中的一些信息:
- Cache-id:对应的是/var/lib/docker/overlay目录下的cache-id
- Diff:对应是这个chain中,最上层的layer的id,也就是为这个chain附加上去的层,是一个layer-id
- Parent:就是这个chain除去最上层的layer后,上一个chain的chain-id
- Size:是这个chain附加上去的层的大小
- Tar-split.json.gz:是对这个chain附加上去的layer的源数据解压后重新通过tar-split的方式压缩后的数据包,对于为什么需要tar-split重新压缩,原因可以参考image-spec中的描述。