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

2019-12-11  本文已影响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,好处是多个镜像可以共享,拉取一个新镜像时,只需拉取不同的部份即可。一个容器典型分成三层:

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 文件系统,其中

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

对于读:

对于写:

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

小结

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

上一篇 下一篇

猜你喜欢

热点阅读