镜像底层存储原理

2024/03/03

镜像存储

查看镜像信息:

docker inspect nginx:latest
bash

可以发现这段数据:

{ // ...... "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/9dbe4e72af5122d82424060a7e5c584cd4e0d033ffe18d2c48195bb464ebc7d3/diff:/var/lib/docker/overlay2/e7835eb584a204c5865ea76005304d92675246c288df7bd5a93128c5a5f12328/diff:/var/lib/docker/overlay2/0fa5641bd040128a82e7438f2d798e564352a2e79d58c38676e48e64d15d39fe/diff:/var/lib/docker/overlay2/c3c620c3b71957982ff4032db14bcfe5bc96fc0c401c6d56421402e98e14bb17/diff:/var/lib/docker/overlay2/f5f4757bc9d8c8bb7245b11f625e755442603b87f46eb1fcb2556801b4921b98/diff", "MergedDir": "/var/lib/docker/overlay2/5bd32a9a280aabb65f1ea0cf24a5d075383aa768fb9d83ba9a4e2568f2effddd/merged", "UpperDir": "/var/lib/docker/overlay2/5bd32a9a280aabb65f1ea0cf24a5d075383aa768fb9d83ba9a4e2568f2effddd/diff", "WorkDir": "/var/lib/docker/overlay2/5bd32a9a280aabb65f1ea0cf24a5d075383aa768fb9d83ba9a4e2568f2effddd/work" }, }, "Name": "overlay2" } // ...... }
json

该数据指示了镜像是怎么存的:

底层目录:LowerDir

以冒号分割每个路径:

  • /var/lib/docker/overlay2/9dbe4e72af5122d82424060a7e5c584cd4e0d033ffe18d2c48195bb464ebc7d3/diff
  • /var/lib/docker/overlay2/e7835eb584a204c5865ea76005304d92675246c288df7bd5a93128c5a5f12328/diff
  • /var/lib/docker/overlay2/0fa5641bd040128a82e7438f2d798e564352a2e79d58c38676e48e64d15d39fe/diff
  • /var/lib/docker/overlay2/c3c620c3b71957982ff4032db14bcfe5bc96fc0c401c6d56421402e98e14bb17/diff
  • /var/lib/docker/overlay2/f5f4757bc9d8c8bb7245b11f625e755442603b87f46eb1fcb2556801b4921b98/diff

进入第一个目录查看:

[root@my diff]# cd /var/lib/docker/overlay2/9dbe4e72af5122d82424060a7e5c584cd4e0d033ffe18d2c48195bb464ebc7d3/diff [root@my diff]# ls docker-entrypoint.d
bash

这里没有什么特别的,继续进第二个查看:

[root@my diff]# cd /var/lib/docker/overlay2/e7835eb584a204c5865ea76005304d92675246c288df7bd5a93128c5a5f12328/diff [root@my diff]# ls docker-entrypoint.d
bash

直到第四个目录:

[root@my diff]# cd /var/lib/docker/overlay2/c3c620c3b71957982ff4032db14bcfe5bc96fc0c401c6d56421402e98e14bb17/diff [root@my diff]# ls docker-entrypoint.d etc lib tmp usr var [root@my diff]# cd etc/ [root@my etc]# ls apt default group- init.d logrotate.d passwd- rc2.d rc5.d shadow- ucf.conf ca-certificates fonts gshadow inputrc nginx rc0.d rc3.d rc6.d ssl ca-certificates.conf group gshadow- ld.so.cache passwd rc1.d rc4.d shadow systemd [root@my etc]# cd nginx/ [root@my nginx]# ls conf.d fastcgi_params mime.types modules nginx.conf scgi_params uwsgi_params
bash

可以发现nginx的软件包已经在里面了。

最后再来看最后一个文件夹:

[root@my nginx]# cd /var/lib/docker/overlay2/f5f4757bc9d8c8bb7245b11f625e755442603b87f46eb1fcb2556801b4921b98/diff [root@my diff]# ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
bash

可以发现这就是一个基础Linux系统的文件目录。


综合上面的结果,我们可以得知,LowerDir需要倒着看,并且LowerDir中的内容只记录了变化,也就是说把文件夹的内容倒着合并,就能得到我们最终的镜像。

验证

我们进入最底层的操作系统目录:

[root@my ~]# cd /var/lib/docker/overlay2/f5f4757bc9d8c8bb7245b11f625e755442603b87f46eb1fcb2556801b4921b98/diff [root@my diff]# ls -i 529307 bin 50966209 etc 16785303 lib64 530840 opt 50966638 run 50966640 sys 50968228 var 17404669 boot 50966510 home 33662264 media 16785305 proc 16785306 sbin 530842 tmp 33658094 dev 529586 lib 50966637 mnt 33662265 root 33662268 srv 17396824 usr
bash

使用ls -i可以打印出文件的 inode 值,这个值是唯一的,如果两个文件的 inode 值一样,则表示它们是同一个文件。

省略掉测试过程,实际容器启动后的 inode 值是完全一样的。

文件删除如何表现?

合并目录:MergedDir

上层目录:UpperDir

镜像中的上层目录

测试如下Dockerfile:

FROM centos:centos7 RUN echo "eeee" > /home/a.txt RUN rm -f /home/a.txt CMD /bin/bash
bash

构建后 inspect 镜像:

{ "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/moall4wjc9i62uwepefru630o/diff:/var/lib/docker/overlay2/0121c8265441df30c90ff0f2f5e35135b521e4919810cd3bd777e9f51cd0b883/diff", "MergedDir": "/var/lib/docker/overlay2/wh9e0f3n3dq3r4rgm8sllc2b2/merged", "UpperDir": "/var/lib/docker/overlay2/wh9e0f3n3dq3r4rgm8sllc2b2/diff", "WorkDir": "/var/lib/docker/overlay2/wh9e0f3n3dq3r4rgm8sllc2b2/work" }, "Name": "overlay2" }, }
json

可以发现 LowerDir 只有两层了, 最后一层是我们的 centos,进入第一层看看:

[root@my diff]# cd /var/lib/docker/overlay2/moall4wjc9i62uwepefru630o/diff [root@my diff]# ls etc home [root@my diff]# ls home/ a.txt [root@my diff]# cat home/a.txt eeee
bash

可以发现文件仍然存在,但是启动容器后文件消失,那么就可以推断,文件删除并不是在 LowerDir 中做的。


搜索其它文件夹,可以发现在上层目录中有这样的东西:

[root@my wh9e0f3n3dq3r4rgm8sllc2b2]# cd /var/lib/docker/overlay2/wh9e0f3n3dq3r4rgm8sllc2b2/diff [root@my diff]# ls etc home [root@my diff]# cd home/ [root@my home]# ls a.txt [root@my home]# ll 总用量 0 c---------. 1 root root 0, 0 3月 3 16:35 a.txt
bash

可以发现a.txt被标记为了字符设备文件来表示这个文件被删除(我猜的)。


如果有多个删除呢:

FROM centos:centos7 RUN echo "eeee" > /home/a.txt RUN rm -f /home/a.txt RUN mkdir /home/test RUN echo "eeee111" > /home/test/b.txt RUN rm -f /home/test/b.txt CMD /bin/bash
Dockerfile

inspect一下:

{ "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/x4g07210p4f4l01ak85q2s6e4/diff:/var/lib/docker/overlay2/qo1amtaglj2r0klq946qrts65/diff:/var/lib/docker/overlay2/wh9e0f3n3dq3r4rgm8sllc2b2/diff:/var/lib/docker/overlay2/moall4wjc9i62uwepefru630o/diff:/var/lib/docker/overlay2/0121c8265441df30c90ff0f2f5e35135b521e4919810cd3bd777e9f51cd0b883/diff", "MergedDir": "/var/lib/docker/overlay2/xxgrx14wij07venjjp168aod1/merged", "UpperDir": "/var/lib/docker/overlay2/xxgrx14wij07venjjp168aod1/diff", "WorkDir": "/var/lib/docker/overlay2/xxgrx14wij07venjjp168aod1/work" }, "Name": "overlay2" } }
json

可以发现a.txt已经被删除,但是b.txt仍然在最上层的 LowerDir 中,并且在 UpperDir 中,b.txt同样被标识为 字符设备文件。

容器中的上层目录

进入运行中的nginx容器,修改其配置文件:

cd /etc/nginx echo "#111" >> nginx.conf
bash

inspect容器:

{ "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/a50ef32231b6680d8ba9b0f709fba69faa40b504dfe6b1d283765d88e16e9755-init/diff:/var/lib/docker/overlay2/5bd32a9a280aabb65f1ea0cf24a5d075383aa768fb9d83ba9a4e2568f2effddd/diff:/var/lib/docker/overlay2/9dbe4e72af5122d82424060a7e5c584cd4e0d033ffe18d2c48195bb464ebc7d3/diff:/var/lib/docker/overlay2/e7835eb584a204c5865ea76005304d92675246c288df7bd5a93128c5a5f12328/diff:/var/lib/docker/overlay2/0fa5641bd040128a82e7438f2d798e564352a2e79d58c38676e48e64d15d39fe/diff:/var/lib/docker/overlay2/c3c620c3b71957982ff4032db14bcfe5bc96fc0c401c6d56421402e98e14bb17/diff:/var/lib/docker/overlay2/f5f4757bc9d8c8bb7245b11f625e755442603b87f46eb1fcb2556801b4921b98/diff", "MergedDir": "/var/lib/docker/overlay2/a50ef32231b6680d8ba9b0f709fba69faa40b504dfe6b1d283765d88e16e9755/merged", "UpperDir": "/var/lib/docker/overlay2/a50ef32231b6680d8ba9b0f709fba69faa40b504dfe6b1d283765d88e16e9755/diff", "WorkDir": "/var/lib/docker/overlay2/a50ef32231b6680d8ba9b0f709fba69faa40b504dfe6b1d283765d88e16e9755/work" }, "Name": "overlay2" }, }
json

同样查看其上层目录中的nginx.conf,可以发现我们追加的内容。

如果我们再进容器,再修改某个文件,再来看上层目录,可以发现上层目录是会跟着变的。

写时复制技术:如果在需要修改底层目录中的内容,Docker会先把对应的文件复制到UpperDir,然后再修改。如果是读,则优先读UpperDir,如果没有,则去读 LowerDir,此时并不会复制。

合并目录: MergeDir

看名字就知道,合并目录是将所有东西整合起来后的目录,所以这里也不多说啥了。