Docker 的优势非常明显, 尤其是对于开发者来说, 它提供了一种全新的软件发布机制. 也就是说使用 docker 镜像作为软件产品的载体, 使用 docker 容器提供独立的软件运行上下文环境, 使用 docker hub 等提供镜像的集中管理, 这其中最重要的是使用 Dockerfile 定义容器的内部行为和关键属性来支持软件运行. 但是实际的生产环境往往需要定义数量庞大的 docker 容器, 并且容器之间具有错综复杂的联系. 手动的记录和配置这些复杂的容器关系, 不仅效率低下而且容易出错. 所以, 我们迫切需要一种像 Dockerfile 定义 docker 容器一样能够定义容器集群的编排和部署工具. 于是, Docker Compose 出现了(其实应该说 Fig 出现了, docker 收购了 Fig 并改名为 compose).
Dockerfile 重现一个容器, compose 则用来重现容器的集群.
说明: 本文的演示环境为 Ubuntu 16.04.
编排和部署
编排(orchestration)
编排指根据被部署的对象之间的耦合关系, 以及被部署对象对环境的依赖, 制定部署流程中各个动作的执行顺序, 部署过程所需要的依赖文件和被部署文件的存储位置和获取方式, 以及如何验证部署成功. 这些信息都会在编排工具中以指定的格式 (比如配置文件或特定的代码) 来要求运维人员定义并保存起来, 从而保证这个流程能够随时在全新的环境中可靠有序地重现出来.
部署(deployment)
部署是指按照编排所指定的内容和流程, 在目标机器上执行环境初始化, 存放指定的依赖文件, 运行指定的部署动作, 最终按照编排中的规则来确认部署成功.
所以说, 编排是一个指挥家, 他的大脑里存储了整个乐曲此起彼伏的演奏流程, 对于每一个小节每一段音乐的演奏方式都了然于胸. 而部署就是整个乐队, 他们严格按照指挥家的意图用乐器来完成乐谱的执行. 最终, 两者通过协作就能把每一位演奏者独立的演奏通过组合, 重叠, 衔接来形成高品位的交响乐. 这也是 docker compose 要完成的使命.
Compose 原理
笔者在前文《Docker Compose 简介》中演示了官方的示例, 本文不再赘述, 接下来我们去探索 compose 的工作原理. 先来了解两个 compose 中常常提及的概念:
project
通过 docker compose 管理的一个项目被抽象称为一个 project, 它是由一组关联的应用容器组成的一个完整的业务单元. 简单点说就是一个 docker-compose.YAML 文件定义一个 project.
我们可以在执行 docker-compose 命令时通过 -p 选项指定 project 的名称, 如果不指定, 则默认是 docker-compose.YAML 文件所在的目录名称.
service
运行一个应用的容器, 实际上可以是一个或多个运行相同镜像的容器. 可以通过 docker-compose up 命令的 --scale 选项指定某个 service 运行的容器个数, 比如:
$ docker-compose up -d --scale Redis=2
了解了上面的基本概念之后, 让我们一起看看 compose 的一次调用流程:
右上角的 docker-compose 定义了一组 service 来组成一个 project, 通过 docker-compose.YAML 中 service 的定义与 container 建立关系(service 与容器的对应关系), 最后使用 container 来完成对 docker-py(Python 版的 docker client) 的调用, 向 docker daemon 发起 http 请求. 注意, 这里的 project, service 和 container 对应的都是 docker-compose 实现中的数据结构. 下面让我们结合上图来介绍 docker-compose 工作的大致流程.
首先, 用户执行的 docker-compose up 命令调用了命令行中的启动方法, 功能非常简单. 一个 docker-compose.YAML 文件定义了一个 project,docker-compose up 提供的命令行参数则作为这个 project 的启动参数交由 project 模块处理.
然后, 如果当前宿主机已经存在与该应用对应的容器, docker-compose 则进行行为逻辑判断. 如果用户指定可以重新启动已有服务, docker-compose 就会执行 service 模块的容器重启方法, 否则就直接启动已有容器. 这两种操作的区别在于前者会停止旧的容器, 创建并启动新的容器, 并把旧容器移除掉. 在这个过程中创建容器的各项自定义参数都是从 docker-compose up 命令和 docker-compose.YAML 中传入的.
接下来, 启动容器的方法也很简洁, 这个方法中完成了一个 docker 容器启动所需的主要参数的封装, 并在 container 模块执行启动.
最后, contaier 模块会调用 docker-py 客户端来执行向 docker daemon 发起创建容器的 POST 请求.
由此可见 docker-compose 工作的整体流程非常清晰, 简洁!
重新启动 services
前面我们提到当前宿主机已经存在与该应用对应的容器, docker-compose 会进行判断并决定是否重新启动已有服务. 下面我们就通过 demo 来演示几个常见的场景(我们依然使用前文中提到的官方 demo).
强制 recreate
Recreate 就是删除现有的容器并且重新创建新的容器, 为 docker-compose up 命令指定 --force-recreate 选项可以强制 recreate 容器:
创建个别容器
如果应用中的个别 service 对应的容器被删除了, docker-compose up 命令会新建相关的容器:
启动个别容器
与上面类似, 如果应用中的个别 service 对应的容器被停止 (stop) 了, docker-compose up 命令会重新启动相关的容器:
总结
Docker-compose 总体上给人的感觉是并不复杂. 本文只是从概览的角度梳理了一遍 docker-compose 的整体执行流程, 主要目的是理解它的工作原理. 至于相关的使用技巧等细节, 笔者会在接下来的文章中进行介绍.
参考:
《Docker 容器和容器云》
来源: https://www.cnblogs.com/sparkdev/p/9787915.html