1. 镜像与容器
1.1 镜像
Docker镜像类似于未运行的exe应用程序,或者停止运行的VM。当使用docker run命令基于镜像启动容器时,容器应用便能为外部提供服务。
镜像实际上就是这个用来为容器进程提供隔离后执行环境的文件系统。我们也称之为根文件系统(Rootfs)。(注意,rootfs 只是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。同一台机器上的所有容器,都共享宿主机操作系统的内核。)
由于 rootfs 里封装的不仅仅是应用,还包括它运行所需要的所有依赖。这就赋予了容器的强一致性:无论在本地、云端,还是其他任何地方,用户只需要解压打包好的容器镜像,这个应用运行所需要的完整的执行环境就被重现出来了。
我们可以将 Docker 镜像理解为包含应用程序以及其相关依赖的一个基础文件系统,在 Docker 容器启动的过程中,它以只读的方式被用于创建容器的运行环境。
1.1.1 镜像分层
但还有一个不容忽视的问题,例如如果我需要一个在CentOS环境中跑的apache应用,我可以将它打包成一个apache镜像;如果我还需要一个在CentOS环境中跑的mysql应用,我又将它打包成一个mysql镜像……
这几个镜像中都有全部的CentOS的全部环境,将造成大量空间占用问题及碎片化问题。
Docker的解决方法是: 在镜像的设计中,引入了层(layer)的概念。即: 用户制作镜像的每一步操作,都生成一个层,也就是一个增量 rootfs。
Docker 镜像其实是由基于 UnionFS 文件系统的一组镜像层依次挂载而得,而每个镜像层包含的其实是对上一镜像层的修改,这些修改其实是发生在容器运行的过程中的。所以,我们也可以反过来理解,镜像是对容器运行环境进行持久化存储的结果。
1.1.1 镜像的实现
1.1.1.1 Docker 是如何构建并且存储镜像的
Docker 中的每一个镜像都是由一系列只读的层组成的,Dockerfile 中的每一个命令都会在已有的只读层上创建一个新的层,容器中的每一层都只对当前容器进行了非常小的修改
当镜像被 docker run 命令创建时就会在镜像的最上层添加一个可写的层,也就是容器层,所有对于运行时容器的修改其实都是对这个容器读写层的修改。
上面的这张图片非常好的展示了组装的过程,每一个镜像层都是建立在另一个镜像层之上的,同时所有的镜像层都是只读的,只有每个容器最顶层的容器层才可以被用户直接读写,所有的容器都建立在一些底层服务(Kernel)上,包括命名空间、控制组、rootfs 等等,这种容器的组装方式提供了非常大的灵活性,只读的镜像层通过共享也能够减少磁盘的占用。
1.1.1.2镜像概述
所有的 Docker 镜像都是按照 Docker 所设定的逻辑打包的,也是受到 Docker Engine 所控制的。
我们常见的虚拟机镜像,通常是由热心的提供者以他们自己熟悉的方式打包成镜像文件,被我们从网上下载或是其他方式获得后,恢复到虚拟机中的文件系统里的。而 Docker 的镜像我们必须通过 Docker 来打包,也必须通过 Docker 下载或导入后使用,不能单独直接恢复成容器中的文件系统。
虽然这么做失去了很多灵活性,但固定的格式意味着我们可以很轻松的在不同的服务器间传递 Docker 镜像,配合 Docker 自身对镜像的管理功能,让我们在不同的机器中传递和共享 Docker 变得非常