美文网首页
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:镜像与存储驱动

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