精通Docker第三版 – 第五章 Docker Compose

Coding Alan 6年前 (2019-03-23) 7564次浏览 1个评论 扫描二维码

《精通Docker第三版》完整目录:

第一章 Docker概览
第二章 创建容器镜像
第三章 存储和发布镜像
第四章 管理容器
第五章 Docker Compose
第六章 Windows容器
第七章 Docker Machine
第八章 Docker Swarm
第九章 Docker和Kubernetes
第十章 在公有云上运行Docker
第十一章 Portainer – 一个Docker的GUI
第十二章 Docker安全
第十三章 Docker工作流
第十四章 Docker进阶

本章中我们会来看另一个核心Docker工具,称为Docker Compose,还有一个当前在开发中的Docker App。我们会将本章分解成如下小节:

  • Docker Compose的介绍
  • 第一个Docker Compose应用
  • Docker Compose YAML文件
  • Docker Compose命令
  • Docker App

技术准备

和此前章节一样,我们将继续使用本地Docker安装。同样,本章中的截图都取自我个人偏好的操作系统,macOS。

不论你是在哪个平台上安装的Docker,我们所运行的Docker命令可以在三种操作系统中运行。但是,有极少数的支持命令,可能只能在macOS或基于Linux的操作系统上运行。

本章中所使用的完整代码请参见:https://github.com/alanhou/mastering-docker。

访问如下视频来查看代码实时操作:

Docker Compose简介

第一章 Docker概览中,我们讨论了设计Docker所解决的一些问题。并讲解了如何处理这些挑战,如在一个容器中以两个隔离的进程同时运行两个应用,也即在相同主机上可以运行相同软件栈的两个完全不同的版本,如PHP 5.6和PHP 7,这个我们在第二章 创建容器镜像中已经实现了。

第四章 管理容器的最后,我们启动了一个由多个容器组成的应用,而不是在单容器中运行所需的软件栈。我们所启动的示例应用,Moby计数器,是由Node.js编写并使用Redis作为后台键值对的存储,在该例中存储的是屏幕中Docker logo的位置。

这表示我们应启动两个容器,一个给应用一个给Redis。虽然因为应用本身非常基础这么做非常的简单,手动启动各容器存在很多的劣势。

例如,如果我们想要同事部署相同的应用,需要将如下命令发送给他们:

好吧,我可以省略掉前两条命令,因为在运行时会拉取未拉取的镜像,但在应用变得越来越复杂的时候,我就要发送不断增长的命令、指令集。

我还要说清楚他们应该按照相应的顺序执行这些命令。此外,我要包含解决可能出现的潜在问题的细节,这时我们会发现自己处于“运维躺枪”的境地,而这正是我们所竭力避免的。

虽然Docker的责任在于创建镜像并使用这些镜像来启动容器,人们把它看作一种阻止我们找寻自我的技术。借助于Docker, 人们不再需要担心所启动的应用的环境不一致性,因为这些都打包到镜像中了。

基于这一原因,在2014年7月,Docker购买了一家英国初创公司Orchard Laboratories,它提供两种基于容器的产品。

其中第一款产品为基于Docker主机托管平台:可将其看作Docker Machine的混合产品,本章稍后我们会来学习它,以及Docker自身。通过一个单独的命令orchard,你可以启动一个主机、然后通过一个新启动的主机代理你的Docker命令。例如,可以使用如下命令:

这些命令将在Orchard平台上启动一个Docker主机以及一个Redis容器。

第二款产品是一个名为Fig的开源项目。Fig让我们可以使用YAML来定义多容器应用的结构。然后它会读取YAML文件并按照定义自动启动这些容器。这样的优势在于因其是一个YAML文件,对于开发人员而言,在他们的代码库中将fig.yml文件和Dockerfile一起传递简单直接。

就这两款产品,Docker购买Orchard Laboratories意在Fig。此后一段时间,Orchard服务停止,并用于2015的2月,Fig变为了Docker Compose。

它作为 Mac和Windows上 Docker 安装的一部分,以及第一章 Docker概览中Linux上Docker的安装中我们安装了Docker Compose。我们暂不深入讨论它能做什么,先使用Docker Compose来启动上一章中手动启动的两个容器应用。

第一个Docker Compose应用

已经讨论过Docker Compose使用YAML文件,通常命名为dockercompose.yml,来定义多容器应用是什么样的。我们在第四章 管理容器中启动中的双容器应用Docker Compose的形式如下:

即便不逐行进行分析,以上的代码应该已经很直白了。要启动应用,我们只需进入包含docker-compose.yml文件的目录并运行如下命令:

在如下的Terminal输出中可以看到,在启动时发生了许多事:

可以看到,通过前面的几行,Docker Compose执行了如下操作:

  • 如我们在docker-compose.yml文件的最后定义的那样,它使用默认驱动器创建了一个名为mobycounter_redis_data的数据卷。
  • 使用默认网络驱动创建了一个名为mobycounter_default的网络,我们并没有要求Docker Compose这么做。这个问题一会儿即见分晓。
  • 它启动了两个容器,一个名为mobycounter_redis_1,另一个名为mobycounter_mobycounter_1。

你可能还看到这个多容器应用的Docker Compose命名空间以mobycounter作为前缀。它取自我们存放mobycounter文件的目录名。

一旦启动,Docker Compose与mobycounter_redis_1和mobycounter_mobycounter_1进行连接并将输出内容流向Terminal会话中。在Terminal屏幕中,你可以看到redis_1和mobycounter_1开始和彼此进行了交互。

使用docker-compose up来运行Docker Compose时,它会在前台运行。按下Ctrl + C会停止容器并将访问返回到Terminal会话中。

Docker Compose YAML文件

在更深入使用Docker Compose之前,我们先来深入分析docker-compose.yml文件,因其是Docker Compose的核心。

ℹ️YAML是一个递归的首字母缩写(YAML Ain’t Markup Language)。很多应用都使用它作配置同时让定义的结构化数据格式更易于人类阅读。本例中你看到的缩进非常之重要,因为它定义了数据的结构。

Moby计数器应用

我们用于启动多容器应用的docker-compose.yml文件分成三个部分。

第一部分仅用于指定我们用于定义Docker Compose 语言的版本,本例中,我们使用Docker和Docker Compose最近的一个版本,使用了版本号3:

下一部分中定义了容器,该部分为services。格式如下:

我们的示例中定义了两个容器。我将两者分开了以易于阅读:

定义服务的语法和使用docker container run命令启动容器时非常相近。我说相近是因为虽然在阅读定义时很容易理解 ,只有在仔细查看时才会了解到Docker Compose和 docker container run命令之间有很多不同。

例如,在运行docker container run命令时没有针对如下的标记:

  • image;这告知Docker Compose下载和使用哪个镜像。在运行ocker container run 时并没有这个选项,因为在命令行中你只能运行一个容器。在前面章节中已经看到,镜像总是在命令行最后进行定义而无需传递一个标记。
  • volume:这和–volume标记是一样的,但是它能接收多个数据卷。它只会使用在Docker Compose YAML文件中声明的数据卷,一会儿就会了解到更多。
  • depends_on:这在docker container run命令中永远不会生效,因为该命令仅针对单个容器。在Docker Compose中,depends_on用于帮助构建一些逻辑来组织各容器启动的顺序。例如,仅在成功启动容器 A 之后才启动容器 B。
  • ports:这基本上是–publish标记,可接收一系列端口。

唯一在运行 docker container run时存在的等价标记是:

  • restart:这与–restart相同并接收相同的输入。

我们的Docker Compose YAML文件最后一部分是声明我们的数据卷:

示例投票应用

已经提到过Moby计数器应用的 Docker Compose文件是一个非常简单的示例。我们来看一个更为复杂的Docker Compose并了解如何引入构建容器以及多个网络。

在本书的 Git 仓库中,你可以在Chapter05目录中找到一个名为example-voting-app的文件夹。这是从官方的Docker示例仓库中复制的投票应用。

打开docker-compose.yml文件可以看到该应用由5个容器、2个网络和一个数据卷组成。现在先忽略其它文件,我们将在后续的章节中进行学习。先来看看docker-compose.yml中发生了什么:

可以看到,开头处通过简单地定义版本,然后列出一系列services。第一个容器名为vote:它是一个Python应用,允许用户提交投票。从下面的定义可以看出,这里并没有下载一个镜像,而是使用build代替 image 命令从头开始构建一个镜像:

这里的build指令告诉Docker Compose使用Dockerfile构建一个容器,该文件可在./vote下找到。Dockerfile文件本身对于Python应用的表述非常直接。

一旦启动了容器,接着我们会将宿主机的./vote文件挂载到容器中,这通过传递想要挂载的文件夹路径以及想要挂载的容器来实现的。

我们告诉容器在启动时运行python app.py。并将宿主机上的5000与容器中的80端口进行了映射,最后我将附加了两个网络到容器,一个名为front-tier,另一个名为back-tier。

front-tier包含将端口映射到宿主机的容器,back-tier预留用于无需暴露端口以及私有隔离网络运行的容器。

接下来,我们有另一个与front-tier网络相连接的容器。这个容器显示了投票的结果。result容器包含一个Node.js应用,它与一会儿会说到的PostgreSQL数据库相连接,因投票实时反馈到投票容器中它显示实时的结果。和vote容器一样,该镜像使用./result文件夹下的Dockerfile在本地进行构建。

我们暴露的端口是5001,通过它我们可以连接并查看结果。接下来也是最后一个应用容器名为worker。

worker容器运行一个.NET应用,它的唯一任务是连接Redis并通过将其传送给运行在名为 db 的容器上的PostgreSQL数据库来注册每个投票。该容器同样是使用Dockerfile来进行构建,但这次不是传递Dockerfile所存放文件夹路径,我们使用了context。它设置了docker构建的工作路径并允许我们定义额外的选项如标签和修改Dockerfile的名称。

因为这个容器仅仅是连接redis和db容器,它也就无需暴露任何端口来被直接连接,它也不需要与运行在front-tier网络中的任一容器进行通讯,也就是说我们只需将其添加到back-tier网络中即可。

那么,现在我们就有了投票应用,从终端用户注册投票并将它们发送到redis容器,在那里投票由worker容器进行处理。service中的 redis 容器定义如下:

该容器使用官方的Redis镜像并且不从Dockerfile进行构建,我们要确保6379端口可用,但仅在back-tier网络中。我们还指定了容器的名称,通过container_name将其设置为redis。这是为了避免考虑由Docker Compose生成默认的名称,如果你还记得的话,Docker Compose会使用文件夹名来在它们自己的命名空间中启动容器。

紧接着的也是最一个容器是PostgreSQL容器,我们前面已提到其名称为db:

可以看到,它与redis容器极其相似,使用了官方镜像,但是你也会注意到并没有暴露任何端口,因为这是官方镜像的一个默认选项。我们还指定了容器的名称。

因其是存储投票的地方,我们将创建并挂载一个数据卷来作为PostgreSQL数据库的持久化存储:

最后是我们一直有提到的两个网络:

运行docker-compose up会输出很多启动时正在发生的反馈,第一次启动应用大约会花费5分钟。如果你按照本文操作并启动了自己的应用,接下来的就是启动时的缩减版本。

小贴士:你可能会得到npm ERR! request to https://registry.npmjs.org/nodemon failed, reason: Hostname/IP doesn’t match certificate’s altnames的错误。这时,使用有权限用户运行echo “104.16.16.35 registry.npmjs.org” >> /etc/hosts命令来写入/etc/hosts。

首先会创建网络并准备好容器要使用的数据卷:

然后会构建投票容器镜像:

一旦这一投票镜像已构建,就开始创建worker镜像:

然后拉取了 redis 镜像:

以下为db容器拉取了PostgreSQL镜像:

接下来是重头戏了,开始构建结果镜像了。 Node.js输出较繁琐,你看在屏幕上看到很多Dockerfile中npm区域执行的输出,事实上,会有超过250行的输出:

该应用的result部分可通过http://localhost:5001进行访问。默认是没有投票的,被分成了50/50:

精通Docker第三版 – 第五章 Docker Compose

译者注:如使用官方的源码以上页面会因为 AngularJS的导入而出现空白的显示,需修改result/views/index.html中对应的内容

应用的投票部分可通过http://localhost:5000进行访问:

精通Docker第三版 – 第五章 Docker Compose

点击 CATS或DOGS都会登记投票,你应该可以在Terminal中看到如下的Docker Compose日志输出:

精通Docker第三版 – 第五章 Docker Compose

上面有一些错误,因为Redis的表结构仅在投票应用登记了第一个投票后才会创建, 一旦投了一票后,就会创建Redis表结构,worker会接收该投票并通过写入db容器来对其进行处理。投票完成后,result容器会实时进行更新:

精通Docker第三版 – 第五章 Docker Compose

在后续章节学习Docker Swarm 栈和Kubenetes集群时我们会再来看Docker Compose的YAML文件。这里我们先回到Docker Compose并来了解一些我们可以运行的命令。

Docker Compose命令

本章已过半,我们只用到了一个Docker Compose命令docker-compose up。如果你照着操作,这时运行docker container ls -a命令,会在终端中看到类似如下的信息:

可以看到,这里有很多状态为EXITED的容器。这是因为在我们使用Ctrl + C返回到Terminal时,Docker Compose的容器就被停止了。

选择一个Docker Compose应用并进入包含docker-compose.yml文件的对应目录,我们会一起来使用更多的Docker Compose命令。这里我将使用Example Vote应用。

up和 ps

第一个命令是docker-compose up,但这次我们会添加一个标记。在你所选择的应用文件夹中,运行如下命令:

这会再次以非连接的方式启动之前的应用:

在返回到Terminal之后,你可以使用如下命令查看运行中的容器:

可以在如下终端输出中可以看到,所有容器的状态都是Up

在运行这些命令时,Docker Compose只知道在docker-compose.yml文件的service区中定义的容器,所有其它不属于服务栈的容器都会被忽略。

config

运行如下命令会验证我们的docker-compose.yml文件:

如果不存在问题,会在屏幕上打印出Docker Compose YAML文件中已渲染的一份拷贝,这是Docker Compose解释这一证件的方式。如果你不想要看到这一错误,只是是要检查错误,可以运行如下命令:

这是–quiet的简写。我们所使用的示例文件中并不存在任何错误,如果有其它错误,则会像如下内容这样显示:

pull, build和 create

接下来的两条命令可以帮助我们来对Docker Compose应用的启动来做准备。下面这条命令会读取我们的Docker Compose YAML文件并拉取它所读取的镜像:

如下命令会对在我们的文件中所找到的指令执行build操作:

这些命令对于你初次定义基于Docker Compose的应用而又不通过启动应用来进行测试时非常有用。docker-compose build命令可用于在原始构建镜像的Dockerfile有更新时触发新的构建。

pull和build命令仅仅是生成/拉取应用所需的镜像,它们本身不会去配置容器。需要使用如下命令来进行配置:

该命令会创建但不启动容器。它与docker container create命令操作的方式相同,会在启动容器前处于exited的状态。对create命令可以传递几个用得到的标记:

  • –force-recreate: 它会重新创建容器,哪怕是在配置文件未发生修改而无需重建
  • –no-recreate: 在容器已存在时不进行重建,该标记不能与前一个标记同时使用
  • –no-build: 它不会重建镜像,哪怕是需要构建的镜像并不存在
  • –build: 它会在创建容器之前构建镜像

start, stop, restart, pause,和unpause

以下命令和docker container对应的命令的功能相同,唯一的区别是它们作用于所有的容器的修改。

也可以通过传递名称来定位单个服务,例如,对db服务进行暂停和取消暂停,我们可以运行如下命令:

top, logs和events

接下来的三个命令全部用于显示运行中容器及Docker Compose中所发生状况的反馈信息。

下面一条命令,我对应的docker container命令一样,显示由Docker Compose所启动的每个容器运行的进程信息:

可以从如下的Terminal输出中看到,每个容器都被分割为独立的部分:

如果你仅想要看单个服务的信息,只需在运行该命令时传递服务的名称即可:

下一条命令将每一个运行中的容器的日志输出到屏幕中:

和docker container命令类似,你可以传递-f或–follow等标记来保持日志持续输出直至按下Ctrl + C为止。同样你可以通过将名称放到命令之后来仅打印单个服务的日志:

events命令也是和docker container对应命令类似,它输出事件,比如由其它我们所讨论的命令实时触发的事件。例如,运行如下命令:

新打开一个窗口运行docker-compose pause命令即可获得如下输出:

这两条命令与docker container对应的命令运行相似。运行如下命令:

译者注:记得先执行 docker-compose unpause

这会在运行中的worker容器中会启动一个新的进程来对db容器执行3次ping操作,如下所示:

run命令对于在应用内一次性地运行容器化命令时非常有用。例如,如果你要使用composer等包管理器来更新存储在数据卷中的项目的依赖,可以运行如下命令:

这会以install命令运行composer容器并将data_volume挂载到容器中的/app。

scale

scale命令会接收向其传递的服务来按所定义的数量来对服务进行扩充操作,比如,要添加更多的worker容器,只需要运行如下命令:

但这会给出如下的警告:

现在我们应当使用的命令如下:

虽然scale命令来当前版本的Docker Compose中仍然可用,会在该软件的未来版本中进行删除。

你会注意到我选择对worker容器进行扩充。这是有原因的,如果尝试运行如下命令就会明白了:

你会注意到虽然Docker Compose创建了两个额外的容器,它们未能启动并报出如下错误:

这是因为我们不能将三个独立的容器映射到相同的端口上。有规避的方式,我们将在后面的章节中了解更多细节。

kill, rm和down

我们最后来看的这三条Docker Compose命令用于删除/终止Docker Compose应用。第一个命令通过停止运行中容器进程来停止运行中的容器。即 kill 命令:

运行这一命令时要非常小心,因为它不会像在运行docker-compose stop时那样等待容器正常的停止,也就是说使用docker-compose kill命令有可能会导致数据损失。

接着是rm命令,这会删除状态为exited的所有容器:

最后一个命令是down命令。你可能已经猜到了,它的效果与运行docker-compose up的效果相反:

这会删除由docker-compose up所创建的容器和网络。如果想要删除所有,可以通过运行如下命令:

这会删除所有你在运行docker-compose up命令时产生的容器、网络和镜像(包含拉取和构建的),包括Docker Compose 应用之外还在用的镜像。但如果镜像在使用中,会触发一个错误且不会被删除:

在以上的输出中可以看到,还有另一个容器Moby计数器应用在使用redis镜像,因此没有被删除。但是其它Example Vote应用所使用的镜像都被删除了,包含docker-compose up所构建的镜像和从Docker Hub上所下载的镜像。

Docker App

在开始这一节之前,我应该发布一条警告:

我们将要讨论的功能还处于实验阶段。它还处于开发的早期阶段,仅用作未来功能的一个预览。

基于这一原因,我只会讲在macOS版本的安装。但在安装前,我们来讨论下Docker App究竟是什么。

虽然Docker Compose文件在向其他人分享时非常有用,你可能注意到本章至此缺失了一个非常重要的元素,那就是不能像Docker镜像那样发布Docker Compose文件。

Docker认识到了这一问题,并在开发一个新功能,称为Docker App,旨在解决这一问题。

Docker App是一个自包含的二进制,有助于你创建可通过Docker Hub或Docker企业仓库分享的应用包。

小贴士:我看建议查看GitHub项目的Releases页面(你可以在扩展阅读部分找到这一链接)来确保你使用的是最新版本。如果版本晚于0.4.1,你需要在下面的命令中替换掉版本号。

要在macOS上安装Docker App,你可以运行如下命令来先设定要下载的版本号:

译者注:当前翻译时 Docker App 的最新版本为 v0.6.0,已不再支持 save, ls 等命令,因此采用原书相同版本进行的操作

现在你已经有了正确的版本,可以开始使用如下命令拼接并进行下载了:

完成以上操作,你就可以运行如下命令在屏幕上打印该二进制文件的基本信息了:

没有跟着操作的读者可以在下面看到以上命令的完整输出:

我们使用的docker-compose.yml文件要做些微调。版本号由3升级为3.6 。不这样做会导致如下错误:

我们要运行的也是产生以上报错的命令如下:

该命令接收我们的docker-compose.yml文件并将其嵌入.dockerapp文件。一开始该文件中会有一些注释详细描述进入到下一步之前你需要做的修改。我们在仓库里放了一个未修改版本的文件,在Chapter5/mobycounter-app 文件夹下,名为mobycounter.dockerapp.original。

mobycounter.dockerapp文件编辑后的版本如下所示:

可以看到,它分成了3个部分,第一部分包含应用的元数据,如下所示:

  • version:这是将要发布到Docker Hub上的应用版本号
  • name:在Docker Hub上所显示的应用名
  • description:应用的简短描述
  • namespace:这是你的Docker Hub用户名或者你有授权的组织名
  • maintainer:一组应用的维护人员

第二部分包含我们的Docker Compose文件。你可能注意到有几处用变量进行了替换。在本例中,我们用${port}替换了端口8080。变量port的默认值在最后一个部分中进行了定义。

完成了.dockerapp文件之后,你就可以运行如下命令来将这一Docker App保存为镜像:

我以运行如下命令来查看本机中活跃的Docker App:

因Docker App基本上是封装在标准Docker镜像中的一些元数据,你也可以通过运行命令来进行查看:

如果你没有照着操作的话,可以查看终端中的输出结果如下:

运行如下命令会显示该Docker App的概要信息,与通过docker image inspect来查看构建镜像的细节方法相似:

可以从如下的终端输出中看到,运行 docker-app inspect比docker image inspect命令输出的信息更为友好:

我们已完成了应用,现在需要将其推送到Docker Hub上。要实现推送,只需要运行如下命令:

这表示我们的应用已经在Docker Hub上进行了发布:

精通Docker第三版 – 第五章 Docker Compose

那么, 如何获取该Docker App呢?首先我们需要删除本地镜像。运行如下命令来实现删除:

删除后进入另一个目录:

现在,让我们下载这一Docker App,对端口做出修改,并进行启动:

同样,如果有读者没有照着操作,可在以下终端输出查看前述命令运行效果:

可以看到,我们甚至无需手动下载这个Docker App镜像,就启动并运行了该应用。访问http://localhost:9090/应该会显示邀请你点击来添加logo的页面。

对于这种常规的前面运行的Docker Compose应用,按下Ctrl + C就可以返回terminal。

你可以运行如下命令来与应用进行交互或终止应用:

Docker App中还有更多的功能。但我们现在还不适合讲解更多的细节。在第八章 Docker Swarm第九章 Docker和Kubernetes中我们会再讲到Docker App。

正如在这一部分前面所提到的,这一功能还处于开发的早期阶段,我们这里讨论的命令和功能在未来都有可能发生变化 。但虽然还处于早期阶段,我希望读者能看到Docker App的优势以及它是如何基于Docker Compose的坚实基础来进行构建的。

总结

希望读者能喜欢这一章有关Docker Compose的内容,也希望大家能像我一样,看到它在进化成为非常有用的第三方工具并成为Docker核心体验的一个重要组成部分。

Docker Compose引入了一些有关运行和管理我们的容器的关键概念。我们会在第八章 Docker Swarm第九章 Docker和Kubernetes中更进一步的了解这些概念。

下一章中,我们将暂时告别基于Linux平台的容器,短暂地学习Windows容器。

课后问题

  1. Docker Compose文件使用哪种开源格式?
  2. 在我们初始的Moby计数器Docker Compose文件中,哪个标记和Docker命令行对应的标记功能相同?
  3. 是非题:通过Docker Compose文件你只能使用来自Docker Hub的镜像吗?
  4. 默认情况下,Docker Compose如何决定要使用的命名空间?
  5. 为docker-compose up添加哪一个标记来在后台启动容器?
  6. 对Docker Compose文件执行语法检查最好的方式是什么?
  7. 说明Docker App如何运行的基本原理。

扩展阅读

有关果园实验室(Orchard Laboratories)的更多信息,如下:

  • 果园实验室官网: https://www.orchardup.com/
  • 果园实验室加入Docker:https://blog.docker.com/2014/07/welcoming-the-orchard-and-fig-team

有关Docker App项目的更多信息,参见:

最后,有关我们未讲解到的一些主题的扩展链接:

 

喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(1)个小伙伴在吐槽
  1. 打卡+1 :grin:
    wing2019-09-30 14:53 回复