背景

正在看一篇论文,名为《Efficient Service Handoff Across Edge Servers via Docker Container Migration》,里面介绍了如何利用docker快速迁移应用,对于一些低延迟的场景比较有用,里面有关于docker存储的方面,所以就有了本篇文章


一.Docker的老存储引擎AUFS

1.1 什么是AUFS

AUFS是一种 Union File System,全称是Advanced Union File System,主要是把不同物理位置的目录合并mount到同一个目录下

1.2 AUFS的读写操作

将多个目录mount成一个后,读写操作如下:

  • 默认情况下,最上层的目录是读写层,下面可以有一个或者多个只读层
  • 读文件时,打开文档使用O_RDONLY选项,从最上面一个文件开始逐层往下找,打开第一个找到的文件,并读取其中的内容
  • 写文件时,打开文件使用O_WRONLY和O_RDWR选项
    • 如果在最上层找到文件,直接打开文件
    • 如果是在只读层中找到,则将该文件复制到最上层,然后在最上层打开这个copy的文件(如果读写的文件比较大,这个过程耗费的时间就会比较久)
  • 删除文件,在最上层创建一个whiteout文件,就是在原来文件名字前面加上.wh

二、Docker使用的存储引擎

2.1 Docker 默认的存储引擎

AUFS是及更早版本的首选存储驱动程序,之后的docker版本默认存储引擎是Overlay2。需要注意的是:不同的存储引擎是需要文件系统支持的!

2.2 Docker层的实现

构建docker镜像时一般使用dockerfile的方式(也可以在容器里面修改,然后commit成新的镜像,前者可以看到具体的实现,后者除了作者,没人知道如何实现的,所以一般使用前者的方式),而docker的每行命令,都会生成一个层,即使没做什么事。在写dockerfile时,要避免生成很多层,因为使用存储引擎在进行文件查找时,会遍历层,会消耗性能

docker的镜像层和容器层如图所示:

Image layers是base image层,该层是不可改动的(例子中是ubuntu:镜像),是静态的(理解为是不会发生变化的);

当我们基于image layer运行容器时(比如基于ubuntu:镜像运行容器),Docker会创建一个读写层,也就是图中的Container layer,该读写层位于所有层的最上面。

我们对容器的更改操作都会在该读写层中,当我们执行 docker commit 命令去保存我们的改动,生成新的镜像时,就相当于给该读写层copy了一下,放到了Image layers中了

2.3 Docker实现文件的增删改

2.3.1 copy-on-write写时复制策略

我们首先需要了解Docker中使用的copy-on-write策略,该策略不是docker设计出的,而是最初沿用AUFS存储引擎的策略,该部分的内容见Docker的老存储引擎AUFS

2.3.2 allocate-on-demand用时分配

用时分配是应用在原本没有该文件的场景,只有在新写入一个文件时才分配空间,这样可以提高存储资源的利用率。比如:启动一个容器时,并不会为这个容器预分配一些磁盘空间,只有当新文件写入时,才按需分配新空间

如此设计的好处

  • 最显而易见的好处是减少了存储空间;镜像被分成了很多静态可读层,而这些层是可以共享的;当基于本地一个已有的镜像去构建一个新的镜像时,只需要添加新的层,原有的层可以直接使用
  • 启动容器变得轻量快速,因为启动容器时,只是在已有的层上添加了一层读写层,其他层都是用到的时候才会去搜索,遵循copy-on-write原则

三. Docker存储引擎之Overlay2详细过程

本节内容是我想写这篇文章的主要原因,前面介绍AUFS主要是因为该论文是2017年SEC会议收集的,那时候docker还是使用的AUFS引擎,现在docker已经默认使用Overlay2了,但是对于核心的思想,都是一样的。下面这一部分会以一个具体的例子来介绍docker存储中的一些id的作用,以及image是如何和layer映射到一起的。实验步骤如下:

1.拉取一个镜像,并基于镜像启动一个容器,这里以jenkins/jenkins为例子(选择该镜像是因为之后要利用jenkins跑CI),大家做测试可以选择busybox

2.查看容器的ID,是为了去找到对应的layer层

root@VM-0-3-ubuntu:/home/ubuntu# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
39274f2825c4        jenkins/jenkins     "/sbin/tini -- /usr/…"   23 hours ago        Up 23 hours         0.0.0.0:8080->8080/tcp, 50000/tcp   test

可以看到容器的ID是39274f2825c4

3.查看镜像ID,是为了该镜像的元数据,即该镜像包含哪些layer层

root@VM-0-3-ubuntu:/home/ubuntu# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
jenkins/jenkins     latest              13e2b551515d        3 days ago          717MB

我们可以看到镜像的ID是13e2b551515d

4.到 /var/lib/docker/image/overlay2/imagedb/content/sha256 目录下找到对应镜像文件的元数据(这里是用Step3找到的镜像ID),cat该文件,并找到rootfs部分的diff_ids

cat ${Image_Id}| python -m json.tool | grep rootfs -A 50"rootfs": {"diff_ids": ["sha256:7948c3e5790c6df89fe48041fabd8f1c576d4bb7c869183e03b9e3873a5f33d9","sha256:4d1ab3827f6b69f4e55bd69cc8abe1dde7d7a7f61bd6e32c665f12e0a8efd1c9",........"sha256:f7e3a65f50c8e650b07c6ea2e2486852543ae281a680cb3b71f05b4a58118cb4"],"type": "layers"}

这里一共有20层,我将中间的一些层去掉了,但是不妨碍分析。最上面的7948c3e5...是Image的layer层的底层,那既然找到了diff_id,我们再去看下layer层的实现

5.在/var/lib/docker/image/overlay2/layerdb/sha256目录下去查看layer信息(sha256目录保存的是可读层的信息,mount目录保存的读写层的信息)

root@VM-0-3-ubuntu:/var/lib/docker/image/overlay2/layerdb/sha256# ls -la
total 92
drwxr-xr-x 23 root root 4096 Nov 14 10:25 .
drwx------  5 root root 4096 Nov 13 10:54 ..
drwx------  2 root root 4096 Nov 13 12:44 0277e531f0f0767896e5e3c953a44bac7d46a1595ea9e1a461257d9664868be6
#此处去掉了一些层
drwx------  2 root root 4096 Nov 11 14:06 7948c3e5790c6df89fe48041fabd8f1c576d4bb7c869183e03b9e3873a5f33d9

我们可以看到,仅仅只能找到7948c3e579...这个Image的底层,其他的layer在这个目录下都找不到,这是为什么呢?

这是由于Docker使用了chainID来保存layer,计算公式是:chainID=sha256sum(H(chainID) diffid),其中如果是最底层layer,那么其chainID=diff_id。所以在sha256目录下,我们只会看到Image的最底层layer,而看不到其他的layer。 (sha256下的目录是以chainID命名的,而不是以diff_id命名的,所以需要转换一下)

6.计算下7948c3e579...下一层的chainID

root@VM-0-3-ubuntu:/var/lib/docker/image/overlay2/layerdb/sha256# echo -n "sha256:7948c3e5790c6df89fe48041fabd8f1c576d4bb7c869183e03b9e3873a5f33d9 sha256:4d1ab3827f6b69f4e55bd69cc8abe1dde7d7a7f61bd6e32c665f12e0a8efd1c9" | sha256sum
72df91e735ae2b70a23f1770aa365b67f4772a545ed0a23d3decc7643b21a4e4  -

可以看到下一层的chainID是72df91e735........,这样我们能从layerdb/sha256找到对应的下一层了,依次类推,我们能知道所有的层级结构;

目录下的layerdb目录中保存的是元数据,那么我们需要到overlay2目录中去找到对应的真实目录,那么这个之间的映射关系是怎么得到的呢?

这个时候cache-id就排上用场了,我们可以打印出该chainID的cache-id,并到overlay2目录中去找(dockr会创建/var/lib/docker/{存储引擎名称}老保存实际的数据)

#到最底层7948c3e579...中打印cache-id
root@VM-0-3-ubuntu:/var/lib/docker/image/overlay2/layerdb/sha256/7948c3e5790c6df89fe48041fabd8f1c576d4bb7c869183e03b9e3873a5f33d9# cat cache-id
cecf9979915e13912ed6cba6dbf22d622c4c59e645fe57ce37a76f5e098363c6#到/var/lib/dockr/overlay2中找到对应的目录
root@VM-0-3-ubuntu:/var/lib/docker/overlay2# ls -la | grep cecf9979915e13912ed6cba6dbf22
drwx------  3 root root 4096 Nov 11 14:06 cecf9979915e13912ed6cba6dbf22d622c4c59e645fe57ce37a76f5e098363c6

可以看到已经找到对应的目录了,该目录中记录了所有的diff

8.在Step2中找到的容器ID的元数据其实是保存在/var/lib/docker/image/overlay2/layerdb/mount/目录下,该目录保存的是docker的容器读写层。这其实也对应了docker的存储实现机制,镜像都是只读层,当运行一个容器时,会产生一个容器层,该层即是读写层。

root@VM-0-3-ubuntu:/var/lib/docker/image/overlay2/layerdb/mounts# ls -la
drwxr-xr-x 2 root root 4096 Nov 13 10:54 39274f2825c4ac012eb08ffc7fe7b383ab2f7ccda0f7fd1fd046b235d32d8a93

查看该目录下的文件

root@VM-0-3-ubuntu:/var/lib/docker/image/overlay2/layerdb/mounts/39274f2825c4ac012eb08ffc7fe7b383ab2f7ccda0f7fd1fd046b235d32d8a93# ls -l
total 12
-rw-r--r-- 1 root root 69 Nov 13 10:54 init-id
-rw-r--r-- 1 root root 64 Nov 13 10:54 mount-id
-rw-r--r-- 1 root root 71 Nov 13 10:54 parent     

init-id 和 mount-id里面的内容基本一样,都是记录该容器层的cache-id,用来映射overlays目录下真实的存储层

parent文件是记录上一层的layer层,里面记录的chainID


四. 参考资料

因为最初只是将知识记录在自己的有道云上,因此有部分网上的资料忘记了存储链接,若有兄弟发现自己写的文章被我引用了,请联系我删除或者补链接!

参考资料如下:

Docker存储驱动之--overlay2

docker修改镜像的存储位置_Docker存储原理相关推荐

  1. docker修改镜像存储路径

    docker修改镜像存储路径 查看docker镜像的默认存储路径 docker info 指定镜像和容器存放路径的参数是--graph=/var/lib/docker,其默认存储位置为/var/lib ...

  2. docker修改镜像的存储位置_云原生存储详解:容器存储与 K8s 存储卷(内含赠书福利)...

    作者 | 阚俊宝  阿里巴巴技术专家 参与文末留言互动,即有机会获得赠书福利! 导读:云原生存储详解系列文章将从云原生存储服务的概念.特点.需求.原理.使用及案例等方面,和大家一起探讨云原生存储技术新 ...

  3. docker修改镜像的存储位置_win10家庭版Docker环境搭建步骤

    1. 什么是Docker 一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会 ...

  4. docker 修改镜像地址

    初学docker 看网上说docker要修改镜像,因为国内的镜像比较快 所以我也修改了 [root@izr86o1 etc]# cd docker [root@izr86o1 docker]# ls ...

  5. Docker——修改镜像源

    国外的镜像仓库拉取镜像经常失败,修改为国内仓库后拉取能更快. 1.选择国内镜像仓库 Docker 官方中国区:https://registry.docker-cn.com 网易:http://hub- ...

  6. Docker 修改镜像源地址

    docker run -p 8050:8050 scrapinghub/splash --registry-mirror=http://hub-mirror.c.163.com from:https: ...

  7. docker修改镜像

    如何保证缓存和数据库一致性 说了这么多缓存的必要性,那么使用缓存是不是就是一个很简单的事情了呢,我之前也一直是这么觉得的,直到遇到了需要缓存与数据库保持强一致的场景,才知道让数据库数据和缓存数据保持一 ...

  8. Docker修改镜像源为阿里云

    登录阿里云:https://cr.console.aliyun.com/

  9. 以太坊智能合约开发:Solidity语言中变量的存储位置与作用域

    在Solidity中,有一些数据类型是引用类型,如: 数组(string和bytes是特殊的数组,也是引用类型) 结构体(struct) 映射(mapping) 在Solidity中使用引用类型的时候 ...

最新文章

  1. 初中生问题:求任意凸多边形的交叉面积
  2. inv(a) matlab,在MATLAB中,inv(A)表示的是()
  3. 值得永久收藏的 C# 设计模式套路(二)
  4. Python 列表 index( )方法
  5. mac设置python版本切换,和设置python默认版本
  6. SELinux系列(十一)——SELinux策略规则查看方法(seinfo和sesearch)详解
  7. Ps 初学者教程,如何使用色阶滴管工具校正色偏?
  8. 搬水果 - 九度教程第31题(哈夫曼树)
  9. 规约转换装置的一些概念
  10. 【Axure交互教程】 滑动拼图验证登录效果
  11. PSNR峰值信噪比(python代码实现+SSIM+MSIM)
  12. 只会环比下降3%的数据分析师还有救吗?
  13. Jmail的使用方法
  14. 互联网金融爬虫怎么写-第一课 p2p网贷爬虫(XPath入门)
  15. 085400计算机技术专业怎么参加公考,2021年东南大学电子信息(085400)计算机技术_考研专业目录_考试科目_考试范围 - 学途吧...
  16. leetcode刷题技巧
  17. 地表温度反演LST 自存步骤
  18. 全新的小米,从更流畅,更安全,更智慧的MIUI 14开始
  19. 命令行连接mongo数据库
  20. 简单实用 微信授权登陆(网页版)

热门文章

  1. 24、Power Query-数学运算的应用(统计男女人数)
  2. 数据库优化之统计分析实战篇
  3. 使用特殊的技术更新数据库(ABAP)
  4. 外包物料成本核算时的供应商确定
  5. ECC6.0中数据导出到本地时报错GETWA_NOT_ASSIGNED
  6. ABAP常用字符串操作收集整理
  7. python中的字符串是什么,一文秒懂Python中的字符串
  8. 计算机学院开展活动,团学会 | 以梦为马,不负韶华 计算机学院开展“逐梦100”活动(一)...
  9. Python3 中的最大整数和最大浮点数
  10. Python那些优雅的写法:switch-case