既然 docker 是共享主机内核, 并且使用隔离技术, 为什么还需要用 linux 系统作为根镜像

docker bootfs rootfs images unionfs

Posted by gomyck on April 12, 2023

理解 docker 镜像底层原理, 对于开发 docker 镜像和阅读 dockerfile 具有一定的指导作用

什么是镜像

简单理解为软件包, 可以使用 docker 服务执行的一种软件包, 是一种轻量级, 用于打包软件本身与软件运行环境的一种软件包, 镜像需要再容器中启动, 启动后的预期与结果总是一致, 并不会受到操作系统, 软硬件的差异影响, 所以镜像是跨平台的, 利于部署的, 这完全依托于镜像的构成理念, 与 docker 对于此的底层支持

UnionFS 与镜像之间的关系

UnionFS 是一种文件系统, 由 linux 操作系统提供, Linux各发行版实现的UnionFS各不相同, 所以Docker在不同linux发行版中使用的也不同

可以通过docker info来查看docker使用的是哪种, 如:

  • centos, docker18.03.1-ce: Storage Driver: overlay2
  • debain, docker17.03.2-ce: Storage Driver: aufs

UnionFS 可以通过

1
$ mount -t aufs -o dirs=./x1:./x2 none ./xxx

把 x1 和 x2 挂载至 xxx 上, 这样在 xxx 上就可以看到两个文件夹的内容了

默认情况下, 最左边的文件夹(x1)是读写的, 其余全是只读, 除非显示声明 mount -t aufs -o dirs=./x1=rw:./x2=rw none ./xxx

如果尝试修改 x2 里的文件, 则会在 x1 下创建一个同名的被修改过后内容的文件, 并不会动 x2 的文件(因为是是只读)

如果是删除 x1 和 x2 的文件, 则 x1 里的文件会真删, x2 里的文件则会在 x1 内创建一个 .wh.文件名 的文件, 来标注文件被排除忽略显示, 删除一个文件和在 x1 里添加一个 wh 文件是一样的效果

查看一个容器的挂载信息

1
2
3
4
5
6
$ ls /var/lib/docker/image/unionfs类型/layerdb/mounts/容器ID

$ cat mount-id

#查看所有挂载点
$ cat /proc/mounts

bootfs rootfs

bootfs(boot file system) 主要包含bootloader 和 kernel,bootloader 主要用于引导加载 kernel,当 kernel 被加载到内存中后 bootfs 会被 umount 掉。

rootfs (root file system) 包含的就是典型 Linux 系统中的/dev,/proc,/bin,/etc 等标准目录和文件

不同的 linux 发行版(如 ubuntu 和 CentOS ) 在 rootfs 这一层会有所区别,体现发行版本的差异性。

传统的 Linux 加载 bootfs 时会先将 rootfs 设为 read-only,然后在系统自检之后将 rootfs 从 read-only 改为 read-write, 然后我们就可以在 rootfs 上进行读写操作了。但 Docker 在 bootfs 自检完毕之后并不会把 rootfs 的 read-only 改为 read-write, 而是利用 union mount(UnionFS 的一种挂载机制)将 image 中的其他的 layer 加载到之前的 read-only 的 rootfs层之上, 每一层 layer 都是 rootfs 的结构,并且是read-only 的。所以,我们是无法修改一个已有镜像里面的 layer 的!只有当我们创建一个容器, 也就是将 Docker 镜像进行实例化,系统会分配一层空的 read-write 的 rootfs ,用于保存我们做的修改。一层 layer 所保存的修改是增量式的, 就像 git 一样。

综上,image其实就是一个文件系统(rootfs),它与宿主机的内核一起为程序提供一个虚拟的linux环境。在启动docker container时,依据image,docker会为container构建出一个虚拟的linux环境。

对于一个精简的os,rootfs可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用host的kernal,自己只需要提供rootfs就可以了。 由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs。

总结

  • 镜像是 rootfs 的打包产物
  • linux 不同的发行版, bootfs 基本一致
  • docker 容器是一个运行在宿主机内核的一个进程, 进程运行结束, 容器也就停止了
  • 镜像的分层实际是利用的 unionfs 分层技术, 所以 layer 是只读的, layer 的概念也可以减少重复资源的生成
  • rootfs 是系统启动需要挂在的第一个文件系统, 其内包含了 linux 运行标准目录和shell命令, 用来制备 linux 运行环境