写点什么

〖Docker 指南③〗Docker 镜像的深度解析

作者:步尔斯特
  • 2022 年 6 月 08 日
  • 本文字数:3895 字

    阅读完需:约 13 分钟

〖Docker指南③〗Docker镜像的深度解析

镜像是一种轻量级、可执行的独立软件包, 用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。

一、docker 镜像

1.1 广度

一个完整的 Docker 镜像可以支撑一个 Docker 容器的运行,在 Docker 容器运行过程中主要提供文件系统视角。

例如一个ubuntu的镜像,提供了一个基本的ubuntu的发行版,当然此镜像是不包含操作系统Linux内核的。

linux 内核和 ubuntu 的 Docker 镜像的区别

传统虚拟机安装 ubuntu 会包含两部分,第一,某一个 Linux 内核的发行版本,比如 Linux 3.8 版本的内核;第二,第一个特定的 Ubuntu 发行版,这部分内容不包含 Linux 内核,但是包含 Linux 之外的软件管理方式,软件驱动,如 apt-get 软件管理包等。

在这里插入图片描述

理解以上内容之后,就可以理解,为什么在一个 Linux 内核版本为 3.8 的 ubuntu 基础上,可以把 Linux 内核版本升级到 3.18,而 ubuntu 的版本依然不变。

Linux内核+ubuntu操作系统发行版,组成一台工作的机器

Docker 很方便的利用了这一点,可以灵活替换 ubuntu 操作系统发行版,技术手段就是Docker镜像

Docker 的架构中,Docker 镜像就是类似于“ubuntu 操作系统发行版”,可以在任何满足要求的 Linux 内核之上运行。简单一点有“Debian 操作系统发行版”Docker 镜像、“Ubuntu 操作系统发行版”Docker 镜 像;如果在 Debian 镜像中安装 MySQL 5.7,那我们可以将其命名为 Mysql:5.7 镜像;如果在 Debian 镜像中安装有 Golang 1.3,那我们可以将其命名为 golang:1.3 镜像;以此类推,大家可以根据自己安装的软件,得到任何自己想要的镜像。

镜像的作用:回到 Linux 内核上来运行,通过镜像来运行时我们常常将提供的环境称为容器。

1.2 精度

刚才提到了“Debian 镜像中安装 MySQL 5.7,就成了 mysql:5.7 镜像”,其实在此时 Docker 镜像的层级概念就体现出来了。底层一个 Debian 操作系统镜像,上面叠加一个 mysql 层,就完成了一个 mysql 镜像的构建。层级概念就不难理解,此时我们一般 debian 操作系统镜像称为 mysql 镜像层的父镜像。

层级管理的方式大大便捷了 Docker 镜像的分发与存储。说到分发,大家自然会联想到 Docker 镜像的灵活性,传输的便捷性,以及高超的移植性。Docker Hub,作为全球的镜像仓库,作为 Docker 生态中的数据仓库,将全世界的 Docker 数据汇聚在一起,是 Docker 生态的命脉。

Docker 有两方面的技术非常重要,第一是 Linux 容器方面的技术,第二是 Docker 镜像的技术。从技术本身来讲,两者的可复制性很强,不存在绝对的技术难点,然而 Docker Hub 由于存在大量的数据的原因,导致 Docker Hub 的可复制性几乎不存在,这需要一个生态的营造。

二、docker 镜像内容

初步接触 Docker。相信很多爱好者都会和我一样,有这样一个认识:Docker 镜像代表一个容器的文件系统内容;

初步接触联合文件系统。联合文件系统的概念,让我意识到镜像层级管理的技术,每一层镜像都是容器文件系统内容的一部分。

镜像与容器的关系:容器是一个动态的环境,每一层镜像中的文件属于静态内容,然而 Dockerfile 中的 ENV、VOLUME、CMD 等内容最终都需要落实到容器的运行环境中,而这些内容均不可能直接坐落到每一层镜像所包含的文件系统内容中,那此时每一个 Docker 镜像还会包含 json 文件记录与容器之间的关系。

因此,Docker 镜像的内容主要包含两个部分:第一,镜像层文件内容;第二,镜像json文件

三、docker 镜像存储

3.1 查看镜像层组成

我们可以通过命令 docker history issa/myubuntu:1.12 查看 issa/myubuntu:1.12,结果如下:

在这里插入图片描述

3.2 镜像层文件内容存储和镜像 json 文件存储

四、docker 镜像的运行原理

当我们在我启动一个容器的时候,docker 会加载这些只读层并在这些只读层的上面(栈顶)增加一个读写层。这时如果修改正在运行的容器中已有的文件,那么这个文件将会从只读层复制到读写层。该文件的只读版本还在,只是被上面读写层的该文件的副本隐藏。当删除 docker,或者重新启动时,之前的更改将会消失。

当一个容器启动后,它将会被移到内存中,而引导文件系统则会被卸载,以留出更多的内存供 initrd 磁盘镜像使用。


Docker 镜像是由文件系统叠加而成,最低端是一个引导文件系统,即 bootfs,典型的为 Linux/Unix 的引导文件系统。bootfs(boot file system)主要包含 bootloader 和 kernel, bootloader 主要是引导加载 kernel, Linux 刚启动时会加载 bootfs 文件系统, 在 Docker 镜像的最底层是 bootfs。 这一层与我们典型的 Linux/Unix 系统是一样的,包含 boot 加载器和内核。当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs。

Docker 镜像的第二层是 root 文件系统 rootfs,位于引导文件系统之上,rootfs 可以是一种或多种操作系统如 ubuntu 文件系统。在 bootfs 之上 。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。rootfs 就是各种不同的操作系统发行版,比如 Ubuntu,Centos 等等。

Docker 镜像是由多个文件系统(只读层)叠加而成。当我们启动一个容器的时候,Docker 会加载只读镜像层并在其上(即镜像栈顶部)添加一个读写层。如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏。当删除 Docker 容器,并通过该镜像重新启动时,之前的更改将会丢失。在 Docker 中,只读层及在顶部的读写层的组合被称为 Union File System(联合文件系统)。

联合文件系统是一种轻量级的高性能分层文件系统,它支持将文件系统中的修改信息作为一次提交,并层层叠加,同时可以将不同目录挂载在同一个虚拟文件系统下,应用看到的是挂载的最终结果。

平时我们安装进虚拟机的 CentOS 都是好几个 G,为什么 docker 这里才 200M?

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

四、UnionFS(联合文件系统)

UnionFS(联合文件系统):Union 文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加, 同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录

五、分层的镜像

以我们的 pull 为例,在下载的过程中我们可以看到 docker 的镜像好像是在一层一层的在下载 。

六、为什么 Docker 镜像要采用这种分层结构呢?

最大的一个好处就是 - 共享资源

比如:有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份 base 镜像, 同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。

七、特点

Docker 镜像都是只读的

当容器启动时,一个新的可写层被加载到镜像的顶部

这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”

八、Docker 镜像 commit 操作

docker commit -m=“提交的描述信息” -a=“作者” 容器ID 要创建的目标镜像名:[标签名]

eg-1:

  1. 从 Hub 上下载 ubuntu 镜像到本地并成功运行docker run -it ubuntu


  1. 安装 vimapt-get -y install vim

  2. vim test.txt


  1. docker commit -m="add vim" -a="issavior" d50d5a500ed0 issa/myubuntu:1.12


eg-2:

  1. 从 Hub 上下载 tomcat 镜像到本地并成功运行docker run -it -p 8080:8080 tomcat

  2. 故意删除上一步镜像生产 tomcat 容器的文档

  3. 也即当前的 tomcat 运行实例是一个没有文档内容的容器,以它为模板 commit 一个没有 doc 的 tomcat 新镜像

  4. 启动我们的新镜像并和原来的对比

九、QA

  1. 问:为什么一个ubuntu:14.04镜像的镜像层的数量是4个,前三层的内容似乎有相同的,如etc?这一点,细心的大家肯定发现了。首先,虽然三层都有,但是会存在两种情况,etc 的子目录下有相同路径的文件,那么上层的会覆盖下层的文件;如果内部的文件路径不相同,那么都会存在,都会呈现给最上 层。[可别较真,说目录也是文件哈,意会]

  2. 问:关于docker安全性问题,对于安全是怎样处理的,如果我从hub下载镜像,能判别是否安全么?层级之间的依赖会导致一个崩了整个docker 都崩了么?从流程上来讲,如果一切可控的话,我认为是安全的。但是依然会存在一些隐患,比如 Dockerfile 中基于的 base images 是否完全受信;镜像的传输过程是否受信;自己的 private docker resgitry 的安全级别达到什么样的层次,这些都有影响。

  3. 问:如何保证仅有的一个deamon的稳定性健壮性?这个问题首先需要知道 docker daemon 的稳定性在哪些方面,那种场景下比较差?的确,docker daemon 存在弊病。比如,daemon 和容器的耦合等,目前 general 来讲,docker daemon 保证绝对的稳定应该还做不到。

  4. 问:生产环境中怎么用docker备份mysql数据?数据存储上 docker,我目前的建议是:三思。举个简单的例子,官方的 mysql 镜像运行出来的 容器,密码是明文的,明文的密码存在于:docker inspect container_name, container.json 文件中,容器的环境变量中,甚至在日志文件中都会存在,just think about it。当然也有办法解决,缓解这种情况。

  5. 问:如果是多层构建,中间的一个层做了升级或者bugfix,会潜在影响上层吧?这个 bugfix 会在上层有体现,但是使用效果是不会有影响的,还有之前的 bug 会永远留在下层,但是没有影响。

用户头像

步尔斯特

关注

🏅️ InfoQ 签约作者 2022.05.07 加入

微信搜一搜:【步尔斯特】,回复「1024」惊喜不断

评论

发布
暂无评论
〖Docker指南③〗Docker镜像的深度解析_步尔斯特_InfoQ写作社区