美文网首页
docker 学习笔记3:镜像与存储驱动

docker 学习笔记3:镜像与存储驱动

作者: 董泽润 | 来源:发表于2019-12-11 17:17 被阅读0次

TL;DR docker 能流行起来,镜像是最大的原因,他把程序的依赖打包在一起,一次构建,处处运行

初识镜像

我们知道,docker 容器实际上与宿主机共享内核,只是宿主机上的一个普通进程,只不过用 mnt, pid, net 等等做了 namespace 隔离,然后再用 cgroup 做了资源限制。

docker and host OS
如上图所示,左侧传统虚拟化出来的需要一层 Guest OS, 这就是本质的区别。

1. 查看内容

root@myali:~# docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
7ddbc47eeb70: Pull complete
c1bbdc448b72: Pull complete
8c3b70e39044: Pull complete
45d437916d57: Pull complete
Digest: sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest

使用 docker pull ubuntu 拉取最新的镜像

root@myali:~# mkdir ubuntu
root@myali:~# docker export $(docker create ubuntu) | tar -C ./ubuntu -xvf -
root@myali:~# ls ubuntu
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

将镜像内容解压后,我们发现,原来就是常用的文件系统内容而己。如果是其它定制化的,那么还会包含用户自定义的,比如 python 依赖,go 库等等

2. 制作镜像

基于上面拉取的 ubuntu,定制新的镜像。先写两个测试文件

root@myali:~# echo "out of docker" > config.json
root@myali:~# echo "out of ccccc" > ccccc

查看我们自写义的 Dockerfile 文件

root@myali:~# cat Dockerfile
FROM ubuntu
COPY config.json /root
COPY ccccc /
CMD /bin/sh

然后打包生成镜像

root@myali:~# docker build . -t myimage:latest
Sending build context to Docker daemon  68.39MB
Step 1/4 : FROM ubuntu
 ---> 775349758637
Step 2/4 : COPY config.json /root
 ---> 9c1a567cc02e
Step 3/4 : COPY ccccc /
 ---> c3f52d5158ab
Step 4/4 : CMD /bin/sh
 ---> Running in 540e2987bf4c
Removing intermediate container 540e2987bf4c
 ---> 4b9c8ca82e42
Successfully built 4b9c8ca82e42
Successfully tagged myimage:latest
root@myali:~# docker history myimage
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
4b9c8ca82e42        4 hours ago         /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/bin…   0B
c3f52d5158ab        4 hours ago         /bin/sh -c #(nop) COPY file:414a288c3053d3f9…   13B
9c1a567cc02e        4 hours ago         /bin/sh -c #(nop) COPY file:64632fffb953c191…   14B
775349758637        5 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>           5 weeks ago         /bin/sh -c mkdir -p /run/systemd && echo 'do…   7B
<missing>           5 weeks ago         /bin/sh -c set -xe   && echo '#!/bin/sh' > /…   745B
<missing>           5 weeks ago         /bin/sh -c [ -z "$(apt-get indextargets)" ]     987kB
<missing>           5 weeks ago         /bin/sh -c #(nop) ADD file:a48a5dc1b9dbfc632…   63.2MB

可以看到,镜像制做是分层的,每一个命令一层 layer, FROM ubuntu 表示基于哪个镜像制作的,最后 CMD /bin/sh 意思是容器启动后要运行的命令,也就是最终 runc 要执行的。

3. 分层设计

分层 layer,好处是多个镜像可以共享,拉取一个新镜像时,只需拉取不同的部份即可。一个容器典型分成三层:

  • 容器层:镜像是静态的,启动时拥有一个完整的容器层,可读写。
  • init 层:这一层是运行时动态生成的,只读,用来给每个容器生成不同的 dev etc 等等
  • 镜像层:镜像不同 layer 的总称,内容是只读的。
root@myali:/var/lib/docker/overlay2# mount | grep -i overlay2
overlay on /var/lib/docker/overlay2/dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/SR34NROUREC6U2N6HMTAOH66CZ:/var/lib/docker/overlay2/l/K535YPMQVAVYMUJUJ5ONJSTV6M:/var/lib/docker/overlay2/l/AHPVAYNKIZTOHPVOYPD7BU7J7V:/var/lib/docker/overlay2/l/IAFCF667EV4HZWTDYKYVHFGX4D:/var/lib/docker/overlay2/l/DRDJ5SXKMKS5SAXLFA6SGQBRSW:/var/lib/docker/overlay2/l/FYPOP5ERVXC7OGFHRU5NLUEBT5:/var/lib/docker/overlay2/l/6LZLHYDHXDIJGNIKISCFFGA5BP,upperdir=/var/lib/docker/overlay2/dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da/diff,workdir=/var/lib/docker/overlay2/dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da/work)
overylay2

上面截图是查看当前挂载的 overlay2 文件系统,其中

  • lowerdir 是底层的镜像层
  • workdir 是 overlayfs 工作目录
  • upperdir 是容器层,所以容器里的对文件的更改都会放到这里
  • merged 是联合文件系统合并目录,最终合并展现给容器用户的
root@myali:~# ls /var/lib/docker/overlay2/dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da/merged
bin  boot  ccccc  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
layer

存储驱动

我们知道容器启动后,程序是要写本地文件的,这里分两种情况,一个是挂载 volume 写日志类的,一般 volume 都是本地磁盘,或是网络文件系统,另外一个是镜像容器层的读写,这个就是 docker 所谓的 Storage Driver, 可以通过 docker info 查看,最新默认是 overlay2,backing fs 是 extfs,也就是 ex4.

root@myali:~# docker info
......
 Server Version: 19.03.5
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
......

1. Driver 类型

基于文件实现的 aufs, overlay, overlay2, 还有基于块实现的 devicemapper, zfs, btrfs, 不同类型之间各有优缺点:基于文件的可以共享文件缓存,所以在内存使用上更高效一些。但是如果有改动,块的更高效一些。

storage driver

2. overlay 与 overlay2

overlay/overlay2 属于联合文件系统,实现都是 copy-on-write,区别是 overlay 采用两层,硬链接文件来实现(需要递归创建目录,消耗更多的 inodes),而 overlay2 最多可以有 128 层,最新推荐使用后者

overlay

对于读:

  • 读的文件不在容器层:如果读的文件不在容器层,则从镜像层进行读
  • 读的文件只存在在容器层:直接从容器层读
  • 读的文件在容器层和镜像层:读容器层中的文件,因为容器层隐藏了镜像层同名的文件

对于写:

  • 写的文件不在容器层,在镜像层:由于文件不在容器层,因此overlay/overlay2存储驱动使用copy_up操作从镜像层拷贝文件到容器层,然后将写入的内容写入到文件新的拷贝中。
  • 删除文件和目录:删除镜像层的文件,会在容器层创建一个whiteout文件来隐藏它;删除镜像层的目录,会创建opaque目录,它和whiteout文件有相同的效果
  • 重命名目录:对一个目录调用rename(2)仅仅在资源和目的地路径都在顶层时才被允许,否则返回EXDEV。

4. 文件结构

不同存储数型的目录是不同的,以 overlay2 为例默认是 /var/lib/docker/overlay2

root@myali:/var/lib/docker/overlay2# ls -rlt
total 44
drwx------ 3 root root 4096 Dec 11 10:52 81d3cf5ac6d993c9768bb3bf6352b0b8e86752ce5c8d6aba23cb0430d07aee56
drwx------ 4 root root 4096 Dec 11 10:52 58bbbb798d7548cedcb3d0c3367b8aed391534c2fef7bec980ed6337281eca76
drwx------ 4 root root 4096 Dec 11 10:52 9fd5371cc3852d951116f38f0fed873a23019edf0d730701b5501ec3ba64e1a4
drwx------ 4 root root 4096 Dec 11 11:00 98a0ef116d91098c578781085cc183cf977ef449a671d3b92134e303ca85b76f
drwx------ 4 root root 4096 Dec 11 12:43 3ac3321ec3c39ee8e9ca202ff9f47f2c45adcdd58970b363c8007ae48f936d04
drwx------ 4 root root 4096 Dec 11 12:43 6068909bbebb79f5e3d3f21d196a0a0ee542daef6379c7702805bb3bbad01dbe
drwx------ 4 root root 4096 Dec 11 12:57 5c4545f510fa94188705e27b6cbaff00b6bda6c4a69f89a6365eee0c85a90bf2-init
drwx------ 4 root root 4096 Dec 11 12:57 5c4545f510fa94188705e27b6cbaff00b6bda6c4a69f89a6365eee0c85a90bf2
drwx------ 2 root root 4096 Dec 11 12:57 l
drwx------ 4 root root 4096 Dec 11 12:57 dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da-init
drwx------ 5 root root 4096 Dec 11 12:57 dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da

注意这个文件夹有个 l 目录,这里面存放不同 layer 的软连接,但是名字很短,主要是用于 mount 挂载时能显示出来,其中 dcfb38888..... 就是我们的容器层,dcfb38888.....-init 是临时生成的只读层。

root@myali:/var/lib/docker/overlay2# ls -l dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da
total 20
drwxr-xr-x 3 root root 4096 Dec 11 15:44 diff
-rw-r--r-- 1 root root   26 Dec 11 12:57 link
-rw-r--r-- 1 root root  202 Dec 11 12:57 lower
drwxr-xr-x 1 root root 4096 Dec 11 15:44 merged
drwx------ 3 root root 4096 Dec 11 12:57 work

看下容器层里面的内容,diff 目录是保存运行时的改动,初始为空。link 文件里的内容是当前层的软连接短名,merged 就是合并后展现组容器的统一统目,work 是工作目录。

root@myali:/var/lib/docker/overlay2# cat dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da/lower
l/SR34NROUREC6U2N6HMTAOH66CZ:l/K535YPMQVAVYMUJUJ5ONJSTV6M:l/AHPVAYNKIZTOHPVOYPD7BU7J7V:l/IAFCF667EV4HZWTDYKYVHFGX4D:l/DRDJ5SXKMKS5SAXLFA6SGQBRSW:l/FYPOP5ERVXC7OGFHRU5NLUEBT5:l/6LZLHYDHXDIJGNIKISCFFGA5BP

再看下 lower 目录,里面都是依赖的所有下层 layer

5. 读写测试

测试在容器里分别写不存在的文件,修改文件,删除文件及目录

# touch bbbbbb
# echo "inner" > ccccc
# cat ccccc
inner
# rm root/config.json
# rm -fr mnt

查看容器层的 diff 内容

root@myali:/var/lib/docker/overlay2# tree -L 2 dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da/diff
dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da/diff
├── bbbbbb
├── ccccc
├── mnt
└── root
    └── config.json

1 directory, 4 files
root@myali:/var/lib/docker/overlay2# file dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da/diff/mnt
dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da/diff/mnt: character special (0/0)
root@myali:/var/lib/docker/overlay2# file dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da/diff/root/config.json
dcfb388887b481f92add6fb4b0ac3b98d76ac9ebeebcaf26e19f1a9de8d5b9da/diff/root/config.json: character special (0/0)

可以看到,新增与改动都在 diff 下面,并且删除了文件和目录变成了 character special file

小结

镜像这一块比较复杂,还涉及到仓库,暂时先不看了

相关文章

  • docker 学习笔记3:镜像与存储驱动

    TL;DR docker 能流行起来,镜像是最大的原因,他把程序的依赖打包在一起,一次构建,处处运行 初识镜像 我...

  • docker存储驱动

    一句话,docker 存储驱动用于管理docker 镜像和容器。 1. 镜像和层 在了解docker存储驱动之前,...

  • 3、docker入门

    docker状态 该命令会返回所有容器和镜像的数量、docker使用的驱动程序和存储驱动,以及docker的基本配...

  • docker 命令

    查看 Docker:docker info 该命令会返回所有容器和镜像的数量、Docker 使用的执行驱动和存储驱...

  • Docker存储驱动之DeviceMapper

    Docker中数据存储相关有几部分,存储驱动(storage driver)用于存储镜像和容器,默认路径为/var...

  • k8s集群详细部署和使用-全集

    Docker基础学习 01.Docker笔记之与镜像相关的命令整理https://www.jianshu.com/...

  • Docker的那些事儿—镜像管理(17)

    上一篇:Docker的那些事儿—镜像的存储:私有仓库(16) 前面学习了构建镜像以及镜像的存储,这节总结下关于镜像...

  • Docker存储驱动devicemapper配置

    devicemapper驱动将每一个 Docker镜像 和容器存储在它自身的具有精简置备(thin-provisi...

  • CI/CD之Docker容器DevOps知识汇总

    容器与虚拟主机的区别 Docker安装 Docker的镜像和容器 Docker的网络 Docker的持久化存储和数...

  • Docker笔记

    Docker笔记 Docker 镜像常用命令 搜索镜像 docker search java 下载镜像 docke...

网友评论

      本文标题:docker 学习笔记3:镜像与存储驱动

      本文链接:https://www.haomeiwen.com/subject/dplbgctx.html