Docker是一个开源的应用程序引擎。Docker使用容器引擎解决平台依赖问题,它在每台宿主机上都启动一个Docker的守护进程,守护进程屏蔽了与具体平台相关的信息,对上层应用提供统一的接口。这样Docker化的应用,就可以在多个平台下运行,Docker会针对不同的平台,解析给不同平台下的执行驱动、存储驱动和网络驱动去执行。达到:

Build once,Run anywhere,Configure once,Run anything.

Docker

如果把Docker当做一个独立的软件来看,它就是用Golang写的开源程序,采用C/S架构,包含Docker Server和Docker Client。docker生态包括两个部分:Docker仓库和Docker自身程序。

Docker仓库

官方的Docker仓库地址为:https://hub.docker.com,上面的应用非常丰富,既有各大公司打包的应用,也有大量个人开发者提供的应用。

Docker自身程序

Docker运行在Linux操作系统之上,属于用户态程序,通过一些接口和内核交互。Docker Daemon作为Server端,在宿主机上以后台守护进程的形式运行。Docker Client使用比较灵活,既可以在本机上以bin命令的形式发送指令,也可在远端通过RESTful API的形式发送指令;Docker的Server端接受指令并把指令分解为一系列任务去执行。

Docker工作流程

一图胜千言:

可以理解为github版的应用部署解决方案。

Docker分层

首先,用户的需求是:把软件运行起来,至于如何安装软件、软件运行在什么操作系统上,用户不太关心。就把软件和它依赖的环境(操作系统、共享库)、配置文件打包在一起,以虚拟机的形式放到官方库。但是,这样存在一个问题:我们不需要每次安装软件都带上它依赖的操作系统,因此引入了分层。比如操作系统是第一层,依赖库和第三方软件是第二层,应用的软件包和配置文件是第三层。如果两个应用有相同的底层,就可以共享这些库。例如下图中应用A和B需要的操作系统一样,就可以共享这一层。

但是这样共享层存在冲突问题,这时候就要规定他们的优先级,一般下层和上层有相同的文件和配置时,上层覆盖下层,数据以上层数据为主。我们给每个应用一个优先级最高的空白层,如果需要修改下层文件,就把这个文件拷贝到这个空白层即可。如下图:

这样从应用A的角度来说,文件已经修改成功,而从应用B的角度来看,文件没发生任何变化。这就是Docker的分层和写时拷贝策略

镜像和容器

主流的虚拟机一般比较笨重(需要消耗大量的系统资源,如:CPU、内存),因此我们最好使用容器类虚拟机(例如OpenVZ、VServer、LXC),这是一种内核虚拟化技术,与宿主机运行在相同的Linux内核,不需要指令级模拟,性能消耗非常小,是非常轻量级的虚拟化容器。Docker使用的就是LXC(后来推出了libcontainer)。

接下来就讨论镜像和容器:在Docker的官方仓库里只有一些完整的文件系统和程序包,没有动态生成新文件的需求,此为镜像(image)。把镜像下载到宿主机对外提供服务时,有可能需要修改文件(比如输出新日志到日志文件中),此为容器。

仓库中的应用都是以镜像的形式存在的,把镜像从Docker仓库拉到本机,以这个镜像为模板启动应用就叫容器。这是Docker最核心的两个概念,所有的指令和文档都是围绕镜像和容器展开的。

Docker变更管理

举个栗子,现有一个应用的Docker镜像,V1.0版本有三层。
接下来需要做如下修改:

  • 修改位于第一层的文件A。
  • 删除位于第二层的文件B。
  • 新建一个文件C。

Docker会新建一个第四层,针对上面的修改,它的处理方法如下:

  • 把第一层的文件A拷贝到第四层,修改文件A的内容。
  • 在第四层把文件B设置为不存在。
  • 在第四层创建以个文件C。

这时候版本就变为了V1.1,我们发布到Docker仓库时,只需要把第四层上传到仓库即可。

参考

[^1]: 《循序渐进学Docker》李金榜,尹烨等. 编著