原文:
annas-archive.org/md5/662046f678252f1c36403502957951fd
译者:飞龙
前言
在过去的几年里,Docker 已成为最令人兴奋的新技术之一。无论是企业公司还是初创公司,都纷纷采纳了这一工具。
多个第一方和第三方工具已被开发用于扩展核心 Docker 功能。本书将指导你安装、配置和使用这些工具,并帮助你了解哪些工具最适合某项任务。
本书内容简介
第一章,扩展 Docker 入门,讨论了 Docker 及其解决的一些问题。我们还将讨论如何通过扩展核心 Docker 引擎来获得更多功能。
第二章,引入第一方工具,介绍了 Docker 提供的工具,用于与核心 Docker 引擎协同工作。这些工具包括 Docker Toolbox、Docker Compose、Docker Machine 和 Docker Swarm。
第三章,存储插件,介绍了 Docker 插件。我们将从 Docker 自带的默认存储插件开始,接着介绍三个第三方插件。
第四章,网络插件,解释了如何在多个 Docker 主机间扩展容器网络,无论是本地部署还是在公共云中。
第五章,构建你自己的插件,介绍了如何最佳地编写自己的 Docker 存储或网络插件。
第六章,扩展你的基础设施,介绍了如何使用一些成熟的 DevOps 工具来部署和管理你的 Docker 主机和容器。
第七章,调度器分析,讨论了如何部署 Kubernetes、Amazon ECS 和 Rancher,跟随前面的章节内容。
第八章,安全性、挑战与结论,帮助解释从何处部署 Docker 镜像的安全隐患,并回顾前面章节中讨论过的各种工具及其最佳应用场景。
本书所需内容
你需要一台能够运行 VirtualBox(www.virtualbox.org/
)的 OS X 或 Windows 笔记本电脑或桌面电脑,并且能够访问 Amazon Web Service 和 DigitalOcean 账户,且拥有启动资源的权限。
本书适合谁阅读
本书面向开发人员和系统管理员,他们觉得基础的 Docker 安装已经无法满足需求,想通过扩展核心 Docker 引擎的功能来进一步优化配置,以应对不断变化的业务需求。
约定
在本书中,您将看到多种文本样式,用以区分不同种类的信息。以下是这些样式的几个示例以及它们的含义说明。
文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 账号等,均以如下方式呈现:“安装完成后,您应该能够通过运行 Docker hello-world
容器来检查是否一切正常。”
代码块如下所示:
### Dockerfile
FROM php:5.6-apache
MAINTAINER Russ McKendrick <russ@mckendrick.io>
ADD index.php /var/www/html/index.php
当我们希望引起您对代码块中特定部分的注意时,相关的行或项目会以粗体显示:
version: '2'
services:
wordpress:
container_name: "my-wordpress-app"
image: wordpress
ports:
- "80:80"
environment:
- "WORDPRESS_DB_HOST=mysql.weave.local:3306"
- "WORDPRESS_DB_PASSWORD=password"
- "constraint:node==chapter04-01"
任何命令行输入或输出都会按如下方式写出:
curl -sSL https://get.docker.com/ | sh
新术语和重要单词以粗体显示。您在屏幕上看到的单词,例如在菜单或对话框中,都会像这样出现在文本中:“要继续安装的下一步,请点击继续。”
注意
警告或重要提示将以如上所示的框呈现。
提示
提示和技巧将以这样的方式呈现。
读者反馈
我们始终欢迎读者反馈。请告诉我们您对本书的看法——喜欢或不喜欢的地方。读者反馈对我们非常重要,它帮助我们开发出您真正能从中受益的书籍。
要向我们发送一般反馈,只需通过电子邮件 <feedback@packtpub.com>
联系我们,并在邮件主题中提到书籍标题。
如果您在某个主题上拥有专长,并且有兴趣撰写或贡献书籍,请参考我们的作者指南,网址为 www.packtpub.com/authors。
客户支持
现在,您已经成为一本 Packt 书籍的自豪拥有者,我们为您提供了许多帮助您最大化购买价值的资源。
下载示例代码
您可以从 www.packtpub.com
账户中下载本书的示例代码文件。如果您是从其他地方购买的此书,您可以访问 www.packtpub.com/support
并注册,文件会直接通过电子邮件发送给您。
您可以通过以下步骤下载代码文件:
-
使用您的电子邮件地址和密码登录或注册我们的网站。
-
将鼠标指针悬停在顶部的支持标签上。
-
点击代码下载和勘误。
-
在搜索框中输入书名。
-
选择您要下载代码文件的书籍。
-
从下拉菜单中选择您购买此书的来源。
-
点击代码下载。
您还可以通过点击书籍网页上的代码文件按钮来下载代码文件。您可以通过在搜索框中输入书名来访问此页面。请注意,您需要登录您的 Packt 账户。
文件下载完成后,请确保使用最新版本的工具解压或提取文件夹:
-
WinRAR / 7-Zip for Windows
-
Zipeg / iZip / UnRarX for Mac
-
适用于 Linux 的 7-Zip / PeaZip
本书的代码包也托管在 GitHub 上,链接为 github.com/PacktPublishing/ExtendingDocker
。我们还提供了其他来自我们丰富图书和视频目录的代码包,您可以在 github.com/PacktPublishing/
查看。快去看看吧!
勘误
尽管我们已尽力确保内容的准确性,但错误有时还是会发生。如果您在我们的书籍中发现错误——可能是文本或代码中的错误——我们将非常感激您能报告此问题。这样,您不仅可以帮助其他读者避免困扰,还能帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问 www.packtpub.com/submit-errata
,选择您的书籍,点击 勘误提交表单 链接,输入勘误的详细信息。经验证后,您的勘误提交将被接受,并上传到我们的网站或添加到该书籍的勘误列表中。
要查看之前提交的勘误, 请访问 www.packtpub.com/books/content/support
,并在搜索框中输入书名。相关信息将显示在 勘误 部分。
盗版
网络上的版权材料盗版问题是各类媒体的持续问题。在 Packt,我们非常重视保护我们的版权和许可证。如果您在互联网上发现我们作品的任何非法复制版本,请立即提供其位置地址或网站名称,以便我们采取措施。
如果您发现涉嫌侵权的内容,请通过<copyright@packtpub.com>
联系我们,并附上涉嫌盗版材料的链接。
感谢您帮助保护我们的作者以及我们提供有价值内容的能力。
问题
如果您在本书的任何方面遇到问题,可以通过<questions@packtpub.com>
联系我们,我们将尽力解决问题。
第一章:扩展 Docker 简介
在本章中,我们将讨论以下主题:
-
为什么 Docker 会被整个行业广泛接受
-
一个典型的容器生命周期是什么样的?
-
接下来的章节将涵盖哪些插件和第三方工具?
-
接下来章节你需要什么?
Docker 的崛起
很少有技术能够在整个行业中如此广泛地被采纳。自 2013 年 3 月首次公开发布以来,Docker 不仅赢得了像你我这样的最终用户的支持,还得到了 Amazon、Microsoft 和 Google 等行业领袖的支持。
Docker 目前在其网站上使用以下句子来描述为什么你会想要使用它:
“Docker 提供了一套集成技术套件,使开发和 IT 运维团队能够在任何地方构建、交付和运行分布式应用程序。”
有一个基于灾难女孩照片的网络迷因,总结了为什么这样一个看似简单的解释实际上非常重要:
尽管 Docker 的描述听起来简单,但实际上,对于大多数开发人员和 IT 运维团队来说,它一直是一个理想的工具,可以确保一个应用程序在以下三个主要阶段的生命周期中始终如一地运行:
-
开发
-
预发布和预生产
-
生产
为了说明在 Docker 出现之前为什么这是一个问题,让我们看看这些服务传统上是如何配置和部署的。人们通常倾向于使用专用机器和虚拟机器的组合。让我们更详细地看一下这些。
尽管可以使用配置管理工具,如 Puppet,或者像 Ansible 这样的编排工具来保持服务器环境之间的一致性,但在服务器和开发者的工作站之间强制执行这些一致性仍然是困难的。
专用机器
传统上,这些是单个硬件设备,经过配置以运行你的应用程序。虽然这些应用程序可以直接访问硬件,但你受限于可以安装在专用机器上的二进制文件和库,因为它们必须在整个机器上共享。
为了说明 Docker 解决的一个潜在问题,假设你有一台单独的专用服务器运行你的 PHP 应用程序。当你最初部署这台专用机器时,构成你电子商务网站的三个应用程序都使用 PHP 5.6,因此在兼容性方面没有问题。
你的开发团队一直在缓慢地处理这三个 PHP 应用程序。你已经将它部署到主机上,以便让它们与 PHP 7 兼容,因为这将大大提升它们的性能。然而,他们始终无法解决 App2 中的一个错误,这意味着在用户将物品添加到购物车时,应用程序将无法在 PHP 7 下运行,并会崩溃。
如果你只有一个主机运行三款应用程序,直到开发团队解决 App2 的错误,你将无法从 PHP 5.6 升级到 PHP 7,除非你做以下其中一项:
-
部署一个运行 PHP 7 的新主机,并将 App1 和 App3 迁移到该主机上;这可能既费时又昂贵
-
部署一个运行 PHP 5.6 的新主机,并将 App2 迁移到该主机上;同样,这也可能既费时又昂贵。
-
等待直到错误被修复;从 PHP 5.6 升级到 PHP 7 所带来的性能提升可能会增加销售额,但目前没有修复的预计时间。
如果你选择前两个选项,你还需要确保新专用主机要么与开发者的 PHP 7 环境匹配,要么按完全相同的方式配置新专用主机;毕竟,你不希望因为机器配置不当而引入更多问题。
虚拟机
解决前面提到的情况的一种方法是,通过安装一个如以下的虚拟化管理程序,将专用主机的资源切割并提供给应用程序:
-
KVM:
www.linux-kvm.org/
-
XenSource:
www.xenproject.org/
-
VMware vSphere:
www.vmware.com/uk/products/vsphere-hypervisor/
一旦安装完成,你就可以在每个不同的虚拟主机上安装你的二进制文件和库,并在每个虚拟主机上安装你的应用程序。
回到专用主机部分提到的场景,你将能够在安装了 App1 和 App2 的虚拟机上升级到 PHP 7,同时在修复开发工作进行时保持 App2 不变且可正常工作。
很好,那么问题在哪?从开发者的角度来看,根本没有问题,因为他们的应用程序可以在适合他们的 PHP 版本下运行;然而,从 IT 运维的角度来看:
-
更多的 CPU、RAM 和磁盘空间:每个虚拟机都将需要额外的资源,因为运行三个来宾操作系统以及三款应用程序的开销需要考虑。
-
更多的管理:现在,IT 运维需要修补、监控并维护四台机器,包括专用主机和三台虚拟机,而之前他们只需要维护一台专用主机。
如前所述,你还需要确保托管应用程序的三个虚拟机的配置与开发人员在开发过程中使用的配置一致;同样,你不希望因为部门之间的配置和流程偏差而引入额外问题。
专用主机与虚拟机
下图展示了典型的专用主机与虚拟机主机配置方式:
如你所见,二者之间的最大区别非常明显。你正在进行资源利用与能够使用不同二进制文件/库运行应用之间的权衡。
容器
现在我们已经涵盖了传统应用部署的方式。接下来,让我们看看 Docker 为这个过程带来了什么。
回到我们的场景,三款应用运行在同一台主机上。你可以在主机上安装 Docker,然后将每个应用部署为容器,这样既能享受虚拟机的好处,又大大减少了占用空间,也就是完全去除了虚拟机管理程序和客户操作系统,取而代之的是一个直接与主机内核对接的精简界面。
这为 IT 运维和开发团队带来的优势如下:
-
低开销:如前所述,IT 运维团队的资源和管理负担较低
-
开发团队提供容器:与其依赖 IT 运维团队为每个应用配置三个不同的环境,将它们与开发环境对接,不如直接将容器交给运维团队进行生产部署。
从下面的图示可以看到,应用与主机操作系统之间的层次已被简化:
所有这些意味着,开篇提到的“灾难女孩”表情包应该不再适用了,因为开发团队已经将应用与所有配置、二进制文件和库一起打包成容器交给运维团队,这意味着如果在开发环境中能正常运行,那么在生产环境中也能运行。
这看起来可能太好了,甚至让人怀疑,老实说,确实有一个“但是”。对于大多数 web 应用或已经预编译的静态二进制文件应用,你应该不会遇到问题。
然而,由于 Docker 与底层主机共享资源,如内核版本,如果你的应用需要编译或依赖某些仅与共享资源兼容的库,那么你必须在相同的操作系统上部署容器,在某些情况下,甚至需要相同的硬件。
Docker 通过收购一家名为 Unikernel Systems 的公司,试图解决这个问题,收购发生在 2016 年 1 月。到写本书时,关于 Docker 如何将这一技术整合到其核心产品中的细节还不多。如果你想了解更多关于这一技术的信息,可以访问 blog.docker.com/2016/01/unikernel/
。
每个人都应该使用 Docker 吗?
那么,真的这么简单吗?每个人应该停止使用虚拟机,改用容器吗?
2014 年 7 月,Wes Felter、Alexandre Ferreira、Ram Rajamony 和 Juan Rubio 发表了一篇名为《虚拟机与 Linux 容器的性能更新比较》的 IBM 研究报告,并得出结论:
“虚拟机和容器都是成熟的技术,受益于十年的硬件和软件渐进式优化。通常,Docker 在我们测试的所有案例中都等同于或超越了 KVM 性能。我们的结果显示,KVM 和 Docker 都对 CPU 和内存性能引入了微不足道的开销(除了极端情况下)。对于 I/O 密集型工作负载,这两种虚拟化方式都应该谨慎使用。”
接着,它提到以下内容:
“虽然容器本身几乎没有开销,但 Docker 并非没有性能陷阱。Docker 卷比存储在 AUFS 中的文件有明显更好的性能。Docker 的 NAT 也会为高数据包率的工作负载引入开销。这些特性代表了管理简便性与性能之间的权衡,应根据具体情况进行考虑。”
这份完整的 12 页报告,详细对比了我们讨论过的传统技术与容器,可以从以下网址下载:
在 IBM 研究报告发布不到一年后,Docker 为其生态系统引入了插件。我看到的其中一个最佳描述来自 Docker 软件工程师 Jessica Frazelle,她形容这个发布就像是“包含电池,但可以替换”,意味着核心功能可以轻松地被第三方工具替代,这些工具可以用来解决 IBM 研究报告中的结论。
在撰写本书时,Docker 目前支持卷和网络驱动插件。未来将会增加更多插件类型,以将更多 Docker 核心功能暴露给第三方。
容器的生命周期
在我们查看各种插件和扩展 Docker 的方式之前,应该先了解容器的典型生命周期。
使用前一节中的示例,让我们启动官方的 PHP 5.6 容器,然后将其替换为官方的 PHP 7.0 容器。
安装 Docker
在我们启动容器之前,需要先让 Docker 正常运行;幸运的是,这个过程很简单。
在接下来的章节中,我们将使用 Docker Machine 来引导我们的 Docker 环境;但是现在,让我们先在云服务器上快速安装 Docker。
以下指令适用于托管在公共云上的 Ubuntu 14.04 LTS 或 CentOS 7 实例,示例如下:
-
Digital Ocean:
www.digitalocean.com/
-
亚马逊云服务:
aws.amazon.com/
-
微软 Azure:
azure.microsoft.com/
-
VMware vCloud Air:
vcloud.vmware.com/
你也可以尝试在本地运行一个虚拟机,使用以下方法:
-
Vagrant:
www.vagrantup.com/
-
Virtualbox:
www.virtualbox.org/
-
VMware Fusion:
www.vmware.com/uk/products/fusion/
-
VMware Workstation:
www.vmware.com/uk/products/workstation/
我将使用托管在 Digital Ocean 上的 CentOS 7 服务器,因为它方便快捷,可以快速启动并随时终止。
一旦你的服务器启动并运行,你可以通过运行以下命令从官方 Yum 或 APT 仓库安装 Docker:
curl -sSL https://get.docker.com/ | sh
如果你像我一样运行的是 CentOS 7 服务器,你需要确保该服务正在运行。为此,请输入以下命令:
systemctl start docker
安装完成后,你应该能够通过运行 Docker hello-world
容器来检查一切是否按预期工作,输入以下命令:
docker run hello-world
一旦你安装了 Docker,并确认它按预期运行,你可以通过运行以下命令来下载官方 PHP 5.6 和 PHP 7.0 镜像的最新版本:
docker pull php:5.6-apache && docker pull php:7.0-apache
有关官方 PHP 镜像的更多信息,请参阅 Docker Hub 页面:hub.docker.com/_/php/
。
现在我们已经下载了镜像,是时候部署我们的应用程序了,因为我们保持得非常简单;我们将要部署的仅仅是一个 phpinfo
页面,这将确认我们正在运行的 PHP 版本,以及容器环境的其他详细信息:
mkdir app1 && cd app1
echo "<?php phpinfo(); ?>" > index.php
现在,index.php 文件已经到位。让我们通过运行以下命令来启动 PHP 5.6 容器:
docker run --name app1 -d -p 80:80 -it -v "$PWD":/var/www/html php:5.6-apache
这将启动一个 app1
容器。如果你输入你的服务器实例的 IP 地址或解析到该地址的域名,你应该看到一个页面,显示你正在运行 PHP 5.6:
现在你已经成功运行了 PHP 5.6,让我们将其升级到 PHP 7。传统上,这意味着使用第三方 YUM 或 APT 仓库安装一组新的软件包;根据经验,这个过程可能有点不稳定,取决于你安装的 PHP 先前版本的软件包的兼容性。
幸运的是,在我们的案例中,我们正在使用 Docker,所以我们所需要做的就是终止 PHP 5.6 容器,并替换为运行 PHP 7 的容器。在这个过程中,任何时候你都可以使用以下命令检查正在运行的容器:
docker ps
这将打印出正在运行的容器列表(如本节末尾截图所示)。要停止并移除 PHP 5.6 容器,请运行以下命令:
docker rm -f app1
一旦容器终止,运行以下命令来启动 PHP 7 容器:
docker run --name app1 -d -p 80:80 -it -v "$PWD":/var/www/html php:7.0-apache
如果你返回浏览器中的 phpinfo
页面,你将看到现在它正在运行 PHP 7:
要终止 PHP 7 容器,请再次运行 docker rm
命令:
docker rm -f app1
前面终端会话的完整副本可以在以下截图中找到:
这个例子可能展示了 Docker 的最大优势,那就是能够在存储在本地存储上的代码库之上,快速而一致地启动容器。不过,还是存在一些限制。
有什么限制?
所以,在前面的例子中,我们启动了两个容器,每个容器运行不同版本的 PHP,并在我们的(极其简单的)代码库上运行。虽然它演示了启动容器有多简单,但也暴露了一些潜在问题和单点故障。
首先,我们的代码库存储在主机机器的文件系统中,这意味着我们只能在单台主机上运行容器。如果主机因任何原因宕机怎么办?
在纯 Docker 安装中,有几种方法可以解决这个问题。第一种方法是使用官方 PHP 容器作为基础,构建我们自己的自定义镜像,这样我们就可以将代码和 PHP 一起打包。为此,请在包含以下内容的 app1
目录中添加 Dockerfile
:
### Dockerfile
FROM php:5.6-apache
MAINTAINER Russ McKendrick <russ@mckendrick.io>
ADD index.php /var/www/html/index.php
我们还可以使用以下命令来构建自定义镜像:
docker build -t app1:php-5.6 .
当您运行构建命令时,您将看到以下输出:
一旦构建好镜像,您可以将其作为私有镜像推送到 Docker Hub 或您自己的自托管私有注册中心;另一种选择是将自定义镜像导出为 .tar
文件,然后将其复制到每个需要运行您自定义 PHP 容器的实例中。
为此,您需要运行 Docker save 命令:
docker save app1:php-5.6 > ~/app1-php-56.tar
这将创建我们自定义镜像的副本,从以下终端输出中可以看到,镜像大小应约为 482M
的 tar 文件:
现在我们已经将镜像作为 tar 文件保存,我们可以将其复制到其他主机。一旦复制了 tar 文件,您需要运行 Docker load 命令将其导入到第二台主机上:
docker load < ~/app1-php-56.tar
然后我们可以通过运行以下命令启动一个包含我们代码的容器:
docker run --name app1 -d -p 80:80 -it app1:php-5.6
以下终端输出展示了在导入并运行我们自定义容器时应该看到的内容:
到目前为止,进展如何?好吧,有是有,不是也不是。
我们可以将代码库轻松地添加到自定义镜像中,然后通过以下方式之一将镜像发布:
-
官方 Docker Hub
-
我们自己的私有注册中心
-
将镜像导出为 tar 文件并复制到其他主机
然而,对于那些处理不断变化的数据的容器,比如数据库,情况如何呢?我们的数据库选项是什么?
假设我们运行的是来自 hub.docker.com/_/mysql/
的官方 MySQL 容器,我们可以将数据库存储目录(即 /var/lib/mysql/
)从主机挂载,但这可能会导致挂载后容器中文件的权限问题。
为了避免这种情况,我们可以创建一个数据卷,包含我们 /var/lib/mysql/
目录的副本,这意味着我们将数据库与容器分离,这样我们就可以停止、启动,甚至替换 MySQL 容器,而不会丢失数据。
然而,这种方法使我们只能将 MySQL 容器运行在单台主机上,这会成为一个大的单点故障。
如果资源允许,我们可以确保承载 MySQL 容器的主机具有多重冗余,例如多个硬盘的 RAID 配置,能够承受多个硬盘故障。我们可以使用多个电源供应单元(PSU),由不同的电源线路供电,这样即使某一电源线路出现问题,主机仍能保持在线。
主机上的网络也可以做到同样的方式,网络接口卡(NIC)连接到不同的交换机,并由不同的电源线路和网络提供商供电。
尽管这种做法为我们提供了很多冗余,但我们仍然只有一台主机,而这台主机的成本也变得非常高,因为所有这些冗余——包括多个硬盘、网络和电源线路——都会增加我们原本已经支付的主机费用。
那么,解决方案是什么呢?
这就是 Docker 扩展的作用,尽管 Docker 默认不支持在主机服务器之间移动卷,但我们可以插入一个文件系统扩展,使我们能够在主机之间迁移卷,或从共享文件系统(例如 NFS)挂载卷。
如果我们的 MySQL 容器已经设置好,一旦主机出现问题,我们不会遇到问题,因为数据卷可以挂载到另一台主机上。
一旦我们挂载了卷,容器可以继续从上次停止的地方运行,因为我们把数据保存在一个正在复制到新主机的卷中,或者通过来自某个冗余存储(例如 SAN)的文件系统共享进行访问。
网络方面也可以做同样的处理。正如 IBM 研究报告总结中提到的,基于 Docker NAT 的网络在性能方面可能成为瓶颈,并且在设计容器基础设施时也可能会遇到问题。如果这是一个问题,那么可以添加网络扩展,将容器的网络卸载到软件定义网络(SDN)中,而不是让 Docker 核心通过主机上的 iptables 使用 NAT 和桥接接口来管理网络。
一旦你将这种功能引入到 Docker 的核心中,就可能会变得很难管理你的容器。在理想的情况下,你不应该担心容器运行在哪个主机上,或者如果容器/主机因某种原因停止响应,那么你的容器将不会自动在容器网络中的另一个主机上弹出并继续上次的工作。
在本书的后续章节中,我们将探讨如何实现本章讨论的一些概念,并查看由 Docker 编写的工具,这些工具旨在与核心 Docker 引擎一起运行。虽然这些工具的功能可能不如我们在后续章节中讨论的某些工具那样强大,但它们为我们在创建 Docker 主机集群并协调容器时所要覆盖的核心概念提供了一个很好的入门。
在我们查看这些工具之后,我们将探讨卷和网络插件。我们将介绍一些更著名的插件,这些插件为 Docker 核心添加了功能,使我们能够拥有一个更具冗余性的平台。
一旦我们对预编写的插件进行实践操作后,我们将探讨编写你自己插件的最佳方法。
在书的最后几章,我们将开始关注一些第三方工具,这些工具允许你配置、部署并管理容器的整个生命周期。
总结
在本章中,我们探讨了 Docker 及其解决的一些问题。我们还讨论了 Docker 核心引擎可以扩展的几种方式,以及通过扩展 Docker 获得的额外功能可以解决的一些问题。
在下一章中,我们将查看 Docker 提供的四种不同工具,使得部署、管理和配置 Docker 主机实例和容器尽可能简单和无缝。
第二章:引入第一方工具
Docker 提供了多种工具来扩展核心 Docker 引擎之外的功能。在本章中,您将逐步了解如何安装、配置和运行以下工具:
-
Docker Toolbox
-
Docker Machine
-
Docker Swarm
-
Docker Compose
这些工具虽然没有我们将在接下来的章节中使用的高级工具功能强大,但它们将作为一个良好的入门,帮助您理解如何为核心 Docker 引擎添加额外功能,以及容器部署和编排的概念,我们将在本书后面更多地探讨这些内容。
Docker Toolbox
在我们开始了解如何使用其他三种工具之前,应该先看一下如何在本地机器上安装它们。在上一章中,我们下载了 Docker 提供的脚本,并通过 bash 快速配置了官方的 Docker YUM 或 APT 仓库(具体取决于您使用的操作系统),我们执行的命令如下:
curl -sSL https://get.docker.com/ | sh
如果您已经在某个云服务或本地虚拟机上运行了基于 Linux 的服务器,这将非常有用;但是,如果您想在非 Linux 操作系统(如 Mac OSX 或 Windows)上安装 Docker,怎么办呢?
提示
始终检查源。最好的做法是检查您将要下载并安装的 bash 脚本的来源;在我们的案例中,您可以通过浏览器访问 get.docker.com/
来检查。
在我们查看 Docker 提供的工具之前,我们应该先回答一个问题:为什么?
为什么要在本地安装 Docker?
那么,为什么我们需要在非 Linux 系统上安装 Docker Toolbox、Compose、Machine 和 Swarm 呢?首先,您需要记住,Docker 本质上是一个针对基于 Linux 内核的技术的 API,例如 run (github.com/opencontainers/runc
) 和 LXC (linuxcontainers.org
),因此虽然您无法在 Mac OS X 或 Windows 系统上启动容器,但您可以与 Linux 机器上的 Docker 安装进行交互。
能够从本地机器与 Docker 进行交互,意味着您可以跨多个主机启动并与容器进行交互,这些主机可以托管在公共云/托管服务上,也可以本地托管在虚拟机中。
幸运的是,Docker 提供了便捷的方式,帮助您在本地机器上安装 Docker 和本章要介绍的其他三种服务。
安装 Docker Toolbox
Docker 提供了一个名为 Docker Toolbox 的全球安装程序,它使得安装以下软件变得尽可能简单:
-
Docker 客户端
-
Docker Machine
-
Docker Compose
-
Docker Kitematic
-
虚拟机 VirtualBox
要开始,您需要运行一个安装有 Mac OS X 10.8+ 或 Windows 7+ 的机器。在我的例子中,我使用的是 Mac OS X 10.11(El Capitan);Mac OS X 和 Windows 的安装程序差别很小:
-
首先,要开始,您需要从 Docker 网站下载安装程序。您可以在
www.docker.com/docker-toolbox/
找到适用于您操作系统的可执行文件下载链接。 -
下载完安装程序后,您可以通过双击它来启动。接下来会显示一系列屏幕和安装选项。
第一个屏幕是欢迎页面,确认您正在运行的工具箱版本。如果您是从前面截图中的页面下载的,那么您将始终拥有最新版本:
-
要继续安装的下一步,请点击继续。
-
下一屏幕将详细介绍将要安装的包以及它们将安装的位置。还有一个框,如果勾选此框,它将收集您安装 Docker Toolbox 的机器数据,对其进行匿名处理,然后提交给 Docker。
这些信息有助于 Docker 了解您的软件安装在什么类型的机器上,还会反馈您在运行安装程序时遇到的任何错误:
我总是建议保持勾选此框,因为这有助于 Docker 改进产品并提升未来版本安装程序的用户体验。
-
要继续安装的下一步,点击继续。
-
下一屏幕将允许您选择安装工具的磁盘位置。在大多数情况下,您应该使用默认设置,除非您在多个驱动器上运行应用程序:
-
要继续安装的下一步,再次点击继续按钮。
-
对于大多数人来说,标准安装就足够了;但是,如果不打算安装某些工具,您可以点击自定义按钮。您必须安装的两个工具是 Docker 客户端和 Docker 机器。
因为我想安装所有工具,所以我选择了标准安装:
-
一旦您选择了标准或自定义安装,您可以点击安装按钮进行安装。
-
安装本身只需要几分钟,在此期间,您将看到安装程序正在执行的任务反馈:
-
安装完成后,点击继续按钮。
由于运行安装程序也会作为已安装组件的升级程序,它将检查服务管理的任何文件(例如各种工具使用的虚拟机映像)是否需要更新。
根据更新的大小以及数据量的不同,这个过程可能需要几分钟。
该过程仅适用于更新,因此如果你像我一样进行了全新安装,则此部分将被跳过。
-
现在工具已经安装完成,你将获得启动Docker 快速启动终端或 Kitematic 的选项。为了本书的目的,我们将通过点击继续按钮跳过此屏幕:
-
如果一切顺利,你将看到一条消息,确认安装已经完成,你可以点击关闭按钮退出安装程序:
现在,你已经在本地机器上安装了所有工具,可以继续本章及整本书的内容了。
在我们开始查看各个工具之前,我们需要配置 Docker 代理。为此,请运行Docker 快速启动终端应用程序。如果你安装了多个终端模拟器,它会弹出提示,询问你想使用哪个终端;我更喜欢使用 Mac OS X 自带的终端,因此我选择了终端。
一旦你做出选择,一个新的终端窗口将打开,应用程序将为你配置本地的 Docker 安装:
就我而言,在启动Docker 快速启动终端应用程序时,我得到了前面的终端输出。
Docker 机器
因此,当你运行Docker 快速启动终端应用程序时,它会创建一堆证书、SSH 密钥,并配置你的用户环境以运行 Docker。它还启动了一个运行 Docker 的虚拟机。
本地开发
Docker 快速启动终端应用程序是通过 Docker 机器完成此操作的,你可以通过运行以下命令来检查该应用程序启动的机器状态:
docker-machine active
这将列出任何活动机器的名称,第一次安装 Docker 时启动的默认机器名为default
,如果你运行:
docker-machine status default
它应该会告诉你虚拟机当前正在运行。最后,你应该能够通过运行以下命令 SSH 连接到虚拟机:
docker-machine ssh default
你会注意到,当你通过 SSH 连接到虚拟机时,它正在运行 Boot2Docker 发行版。
注意
Boot2Docker 是一个极其轻量的 Linux 发行版,基于 Tiny Core Linux,它的唯一目的是运行 Docker。由于这个原因,整个发行版的大小不到 30 MB,启动大约只需五秒钟,这使得它非常适合用于本地开发机器。关于 Boot2Docker 的更多信息,请参考 boot2docker.io/
,关于 Tiny Core Linux,请参考 tinycorelinux.net/
。
当你运行这些命令时,你应该看到类似下面的终端会话:
虽然没有太多需要 SSH 进入虚拟机的必要,但由于通过工具箱安装的 Docker 客户端已被配置为连接到虚拟机上的 Docker 引擎,这意味着当你在本地运行 Docker 命令时,它会将所有请求传递给虚拟机上的 Docker 引擎。试着运行 hello-world
容器:
docker run hello-world
你应该看到以下输出:
在这一阶段,你可能会想,这一切都很好,但这并不是一个值得兴奋的工具。嗯,你错了。Docker Machine 可不止能在本地启动 Boot2Docker 虚拟机,它还有更多的“隐藏技能”。
进入云端
Docker Machine 能够连接到以下服务,创建实例,并配置你的本地 Docker 客户端,使其能够与基于云的实例进行通信。
当前支持的公共云服务提供商如下:
-
Amazon Web Services (AWS):
aws.amazon.com/
-
DigitalOcean:
www.digitalocean.com/
-
Microsoft Azure:
azure.microsoft.com/
-
Google Compute Engine:
cloud.google.com/compute/
-
Rackspace:
www.rackspace.co.uk/cloud/
-
IBM SoftLayer:
www.softlayer.com
-
Exoscale:
www.exoscale.ch/
-
VMware vCloud Air:
vcloud.vmware.com/
以下自托管平台也可以使用:
-
OpenStack:
www.openstack.org/
-
Microsoft Hyper-V:
www.microsoft.com/virtualization/
-
VMware vSphere:
www.vmware.com/uk/products/vsphere/
DigitalOcean 驱动程序
让我们开始在云中创建一些实例。首先,启动一个在 DigitalOcean 上的虚拟机。
在 DigitalOcean 中启动实例的两个先决条件是:首先需要一个 DigitalOcean 帐户,其次需要一个 API 令牌。
要注册一个 DigitalOcean 账户,请访问 www.digitalocean.com/
并点击注册按钮。登录到您的账户后,您可以通过点击顶部菜单中的API链接生成 API 令牌。
要获取您的令牌,请点击生成新令牌按钮并按照屏幕上的说明操作:
提示
您只有一次机会记录下您的令牌,请确保将其存储在安全的地方,因为任何拥有它的人都能在您的账户中启动实例。
一旦您有了令牌,您可以使用 Docker Machine 启动您的实例。为此,运行以下命令;确保将示例 API 令牌替换为您自己的:
提示
使用反斜杠:由于我们需要传递很多选项给 docker-machine
命令,我们使用反斜杠 \
将命令分成多行,这样更容易跟踪发生了什么。
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
dotest
这将把一个 dotest
实例启动到您的 DigitalOcean 账户中,您将看到类似以下终端输出的内容:
如果您检查 DigitalOcean 控制面板,您现在会看到由 Docker Machine 创建的实例已列在这里:
现在,我们通过 Docker Machine 启动了两个实例,一个是本地运行的,名为default
,另一个是托管在 DigitalOcean 上的,名为dotest
。我们可以通过运行以下命令来确认这一点:
docker-machine ls
这将返回所有正在运行的机器,并确认它们的状态、IP 地址、Docker 版本和名称。还有一列显示您本地环境配置为与哪台机器通信:
在前面的示例中,我们的本地 Docker 客户端配置为与 default
实例进行通信,它是在本地运行的。现在让我们将其更改为与 DigitalOcean 实例进行交互。
为了实现这一点,您需要更改一些本地环境变量,幸运的是,Docker Machine 提供了一种简单的方法来找出这些变量并进行更改。
要了解它们,只需运行以下命令:
docker-machine env dotest
这将告诉您需要运行什么命令来从 default
机器切换到 dotest
。最棒的是,该命令本身会以可执行的方式格式化结果,因此我们再次运行该命令,但这次的输出将被执行:
eval $(docker-machine env dotest)
现在,如果您从 Docker Machine 获取列表,您会注意到 dotest
环境现在是活跃的:
现在,我们的 DigitalOcean 实例已激活,您可以在本地机器上运行 docker
命令,这些命令将会在 DigitalOcean 实例上执行。我们通过运行 hello-world 容器来测试这一点。
如果运行以下命令,你应该看到镜像下载,然后是运行 hello-world 容器的输出:
docker run hello-world
如果接着运行以下命令,你会看到几秒钟前退出的 hello-world 镜像:
docker ps –a
以下是终端输出的示例:
正如你所见,我使用 ssh
进入 DigitalOcean 实例,并运行了 docker ps -a
和 docker images
命令来演示本地运行的命令是在 DigitalOcean 实例上执行的;但这种设置的美妙之处在于,你不应该经常需要 SSH 进入实例。
你可能注意到的一件事是,我们告诉 Docker Machine 的只是我们要使用 DigitalOcean 和我们的 API 令牌;但从未告诉它在哪个区域启动实例,需要什么规格,或者要使用哪个 SSH 密钥。
Docker Machine 拥有一些合理的默认设置:
-
digitalocean-image = ubuntu-15-10-x64
-
digitalocean-region = nyc3
-
digitalocean-size = 512mb
由于我位于英国,让我们看看如何更改区域和机器规格。首先,我们应该通过运行以下命令删除 dotest
实例:
docker-machine rm dotest
这将终止在 NYC3 运行的 512mb
实例。
提示
终止不使用的实例非常重要,因为它们每小时处于活动状态都会产生费用。记住使用 Docker Machine 的关键优势之一是你可以快速启动实例,并尽可能少地进行交互。
现在我们已经删除了旧实例,让我们为 docker-machine
命令添加一些额外的标志,以在所需的区域和规格中启动新实例,我们将称新实例为 douktest
。更新后的 docker-machine create
命令现在看起来类似于以下内容(记得用你自己的 API 令牌替换示例 API 令牌):
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 1gb \
douktest
一旦实例部署完成,你应该看到与之前命令相似的输出,然后可以通过运行以下命令使其激活:
eval $(docker-machine env douktest)
当你进入控制面板时,你会注意到实例已在指定的区域以及所需的规格下启动:
要获取每个区域的详细信息和每个区域可用的机器类型,请运行以下命令查询 DigitalOcean API(记得替换 API 令牌):
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0" "https://api.digitalocean.com/v2/regions" | python -mjson.tool
这将输出每个区域的信息。
还有最后一件事,我们还没弄清楚 SSH 密钥的情况。每次运行 Docker Machine,都会为你启动的实例创建一个新的 SSH 密钥并上传到提供者,每个密钥都存储在你用户主目录下的 .docker
文件夹中。例如,可以通过以下命令找到 douktest
的密钥:
cd ~/.docker/machine/machines/douktest/
在这里,您还会找到用于通过实例上的 Docker 安装认证 Docker 代理的证书以及相关配置:
这涵盖了 DigitalOcean,那么其他服务呢?让我们快速看一下 Amazon Web Services,以便我们可以对不同云服务提供商的驱动程序有个大致了解。
Amazon Web Services 驱动程序
如果您还没有 Amazon Web Services 账户,应该在 aws.amazon.com/
注册一个。如果您是 AWS 新用户,您将有资格获得他们的免费层服务,详情请见 aws.amazon.com/free/
。
如果您不熟悉 AWS,建议在开始本章内容之前先阅读 Amazon 的入门指南,您可以在 docs.aws.amazon.com/gettingstarted/latest/awsgsg-intro/gsg-aws-intro.html
找到该指南。
AWS 驱动程序与 DigitalOcean 驱动程序类似,并具有一些合理的默认设置,考虑到我们不打算深入讨论如何自定义 Docker Machine 启动的 EC2 实例,我将坚持使用默认设置。对于 AWS 驱动程序,默认设置如下:
-
amazonec2-region = us-east-1 (北弗吉尼亚)
-
amazonec2-ami = ami-26d5af4c (Ubuntu 15.10)
-
amazonec2-instance-type = t2.micro
-
amazonec2-root-size = 16GB
-
amazonec2-security-group = docker-machine
在启动实例之前,我们还需要知道我们的 AWS 访问密钥和密钥对,此外,还需要知道 VPC ID,因为我们将在其中启动实例。要获取这些信息,请登录到 AWS 控制台,地址是 console.aws.amazon.com/
。
您应该已经拥有您的访问密钥和密钥 ID 的副本,因为这些是在您的 AWS 用户首次创建时生成的。如果您丢失了它们,可以通过导航到 服务 | IAM | 用户,然后选择您的用户,最后转到 安全凭证 标签页来生成一对新密钥。在那里您应该能看到一个名为 创建访问密钥 的按钮。
注意
Amazon 将 虚拟专用云(VPC)描述为让您能够在 AWS 云中配置一个逻辑上隔离的区域,在该区域内您可以启动您定义的虚拟网络中的资源。您可以完全控制您的虚拟网络环境,包括选择您自己的 IP 地址范围、创建子网、配置路由表和网络网关。
在查找 VPC ID 之前,您应该确保您处于正确的区域,可以通过确保 AWS 控制台右上角显示 N. Virginia 来验证,如果没有显示,您可以从下拉列表中选择正确的区域。
确保你处于正确的区域后,前往服务 | VPC,点击您的 VPC。你无需担心创建和配置 VPC,因为 Amazon 在每个区域都会为你提供默认 VPC。选择 VPC 后,你应该会看到类似于以下截图的内容:
记下 VPC ID,你现在应该有足够的信息通过 Docker Machine 启动实例。为此,请运行以下命令:
docker-machine create \
--driver amazonec2 \
--amazonec2-access-key JHFDIGJKBDS8639FJHDS \
--amazonec2-secret-key sfvjbkdsvBKHDJBDFjbfsdvlkb+JLN873JKFLSJH \
--amazonec2-vpc-id vpc-35c91750 \
awstest
如果一切顺利,你应该会看到类似以下的输出:
你还应该能够通过导航到服务 | EC2 | 实例来在 AWS 控制台中看到启动的 EC2 实例:
你可能已经注意到,Docker Machine 创建了安全组,并且无需我们介入就为实例分配了 SSH 密钥,这符合我们不需要成为环境配置专家的原则,Docker 实例的启动和配置都已自动完成。
在我们终止实例之前,先将本地 Docker 客户端切换为使用 AWS 实例,并启动Hello World
容器:
如你所见,一旦使用 Docker Machine 启动了实例并切换本地 Docker 客户端,你会发现本地运行 Docker 和在云服务商上运行 Docker 在使用上没有区别。
在我们开始累积费用之前,我们应该通过运行以下命令来终止我们的测试 AWS 实例:
docker-machine rm awstest
然后确认实例在 AWS 控制台中已经正确终止:
如果不这么做,EC2 实例将毫不犹豫地一直运行,每小时收取**$0.013**的费用,直到它被终止。
注意
请注意,这不是 Amazon 的弹性容器服务(ECS)。我们将在第七章,查看调度器中介绍 Amazon ECS。
其他考虑事项
如我们所做的示例所示,Docker Machine 是 Docker Toolbox 中的一个强大工具,它使所有技术水平的用户都能轻松启动本地或云提供商中的实例,而不需要动手配置服务器实例或本地 Docker 客户端。
本章中我们使用的示例均为启动 Boot2Docker 或 Ubuntu。Docker Machine 还支持以下操作系统:
-
Debian(8.0+):
www.debian.org/
-
Red Hat Enterprise Linux(7.0+):
www.redhat.com/
-
CentOS(7+):
www.centos.org/
-
Fedora(21+):
getfedora.org/
-
RancherOS(0.3):
rancher.com/rancher-os/
需要提到的关于 Docker Machine 的另一点是,默认情况下,它会启用崩溃报告功能。考虑到 Docker Machine 可以与许多不同的配置/环境组合一起使用,重要的是 Docker 能够收到任何问题的通知,以帮助他们改进产品。如果由于某种原因你想退出此功能,可以运行以下命令来禁用崩溃报告:
mkdir -p ~/.docker/machine && touch ~/.docker/machine/no-error-report
有关 Docker Machine 的更多信息,你可以参考官方文档:
-
Docker Machine:
docs.docker.com/machine/
-
Docker Machine Drivers:
docs.docker.com/machine/drivers/
-
Docker Machine Command Reference:
docs.docker.com/machine/reference/
Docker Swarm
现在我们已经讨论了如何使用 Docker Machine 启动单个 Docker 实例,让我们更进一步,创建一个实例集群。为此,Docker 提供了一个名为 Swarm 的工具。当部署时,它充当你的 Docker 客户端与宿主 Docker 实例之间的调度器,根据调度规则决定在哪里启动容器。
创建本地集群
首先,我们将使用 Docker Machine 在本地通过 VirtualBox (www.virtualbox.org
) 创建一个集群,VirtualBox 是 Docker Toolbox 的一部分。首先,我们将启动一个虚拟机来生成发现令牌。为此,运行以下命令:
docker-machine create -d virtualbox discover
然后配置你的 Docker 客户端以使用新创建的本地实例:
eval "$(docker-machine env discover)"
你可以通过运行docker-machine ls
命令并确保 discover
在活动列中有星号,来检查你的 Docker 客户端是否配置为使用 discover
实例。
最后,你可以通过运行以下命令来安装发现服务:
docker run swarm create
这将下载并运行发现服务并生成令牌。在过程结束时,你将获得一个令牌;请务必记下这个令牌,以便在后续步骤中使用。如果一切顺利,你应该会看到类似于以下输出的内容:
在上面的示例中,令牌是40c3bf4866eed5ad14ade6633fc4cefc
。现在我们已经有了令牌,需要启动一个实例作为调度器,这个实例被称为 Swarm 管理器。
为此,请输入以下命令,并确保替换为你生成的令牌:
docker-machine create \
-d virtualbox \
--swarm \
--swarm-master \
--swarm-discovery token://40c3bf4866eed5ad14ade6633fc4cefc \
swarm-master
现在我们已经启动了 Swarm 管理器虚拟机,可以开始启动充当集群节点的虚拟机。同样,使用发现令牌,运行以下命令以启动两个节点:
docker-machine create \
-d virtualbox \
--swarm \
--swarm-discovery token://40c3bf4866eed5ad14ade6633fc4cefc \
swarm-node-01
然后使用以下命令启动第二个节点:
docker-machine create \
-d virtualbox \
--swarm \
--swarm-discovery token://40c3bf4866eed5ad14ade6633fc4cefc \
swarm-node-02
我们可以通过运行 docker-machine ls
命令检查我们的虚拟机,然后运行以下命令将我们的 Docker 客户端切换到使用集群:
eval $(docker-machine env --swarm swarm-master)
现在您的 Docker 客户端已经与集群通信,您可以运行docker info
来查找有关所有节点和集群本身的信息,您将看到类似以下屏幕截图的内容:
现在,我们有一个三 CPU、3GB 的集群,运行在三个节点上。为了测试它,让我们运行Hello World
容器,然后运行docker ps -a
,以便查看容器启动在哪个节点上:
如您从终端输出中看到的,容器是在swarm-node-01
上启动的,再次运行容器应该会将其启动在我们的第二个节点上:
所以,你看,这是一个非常基础的 Docker Swarm 集群,您可以使用本地 Docker 客户端将容器启动到其中,所有操作都是通过 Docker Machine 启动和管理的。
在我们进入下一部分之前,我们应该删除本地集群。为此,只需运行以下命令:
docker-machine rm discover swarm-master swarm-node-01 swarm-node-02
当提示时点击yes
。然后,您可以通过运行docker-machine ls
命令检查虚拟机是否已终止。
创建远程集群
在我们查看下一个工具之前,让我们在云中启动一个集群。我将使用 DigitalOcean 进行此操作。
首先,让我们创建一个新的发现令牌。由于我们所需要做的只是生成一个发现令牌,因此没有必要仅为此任务在 DigitalOcean 上启动实例,因此我们将简单地在本地启动一台机器,记下发现令牌后再将其移除:
docker-machine create -d virtualbox token
eval "$(docker-machine env token)"
docker run swarm create
docker-machine rm token
现在我们已经获得了发现令牌,让我们在 DigitalOcean 上启动我们的 Swarm 集群,首先我们将研究 Swarm 管理器:
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--swarm \
--swarm-master \
--swarm-discovery token://453sdfjbnfvlknmn3435mwedvmndvnwe \
swarm-master
然后我们将使用这两个节点:
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 1gb \
--swarm \
--swarm-discovery token://453sdfjbnfvlknmn3435mwedvmndvnwe \
swarm-node-01
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 1gb \
--swarm \
--swarm-discovery token://453sdfjbnfvlknmn3435mwedvmndvnwe \
swarm-node-02
如以下屏幕截图所示,我在 DigitalOcean 的伦敦数据中心启动了集群,并为两个节点分配了更多资源:
我们将配置本地 Docker 客户端,使用以下命令连接远程集群:
eval $(docker-machine env --swarm swarm-master)
这将为我们提供以下信息:
我们将使用这个集群进行本章的下一部分,因此目前尽量保持它运行。如果您无法保持运行,则可以通过运行以下命令删除该集群:
docker-machine rm swarm-master swarm-node-01 swarm-node-02
您还应该检查 DigitalOcean 控制面板,以确保您的实例已正确终止。
注意
请记住,使用公共云服务时,您是按使用量付费的,因此如果您的实例保持开机状态,即使它处于errored
状态,使用 Docker Machine 时,计费仍然在继续,您会产生费用。
发现后端
此时,值得指出的是,Docker 允许您更换发现后端,目前我们正在使用默认的托管发现与 Docker Hub,它不建议在生产环境中使用。
Swarm 支持以下发现服务:
-
etcd:
coreos.com/etcd/
-
Consul:
www.consul.io/
-
ZooKeeper:
zookeeper.apache.org/
目前,我们只会查看 Docker 提供的工具,而不会考虑任何第三方选项,因此我们将坚持使用默认的 Discovery 后端。
不幸的是,默认的 Discovery 后端没有提供高可用性,这意味着我们的 Swarm 管理器是单点故障。对于我们的需求来说,这不是一个问题;然而,我不建议在生产环境中运行这种配置。
有关不同 Discovery 后端和 Swarm 高可用性的更多信息,请参考以下 URL:
-
Discovery 后端:
docs.docker.com/swarm/discovery/
-
Swarm 高可用性:
docs.docker.com/swarm/multi-manager-setup/
我们将在后面的章节中深入讨论调度器,所以现在让我们进入 Docker Toolbox 安装的最后一个服务。
Docker Compose
到目前为止,在我们对 Docker Toolbox 工具的探索中,我们一直在使用管理 Docker 主机的服务,本章我们要看的最后一个服务是处理容器的服务。我相信您会同意,到目前为止,Docker 提供的工具非常直观,Docker Compose 也不例外。它最初作为一个名为 Fig 的第三方服务开始,由 Orchard Labs 编写(该项目的原始网站仍然可以访问:fig.sh/
)。
原始项目的目标如下:
“使用 Docker 提供快速、独立的开发环境”
自从 Fig 成为 Docker 的一部分后,它们并没有偏离最初的目标:
“Compose 是一个定义和运行多容器 Docker 应用程序的工具。使用 Compose,您通过 Compose 文件配置应用程序的服务。然后,使用单个命令,您可以从配置中创建并启动所有服务。”
在我们开始查看 Compose 文件并启动容器之前,让我们思考一下像 Compose 这样的工具为什么有用。
为什么选择 Compose?
启动单个容器就像运行以下命令一样简单:
docker run -i -t ubuntu /bin/bash
这将启动并附加到一个 Ubuntu 容器。如我们之前提到的,启动简单容器只是其中的一部分,Docker 并不是用来替代虚拟机的,而是用来运行单个应用程序的。
这意味着你不应该在单个容器中运行整个 LAMP 堆栈,应该将 Apache 和 PHP 运行在一个容器中,然后与一个运行 MySQL 的第二个容器连接。你可以更进一步,运行 NGINX 容器、PHP-FPM 容器以及 MySQL 容器。这就变得复杂了。突然间,你的简单启动命令变成了多行命令,所有命令必须按正确的顺序并带有正确的标志执行。
这正是 Docker Compose 试图解决的问题。你可以使用 YAML 文件定义容器,而不是执行多个长命令。这意味着你可以通过一个命令启动应用程序,并将容器启动顺序的逻辑交给 Compose 来处理。
注意
YAML 不是标记语言(YAML)是一种人类友好的数据序列化标准,适用于所有编程语言。
这也意味着你可以将应用程序的 Compose 文件与代码库一起发布,或者直接发送给另一个开发者/管理员,他们将能够按照你预期的方式启动应用程序。
Compose 文件
几乎每个人在某个时候都会安装、使用或阅读过 WordPress,因此在接下来的几个例子中,我们将使用来自 Docker Hub 的官方 WordPress 容器,你可以在hub.docker.com/_/wordpress/
上找到该容器的详细信息。
注意
WordPress 是一个可以用来创建美丽网站、博客或应用的 Web 软件。我们喜欢说 WordPress 既是免费的,又是无价的。欲了解更多信息,请查看wordpress.org/
。
让我们从安装一个基本的 WordPress 开始,首先创建一个名为wordpress
的文件夹,然后将以下内容添加到一个名为docker-compose.yml
的文件中:
wordpress:
container_name: my-wordpress-app
image: wordpress
ports:
- "80:80"
links:
- "mysql:mysql"
mysql:
container_name: my-wordpress-database
image: mysql
environment:
MYSQL_ROOT_PASSWORD: "password"
你将能够通过确保本地 Docker 客户端配置为使用 Swarm 集群来启动应用程序,运行docker-machine ls
并确保其处于活动状态,然后运行以下命令:
eval $(docker-machine env --swarm swarm-master)
一旦你的客户端配置为与 Swarm 集群通信,在包含docker-compose.yml
文件的文件夹中运行以下命令:
docker-compose up -d
使用命令末尾的-d
标志会以分离模式启动容器,这意味着容器将在后台运行。如果我们不使用-d 标志,那么我们的容器将会在前台启动,我们将无法继续使用同一终端会话,而不停止正在运行的容器。
你将看到类似以下的输出:
如你所见,你可以通过运行docker ps
来查找启动 WordPress 应用程序的节点的 IP 地址。如果你访问图中显示的 IP 地址,其中列出了端口 80
,你将看到一个 WordPress 安装界面:
一个有趣的地方是,尽管 my-wordpress-app
容器在 docker-compose.yml
文件中最先定义,Compose 仍然识别它与 my-wordpress-database
容器的关联,并首先启动了后者。此外,你可能注意到 wordpress:latest
和 mysql:latest
镜像已经在 Swarm 集群中的所有节点上被拉取下来。
那么,docker-compose.yml
文件本身呢?让我们再看一遍,这次带上一些注释。
就 Compose 而言,我们的 WordPress 应用程序被拆分成两个应用程序,一个叫做 wordpress,另一个叫做 mysql。让我们再次看看 docker-compose.yml
文件:
wordpress:
container_name: my-wordpress-app
image: wordpress
ports:
- "80:80"
links:
- "mysql:mysql"
mysql:
container_name: my-wordpress-database
image: mysql
environment:
MYSQL_ROOT_PASSWORD: "password"
在最顶层,我们有应用程序名称。从这里开始,我们定义应用程序的配置,通过给出键和值,确保你密切关注缩进。我倾向于使用两个空格来明确表示有缩进,但又不会使其变得难以阅读。
我们定义的第一个关键字是 container_name
,我们不必这么做,因为 Compose 会根据我们所在的文件夹名称和应用程序名称自动命名我们的容器。如果我们没有定义这个关键字,那么我们的容器将被命名为 wordpress_wordpress_01
和 wordpress_mysql_01
。
下一个关键字告诉应用程序使用哪个 image
,这将直接从 Docker Hub 拉取镜像。
然后我们定义了 ports
,注意我们只为 wordpress 应用程序定义了端口,而没有为 mysql 应用程序定义。因为我们希望 wordpress 应用程序监听宿主机的端口,所以我们给了 80:80。在这种情况下,第一个 80 是 宿主机 端口,第二个是我们希望暴露的 容器 端口。
同样,接下来的关键字只在 wordpress 应用程序中使用,它定义了 links
。链接用于将容器连接在一起,这里将 mysql 容器暴露给 wordpress 容器。这意味着当 wordpress 容器启动时,它将知道 mysql 容器的 IP 地址,并且只有 mysql 容器的端口会暴露给 wordpress 容器。
我们定义的最后一个关键字是 environment
,在这里我们传递更多的键和值,这些将在容器启动时设置为环境变量。
Compose 文件中所有可用键的详细说明可以在官方文档中找到:docs.docker.com/compose/compose-file/
。
启动更多
使用 Compose 的一个优势是它启动的每个环境都是隔离的,让我们使用以下 docker-compose.yml
文件启动另一个 WordPress 安装:
wordpress:
container_name: my-other-wordpress-app
image: wordpress
ports:
- "80:80"
links:
- "mysql:mysql"
mysql:
container_name: my-other-wordpress-database
image: mysql
environment:
MYSQL_ROOT_PASSWORD: "password"
如你所见,除了容器名称外,它与我们之前启动的环境完全相同:
你还会注意到,my-other-wordpress
容器已在集群的第二个节点上启动。目前,每个 Compose 环境会在单一节点上启动。随着我们启动更多的环境,我们将需要开始调整端口分配,因为它们将在主机上发生冲突(也就是说,你不能将两个port 80
分配给同一主机)。
注意
别忘了通过使用docker-machine rm
命令删除你启动的任何基于云的实例,同时检查你的云服务提供商的控制面板,确保这些实例已正确终止。
总结
在本章中,我们介绍了 Docker 提供的附加客户端工具,这些工具可以扩展你核心 Docker 安装的功能。我们所查看的所有工具都旨在融入你的工作流程,并尽可能简单易用。在接下来的章节中,我们将探讨如何使用第三方服务扩展 Docker 的一些核心功能。到时,我们会重新审视本章中提到的一些工具,并看看它们如何为这些工具增加额外的功能。
第三章:卷插件
本章将概述第一方和第三方卷插件。我们将讨论安装、配置和使用以下存储插件:
-
Docker 卷:
docs.docker.com/engine/userguide/containers/dockervolumes/
-
Convoy:
github.com/rancher/convoy/
-
REX-Ray:
github.com/emccode/rexray/
你还将了解如何与 Docker 插件交互,以及它们与我们在第二章中讨论的支持工具的区别和如何协同工作,介绍第一方工具。
注意
本章假设你使用的是 Docker 1.10+。请注意,某些命令在旧版本中可能无法工作。
零卷
在我们查看卷之前,让我们先看看如果不使用任何卷,将一切存储在容器中会发生什么。
首先,让我们使用 Docker Machine 在本地创建一个名为 chapter03
的新 Docker 实例:
docker-machine create chapter03 --driver=virtualbox
eval $(docker-machine env chapter03)
既然我们已经有了机器,可以使用 Docker Compose 来运行 WordPress 场景。首先,我们需要启动我们的 WordPress 容器,正如之前那样,我们使用来自 Docker Hub 的官方 WordPress 和 MySQL 镜像,我们的 docker-compose.yml
文件类似于以下代码:
version: '2'
services:
wordpress:
container_name: my-wordpress-app
image: wordpress
ports:
- "80:80"
links:
- mysql
environment:
WORDPRESS_DB_HOST: "mysql:3306"
WORDPRESS_DB_PASSWORD: "password"
mysql:
container_name: my-wordpress-database
image: mysql
environment:
MYSQL_ROOT_PASSWORD: "password"
如你所见,compose 文件没有什么特别之处。你可以通过运行以下命令启动它:
docker-compose up -d
启动容器后,通过运行以下命令检查它们的状态:
docker-compose ps
如果它们的状态都为 Up
,你可以通过运行以下命令进入 WordPress 安装界面:
open http://$(docker-machine ip chapter03)/
这将打开你的浏览器并转到 Docker 实例的 IP 地址。在我的例子中是 http://192.168.99.100/
。你应该看到以下屏幕:
点击 继续 按钮并安装 WordPress:
填写完信息后,点击安装 WordPress 以完成安装。完成后,MySQL 数据库将更新你的设置,测试帖子和评论也将被添加。当安装完成后,你将看到一个成功屏幕:
现在你应该能够重新运行以下命令:
open http://$(docker-machine ip chapter03)
这将带你到一个非常空的 WordPress 网站:
你的命令行历史记录应该类似于以下终端输出:
现在我们已经安装了 WordPress 网站,让我们通过运行以下命令销毁容器:
docker-compose stop && docker-compose rm
确保在提示时输入y
。然后你会收到一个确认信息,说明你的两个容器已经被删除:
现在我们已经删除了容器,让我们通过重新执行命令来重新创建它们:
docker-compose up -d
docker-compose ps
open http://$(docker-machine ip chapter03)/
如你所见,你再次看到一个安装界面,这是正常的,因为 MySQL 数据库存储在我们已删除的mysql
容器中。
在我们继续查看 Docker 自带的功能之前,先做一些整理,删除掉容器:
docker-compose stop && docker-compose rm
默认卷驱动
在我们开始使用第三方卷插件之前,应该先了解一下 Docker 自带的功能以及卷如何解决我们刚才处理的场景。再次提醒,我们将使用一个docker-compose.yml
文件;不过这次,我们会添加几行代码来创建和挂载卷:
version: '2'
services:
wordpress:
container_name: my-wordpress-app
image: wordpress
ports:
- "80:80"
links:
- mysql
environment:
WORDPRESS_DB_HOST: "mysql:3306"
WORDPRESS_DB_PASSWORD: "password"
volumes:
- "uploads:/var/www/html/wp-content/uploads/"
mysql:
container_name: my-wordpress-database
image: mysql
environment:
MYSQL_ROOT_PASSWORD: "password"
volumes:
- "database:/var/lib/mysql"
volumes:
uploads:
driver: local
database:
driver: local
如你所见,这里我们创建了两个卷,一个叫uploads
,它被挂载到 WordPress 容器的上传文件夹中。第二个卷叫database
,它被挂载到 MySQL 容器的/var/lib/mysql
目录中。
你可以使用以下命令启动容器并打开 WordPress:
docker-compose up -d
docker-compose ps
open http://$(docker-machine ip chapter03)/
在我们完成浏览器中的 WordPress 安装之前,我们应该通过运行docker exec
命令确保uploads
文件夹具有正确的权限:
docker exec -d my-wordpress-app chmod 777 /var/www/html/wp-content/uploads/
现在uploads
文件夹的权限已经正确设置,我们可以按照之前的测试继续进行 WordPress 安装:
由于 WordPress 会在安装过程中创建一个Hello World!
测试帖子,我们应该去编辑该帖子。要做到这一点,请使用安装时输入的凭证登录到 WordPress。登录后,进入文章 | Hello World,然后点击设置特色图片按钮上传一张特色图片。上传完特色图片后,你的编辑界面应该类似于以下截图:
上传完图片后,点击更新按钮,然后通过点击屏幕左上角的标题返回到你的 WordPress 首页。当首页打开后,你应该能看到你上传的特色图片:
在删除容器之前,你可以运行以下命令来显示 Docker 中已创建的所有卷:
docker volume ls
运行该命令时,你应该能看到我们在docker-compose.yml
文件中定义的两个卷:
记得,正如我们在上一章讨论的那样,Docker Compose 会将项目标题(即docker-compose.yml
所在文件夹的名称)作为前缀,这个项目名叫做wordpress-vol
,由于名称中不允许有-
,因此它被去掉了,留下了wordpressvol
。
现在我们有了基本的 WordPress 安装并且更新了内容,让我们像之前一样删除容器:
docker-compose stop && docker-compose rm
docker-compose ps
到此为止,你大概可以猜到接下来会发生什么,让我们重新启动容器,并在浏览器中打开 WordPress 网站:
docker-compose up -d
open http://$(docker-machine ip chapter03)/
启动可能需要几秒钟的时间,因此如果浏览器打开时没有看到你的 WordPress,刷新页面。如果一切顺利,你应该能看到你编辑过的 Hello World!
博客文章:
虽然这看起来和之前的截图相同,但你会注意到你已经从 WordPress 中登出了。这是因为默认情况下,WordPress 将其会话存储在文件系统中,并且由于会话文件不存储在上传目录中,因此在我们删除容器时会丢失会话文件。
卷也可以在容器之间共享,如果我们在 docker-compose.yml
文件的 Services
部分添加以下内容:
wordpress8080:
container_name: my-wordpress-app-8080
image: wordpress
ports:
- "8080:80"
links:
- mysql
environment:
WORDPRESS_DB_HOST: "mysql:3306"
WORDPRESS_DB_PASSWORD: "password"
volumes:
- "uploads:/var/www/html/wp-content/uploads/"
你可以启动一个第二个容器,在 port 8080
上运行 WordPress,并访问我们上传的文件,网址是 http://192.168.99.100:8080/wp-content/uploads/2016/02/containers-1024x512.png
。
请注意,前面的 URL 会因你的安装环境而有所不同,因为 IP 地址可能不同,上传日期和文件名也可能不同。
你可以通过运行以下命令获取更多关于卷的信息:
docker volume inspect <your_volume_name>
在我们的案例中,返回以下信息:
你可能已经注意到,我们为两个卷使用了 local
驱动,这会在我们的 Docker 实例上创建卷,并挂载来自主机的文件夹,这里是指运行在 VirtualBox 下的 Docker Machine 主机。
你可以通过 SSH 登录到主机并进入 docker volume inspect
命令返回的挂载点目录来查看文件夹中的内容。要通过 SSH 登录并切换到 root 用户,运行以下命令:
docker-machine ssh chapter03
sudo su -
然后你就可以切换到包含卷的文件夹,切换到 root 用户的原因是确保你有权限查看文件夹中的内容:
如前面终端输出所示,这些文件属于一个未知用户,其用户 ID 和组 ID 为 32,在容器中,这是 Apache 用户。如果你直接添加文件或进行更改,请小心,因为你可能会遇到各种权限错误,尤其是在容器访问你添加或更改的文件时。
到目前为止一切顺利,但有什么限制呢?最大的限制是你的数据绑定到单个实例。在上一章中,我们讨论了如何使用 Swarm 进行 Docker 集群化,我们提到使用 Docker Compose 启动的容器绑定到单个实例,这对于开发非常好,但对于生产环境来说就不太适用了,因为我们可能有多个主机实例,希望将容器分布到多个实例上,这时候第三方卷驱动就派上用场了。
第三方卷驱动
有多种第三方卷驱动可供选择,它们各自提供不同的功能。首先,我们将研究 Rancher 提供的 Convoy。
在我们开始安装 Convoy 之前,应该先考虑在云端某个地方启动一个 Docker 实例。由于我们已经在 DigitalOcean 和 Amazon Web Services 中启动了 Docker 实例,我们应该终止本地的 chapter03
实例,并在这些提供商中重新启动它,我将使用 DigitalOcean:
docker-machine stop chapter03 && docker-machine rm chapter03
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0\
--digitalocean-region lon1 \
--digitalocean-size 1gb \
chapter03
eval "$(docker-machine env chapter03)"
我们在云提供商中启动实例的原因之一是,我们需要一个完整的底层操作系统来安装和使用 Convoy,而 Boot2Docker 提供的镜像虽然不错,但对于我们的需求来说,它稍微有些轻量级。
提示
在进一步操作之前,我建议您为 DigitalOcean 的 Droplet 配置一个浮动 IP 地址。原因是,在本章节中,我们将安装 WordPress,并将安装迁移到新机器上。如果没有浮动 IP 地址,您的 WordPress 安装可能会出现问题。您可以在 DigitalOcean 网站上找到有关浮动 IP 地址的更多信息,网址是 www.digitalocean.com/community/tutorials/how-to-use-floating-ips-on-digitalocean
。
安装 Convoy
如前所述,我们需要在底层 Docker 主机操作系统上安装 Convoy。为此,您应首先通过 SSH 登录到 Docker 主机:
docker-machine ssh chapter03
提示
由于机器已在 DigitalOcean 启动,我们已经以 root 用户身份连接;这意味着我们在执行命令时不需要加 sudo
,但是由于您可能在其他提供商处启动实例,我会保留 sudo
,以防您不是 root 用户而遇到权限错误。
现在,您已经使用 ssh
命令登录到 Docker 主机,我们可以安装并启动 Convoy。Convoy 是用 Go 编写的,并以静态二进制文件形式发布。这意味着我们不需要手动编译它;我们只需获取二进制文件并将其复制到指定位置:
wget https://github.com/rancher/convoy/releases/download/v0.4.3/convoy.tar.gz
tar xvf convoy.tar.gz
sudo cp convoy/convoy convoy/convoy-pdata_tools /usr/local/bin/
Convoy 的更新版本可以在 github.com/rancher/convoy/releases
上找到;不过,这些版本仅供 Rancher 使用。我们将在后面的章节中详细讨论 Rancher。
现在我们已经准备好了二进制文件,接下来需要设置我们的 Docker 安装,以便加载插件:
sudo mkdir -p /etc/docker/plugins/
sudo bash -c 'echo "unix:///var/run/convoy/convoy.sock" > /etc/docker/plugins/convoy.spec'
convoy.spec
文件告诉 Docker 如何访问 Convoy;有关插件工作的更多细节,请参阅 第五章,构建您自己的插件。
Convoy 已安装并准备就绪,接下来我们只需添加一些存储。出于测试目的,我们将创建并使用一个回环设备;但是,请不要在生产环境中这样做!
注意
回环设备是一种将文件作为真实设备进行解释的机制。此方法的主要优势是,所有用于真实磁盘的工具都可以与回环设备一起使用。请参考wiki.osdev.org/Loopback_Device
。
要创建回环设备并挂载它,请运行以下命令:
truncate -s 4G data.vol
truncate -s 1G metadata.vol
sudo losetup /dev/loop5 data.vol
sudo losetup /dev/loop6 metadata.vol
现在我们的存储已经准备好,我们可以通过运行以下命令启动 Convoy:
sudo convoy daemon --drivers devicemapper --driver-opts dm.datadev=/dev/loop5 --driver-opts dm.metadatadev=/dev/loop6 &
您应该会看到类似于以下的输出:
现在我们已经启动了 Convoy,输入exit
以退出 Docker 主机并返回本地机器。
使用 Convoy 卷启动容器
现在我们已经启动了 Convoy,可以对我们的docker-compose.yml
文件进行一些更改:
version: '2'
services:
wordpress:
container_name: my-wordpress-app
image: wordpress
ports:
- "80:80"
links:
- mysql
environment:
WORDPRESS_DB_HOST: "mysql:3306"
WORDPRESS_DB_PASSWORD: "password"
volumes:
- "uploads:/var/www/html/wp-content/uploads/"
mysql:
container_name: my-wordpress-database
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: "password"
command: mysqld --ignore-db-dir=lost+found
volumes:
- "database:/var/lib/mysql/"
volumes:
uploads:
driver: convoy
database:
driver: convoy
如果你没有将docker-compose.yml
文件放入wordpressconvoy
文件夹中,你会发现稍后在本节中的某些步骤需要更改卷的名称。
如您所见,我突出显示了一些更改。首先,我们已经切换到了使用 MariaDB,原因是现在我们使用的是实际的文件系统,而不是仅仅在主机上使用一个文件夹。我们有一个lost
+ found
文件夹,目前官方的 MySQL 容器无法正常工作,因为它认为卷中已经有数据库。为了解决这个问题,我们可以在启动 MySQL 时使用--ignore-db-dir
指令,而 MariaDB 支持该指令。
让我们启动容器并查看通过以下命令创建的卷:
docker-compose up -d
open http://$(docker-machine ip chapter03)/
docker-compose ps
docker volume ls
docker volume inspect wordpressconvoy_database
您应该会看到类似于以下的终端输出:
在进行其他操作之前,完成 WordPress 安装并上传一些内容:
open http://$(docker-machine ip chapter03)/
记得在上传内容之前为卷设置正确的权限:
docker exec -d my-wordpress-app chmod 777 /var/www/html/wp-content/uploads/
使用 Convoy 创建快照
到目前为止,它与默认的卷驱动没有什么不同。让我们来看看创建快照并备份卷的过程,稍后你会明白为什么要这样做。
首先,让我们跳回 Docker 主机:
docker-machine ssh chapter03
让我们通过运行以下命令来创建我们的第一个快照:
sudo convoy snapshot create wordpressconvoy_uploads --name snap_wordpressconvoy_uploads_01
sudo convoy snapshot create wordpressconvoy_database --name snap_wordpressconvoy_database_01
一旦创建了快照,您将收到一个唯一的 ID。就我而言,这些 ID 分别是c00caa88-087d-45ad-9498-7610844c075e
和4e2a2a6f-887c-4692-b2a8-e1f08aa42400
。
备份我们的 Convoy 快照
现在我们已经有了快照,我们可以以此为基础创建备份。为此,我们必须首先确保存储备份的目标目录存在:
sudo mkdir /opt/backup/
现在我们有了存储备份的地方,让我们创建备份:
sudo convoy backup create snap_wordpressconvoy_uploads_01 --dest vfs:///opt/backup/
sudo convoy backup create snap_wordpressconvoy_database_01 --dest vfs:///opt/backup/
一旦备份完成,您将收到一个 URL 形式的确认。对于上传,返回的 URL 如下:
vfs:///opt/backup/?backup=34ca255e-7164-4734-8b96-579b4e79f728\u0026volume=26a5913e-4794-4df3-bbb9-7a6361c23a75
对于数据库,URL 如下:
vfs:///opt/backup/?backup=41731035-2760-4a1b-bba9-5e906e2471bc\u0026volume=8212de61-ea8c-4777-881e-d4bd07b800e3
记得记录下这些 URL,因为你需要它们来恢复备份。存在一个问题,我们创建的备份存储在我们的 Docker 主机上。如果它出现故障怎么办?那时我们所有的辛勤工作将会丢失!
Convoy 支持为 Amazon S3 创建备份,所以我们来做这件事。首先,你需要登录到你的 Amazon Web Services 账户,并创建一个 S3 桶来存储你的备份。
一旦你创建了一个桶,你需要将凭证添加到服务器:
mkdir ~/.aws/
cat >> ~/.aws/credentials << CONTENT
[default]
aws_access_key_id = JHFDIGJKBDS8639FJHDS
aws_secret_access_key = sfvjbkdsvBKHDJBDFjbfsdvlkb+JLN873JKFLSJH
CONTENT
注意
要了解如何创建 Amazon S3 桶的更多信息,请参考 aws.amazon.com/s3/getting-started/
的入门指南,关于 credentials
文件的详细信息,请参见 blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs
。
现在你的 Amazon S3 桶已经创建。我将其命名为 chapter03-backup-bucket
并创建在 us-west-2
区域。你的 Docker 主机已能访问 Amazon 的 API。你可以重新备份数据,但这次,将它们推送到 Amazon S3:
sudo convoy backup create snap_wordpressconvoy_uploads_01 --dest s3://chapter03-backup-bucket@us-west-2/
sudo convoy backup create snap_wordpressconvoy_database_01 --dest s3://chapter03-backup-bucket@us-west-2/
如你所见,目标 URL 的格式如下:
s3://<bucket-name>@<aws-region>
一旦备份完成,你将再次收到 URL。在我的例子中,备份的 URL 如下:
s3://chapter03-backup-bucket@us-west-2/?backup=6cb4ed46-2084-42bc-8261-6b4da690bd5e\u0026volume=26a5913e-4794-4df3-bbb9-7a6361c23a75
对于数据库备份,我们将看到以下内容:
s3://chapter03-backup-bucket@us-west-2/?backup=75608b0b-93e7-4319-b212-7a1b0ccaf289\u0026volume=8212de61-ea8c-4777-881e-d4bd07b800e3
当运行前述命令时,你的终端输出应该类似于以下内容:
现在我们已经有了数据卷的实例备份,让我们终止 Docker 主机并启动新的主机。如果你还没这样做,exit
离开 Docker 主机,并通过运行以下命令终止它:
docker-machine stop chapter03 && docker-machine rm chapter03
恢复我们的 Convoy 备份
如下图所示,我们已将快照备份存储在 Amazon S3 桶中:
在恢复备份之前,我们需要重新创建我们的 Docker 实例。使用本章前面的部分中提供的在 DigitalOcean 启动 Docker 主机、安装和启动 Convoy 以及设置 AWS 凭证文件的说明。
小贴士
在继续之前,记得将你的浮动 IP 地址重新分配给 Droplet。
一旦你完成备份并且系统运行正常,你应该能够运行以下命令来恢复卷:
sudo convoy create wordpressconvoy_uploads --backup s3://chapter03-backup-bucket@us-west-2/?backup=6cb4ed46-2084-42bc-8261-6b4da690bd5e\u0026volume=26a5913e-4794-4df3-bbb9-7a6361c23a75
你还应该能够运行以下命令:
sudo convoy create wordpressconvoy_database --backup s3://chapter03-backup-bucket@us-west-2/?backup=75608b0b-93e7-4319-b212-7a1b0ccaf289\u0026volume=8212de61-ea8c-4777-881e-d4bd07b800e3
恢复卷的过程将需要几分钟,在此期间,你将看到大量输出流向你的终端。输出应该类似于以下截图:
如你在前面的终端会话结束时所见,恢复过程会从 S3 桶中恢复每个块,因此你会看到这些消息不断滚动。
一旦你恢复了这两个卷,返回到你的 Docker Compose 文件并运行以下命令:
docker-compose up -d
如果一切按计划进行,你应该能够打开浏览器并查看你的内容保持完好,且按你所见的方式显示,使用以下命令:
open http://$(docker-machine ip chapter03)/
提示
别忘了,如果你已经完成了 Docker 主机的操作,你需要使用 docker-machine stop chapter03 && docker-machine rm chapter03
来停止并移除它,否则你可能会产生不必要的费用。
总结 Convoy
Convoy 是一个很好的工具,可以开始查看 Docker 卷,它非常适合快速在不同环境之间移动内容,这意味着你不仅可以共享容器,还可以与其他开发人员或系统管理员共享卷。它的安装和配置也非常简单,因为它以预编译的二进制文件形式发布。
使用 REX-Ray 的块存储
到目前为止,我们已经查看了使用本地存储并备份到远程存储的驱动程序。现在我们将进一步探索,查看直接附加到我们容器的远程存储。
在这个例子中,我们将启动一个 Docker 实例,在 Amazon Web Services 中启动我们的 WordPress 示例,并使用 EMC 提供的卷驱动程序 REX-Ray 将 Amazon Elastic Block Store 卷附加到我们的容器上。
REX-Ray 支持公共云和 EMC 自有存储类型,如下所示:
-
AWS EC2
-
OpenStack
-
Google 计算引擎
-
EMC Isilon, ScaleIO, VMAX 和 XtremIO
该驱动程序正在积极开发中,承诺很快会支持更多类型的存储。
安装 REX-Ray
由于我们将使用 Amazon EBS 卷,我们需要在 AWS 中启动 Docker 主机,因为 EBS 卷不能作为块设备挂载到其他云提供商的实例上。根据前一章内容,这可以使用 Docker Machine 和以下命令完成:
docker-machine create \
--driver amazonec2 \
--amazonec2-access-key JHFDIGJKBDS8639FJHDS \
--amazonec2-secret-key sfvjbkdsvBKHDJBDFjbfsdvlkb+JLN873JKFLSJH \
--amazonec2-vpc-id vpc-35c91750 \
chapter03
切换 Docker Machine 使用新创建的主机:
eval "$(docker-machine env chapter03)"
然后,使用 SSH 进入主机,如下所示:
docker-machine ssh chapter03
一旦你进入 Docker 主机,运行以下命令来安装 REX-Ray:
curl -sSL https://dl.bintray.com/emccode/rexray/install | sh -
这将下载并执行 REX-Ray 最新稳定版本的基本配置:
安装 REX-Ray 后,我们需要配置它以使用 Amazon EBS 卷。作为 root 用户,执行以下操作,在 /etc/rexray/
中添加一个名为 config.yml
的文件:
sudo vim /etc/rexray/config.yml
文件应包含以下内容,记得替换 AWS 凭证的值:
rexray:
storageDrivers:
- ec2
aws:
accessKey: JHFDIGJKBDS8639FJHDS
secretKey: sfvjbkdsvBKHDJBDFjbfsdvlkb+JLN873JKFLSJH
一旦你添加了配置文件,你应该能够直接使用 REX-Ray,运行以下命令应该返回一个 EBS 卷的列表:
sudo rexray volume ls
如果你看到卷的列表,那么你需要启动该过程。如果没有看到卷,请检查你为 accesskey
和 secretkey
提供的用户是否具有读取和创建 EBS 卷的权限。要启动过程并检查一切是否正常,运行以下命令:
sudo systemctl restart rexray
sudo systemctl status rexray
如果一切按预期工作,你应该看到类似以下的终端输出:
安装的最后一步是重新启动实例上的 Docker,以便它识别新的卷驱动程序。为此,运行以下命令:
sudo systemctl restart docker
现在是时候启动一些容器了。我们需要对 Docker Compose 文件做的唯一更改是修改卷驱动程序的名称,其他内容保持不变:
version: '2'
services:
wordpress:
container_name: my-wordpress-app
image: wordpress
ports:
- "80:80"
links:
- mysql
environment:
WORDPRESS_DB_HOST: "mysql:3306"
WORDPRESS_DB_PASSWORD: "password"
volumes:
- "uploads:/var/www/html/wp-content/uploads/"
mysql:
container_name: my-wordpress-database
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: "password"
command: mysqld --ignore-db-dir=lost+found
volumes:
- "database:/var/lib/mysql/"
volumes:
uploads:
driver: rexray
database:
driver: rexray
一旦应用程序启动,运行以下命令设置上传文件夹的权限:
docker exec -d my-wordpress-app chmod 777 /var/www/html/wp-content/uploads/
在 AWS 控制台中,你会注意到现在有一些额外的卷:
通过运行以下命令,在浏览器中打开你新的 WordPress 安装:
open http://$(docker-machine ip chapter03)/
如果你在浏览器中打开 WordPress 站点时遇到问题,请在 AWS 控制台中找到正在运行的实例,并为 port 80
/HTTP
添加规则到 DOCKER-MACHINE 安全组。你的规则应类似于以下图像:
你只需添加一次规则,因为每当你启动更多的 Docker 主机时,Docker Machine 会重新分配 docker-machine
安全组。
一旦页面打开,完成 WordPress 安装并编辑或上传一些内容。你现在应该知道流程了,添加完内容后,是时候停止容器、删除容器,然后终止 Docker 主机:
docker-compose stop
docker-compose rm
在移除主机之前,你可以通过运行以下命令检查卷的状态:
docker volume ls
你将看到类似以下的图像:
最后,是时候移除 Docker 主机了:
docker-machine stop chapter03 && docker-machine rm chapter03
移动 REX-Ray 卷
在我们用 Docker Machine 启动新 Docker 主机之前,值得指出的是,我们的 WordPress 安装可能看起来有些损坏。
这是因为将我们的容器迁移到新的主机会改变我们访问 WordPress 站点的 IP 地址,意味着在你将设置更改为使用第二个节点的 IP 地址之前,你将看到一个损坏的站点。
这是因为它尝试从第一个 Docker 主机的 IP 地址加载内容,如 CSS 和 JavaScript。
关于如何更新这些设置的更多信息,请参阅 WordPress Codex:codex.wordpress.org/Changing_The_Site_URL
。
此外,如果你已经登录到 AWS 控制台,你可能注意到你的 EBS 卷目前没有附加到任何实例:
既然这些都处理好了,让我们使用 Docker Machine 启动新的 Docker 主机。如果你按照上一节的说明启动主机、连接、安装 REX-Ray 并启动 WordPress 和数据库容器,正如我们之前讨论的,你可以通过连接到数据库来更新站点的 IP 地址:
-
如果你想更新 IP 地址,可以运行以下命令。首先,连接到你的数据库容器:
docker exec -ti my-wordpress-database env TERM=xterm bash -l
-
然后使用 MySQL 客户端连接到 MariaDB:
mysql -uroot -ppassword --protocol=TCP -h127.0.0.1
-
切换到
wordpress
数据库:use wordpress;
-
然后最后运行以下 SQL。在我的例子中,
http://54.175.31.251
是旧的 URL,而http://52.90.249.56
是新的 URL:UPDATE wp_options SET option_value = replace(option_value, 'http://54.175.31.251', 'http://52.90.249.56') WHERE option_name = 'home' OR option_name = 'siteurl'; UPDATE wp_posts SET guid = replace(guid, 'http://54.175.31.251','http://52.90.249.56'); UPDATE wp_posts SET post_content = replace(post_content, 'http://54.175.31.251', 'http://52.90.249.56'); UPDATE wp_postmeta SET meta_value = replace(meta_value,'http://54.175.31.251','http://52.90.249.56');
你的终端会话应该类似于以下截图:
不过,我们可以看到内容确实存在,尽管站点看起来像是坏了。
总结 REX-Ray
REX-Ray 仍然处于早期开发阶段,正在不断添加更多功能。在接下来的几个版本中,我预见它将变得越来越有用,因为它正逐渐朝着成为一个集群感知工具而非目前的独立工具发展。
然而,即使在其开发的早期阶段,它仍然是使用 Docker 卷与外部存储的一个极好的入门工具。
Flocker 和 Volume Hub
我们将要看的下一个工具是 ClusterHQ 提供的 Flocker。它无疑是我们将在本章中看到的第三方卷驱动中功能最丰富的。如以下支持的存储选项所示,它涵盖了最广泛的存储后端:
-
AWS Elastic Block Storage
-
OpenStack Cinder 与任何支持的后端
-
EMC ScaleIO、XtremeIO 和 VMAX
-
VMware vSphere 和 vSan
-
NetApp OnTap
-
戴尔存储 SC 系列
-
HPE 3PAR StoreServ 和 StoreVirtual(仅支持 OpenStack)
-
华为 OceanStor
-
Hedvig
-
NexentaEdge
-
ConvergeIO
-
Saratoga Speed
还将很快支持以下存储选项:
-
Ceph
-
Google Persistent Disk
由于大多数人都可以访问 AWS,我们将查看如何在 AWS 上启动 Flocker 集群。
形成你的 Flock
我们将不再亲自手动安装 Flocker,而是将快速了解如何快速启动 Flocker。
本章的这一部分,我们将通过 ClusterHQ 提供的 AWS CloudFormation 模板启动一个集群,快速启动一个 Flocker 集群。
注意
AWS CloudFormation 是亚马逊提供的编排工具,允许你定义你希望的 AWS 基础设施的外观和配置方式。CloudFormation 是免费使用的;但是,你需要为它启动的资源付费。截至撰写时,运行模板一个月的预计费用是 $341.13。有关 CloudFormation 的更多信息,请参考 aws.amazon.com/cloudformation/
,或者要了解费用详情,请参考 calculator.s3.amazonaws.com/index.html#r=IAD&s=EC2&key=calc-D96E035B-5A84-48DE-BF62-807FFE4740A8
。
在启动 CloudFormation 模板之前,我们需要执行一些步骤。首先,您需要创建一个密钥对供模板使用。为此,请登录到 AWS 控制台 console.aws.amazon.com/
,选择您的区域,然后点击 EC2,再在左侧点击密钥对菜单,您创建的密钥对应命名应类似于 flocker-test:
点击创建按钮后,您的密钥对将被下载,请妥善保管,因为以后无法再次下载。现在您已创建并安全下载了密钥对,接下来是时候在 ClusterHQ Volume Hub 上创建一个账户了,您可以访问volumehub.clusterhq.com/
进行注册。
Volume Hub(在写本书时仍处于 Alpha 测试阶段)是一个基于 Web 的接口,用于管理您的 Flocker 卷。您可以使用您的电子邮件地址注册账户,或使用 Google ID 进行登录。
登录/注册后,您将看到一条提示,显示您似乎还没有集群,并提供创建新集群或连接到现有集群的选项:
点击创建新建按钮将弹出一个覆盖层,里面包含了如何使用 AWS CloudFormation 创建集群的说明。由于我们已经完成了第一步,向下滚动到第二步。在这里,您应该看到一个按钮,显示开始 CloudFormation 配置过程,点击它将打开一个新标签页,直接带您进入 AWS 控制台中的 AWS CloudFormation 页面:
启动 AWS CloudFormation 堆栈的第一步是选择模板,这一步已经为我们完成,您可以点击下一步按钮。
接下来,您将需要提供一些关于堆栈的详细信息,包括堆栈名称、EC2 密钥对名称、AWS 访问和密钥秘钥以及您的 Volume Hub 令牌。
要获取您的 Volume Hub 令牌,请访问volumehub.clusterhq.com/v1/token
,系统会展示给您一个令牌。此令牌是唯一与您的 Volume Hub 账户相关联的,请务必不要与他人分享:
填写完详细信息后,您可以点击下一步按钮。在下一页中,系统会要求您为资源添加标签,这是可选的。您应该按照平常的流程为资源打标签。添加完标签后,点击下一步按钮。
提示
请注意,点击创建将会在您的 AWS 账户中启动资源,并产生按小时计费的费用。仅在您打算继续执行接下来的步骤时点击创建。
下一页将展示您提供的详细信息的概览。如果您对这些信息满意,请点击创建按钮。
点击创建按钮后,你将被带回到 AWS CloudFormation 页面,在那里你应该看到你的堆栈状态为CREATE_IN_PROGRESS:
如果你没有看到你的堆栈,点击右上角的刷新图标。通常情况下,创建集群大约需要 10 分钟。在堆栈创建过程中,你可以点击屏幕右下角的一个拆分窗格图标,查看正在进行的事件。
此外,在集群启动时,你应该开始看到节点在你的 Volume Hub 账户中注册。然而,尽管很诱人,还是不要在你的堆栈状态为CREATE_COMPLETE之前使用 Volume Hub。
一旦堆栈部署完成,点击输出标签。这将给你提供连接到集群所需的详细信息。你应该看到类似以下的内容:
我们需要做的第一件事是为之前创建的密钥对设置正确的权限。在我的案例中,它位于我的Downloads
文件夹中:
chmod 0400 ~/Downloads/flocker-test.pem.txt
一旦你设置了权限,你需要使用ubuntu
作为用户名和你的密钥对登录到客户端节点。在我的案例中,客户端节点的 IP 地址是 23.20.126.24:
ssh ubuntu@23.20.126.24 -i ~/Downloads/flocker-test.pem.txt
登录后,你需要运行一些额外的命令来准备集群。为此,你需要记下控制节点的 IP 地址,在前面的屏幕中是54.198.167.2
:
export FLOCKER_CERTS_PATH=/etc/flocker
export FLOCKER_USER=user1
export FLOCKER_CONTROL_SERVICE=54.198.167.2
现在你已经连接到控制服务,你应该能够使用flockerctl
命令查看集群概况:
flockerctl status
flockerctl ls
在运行flockerctl ls
命令时,你不应该看到任何数据集列出。现在我们应该连接到 Docker。为此,运行以下命令:
export DOCKER_TLS_VERIFY=1
export DOCKER_HOST=tcp://$FLOCKER_CONTROL_SERVICE:2376
docker info | grep Nodes
在写这本书时,Flocker AWS CloudFormation 模板安装并配置了 Docker 1.9.1 和 Docker Compose 1.5.2。这意味着你将无法使用新的 Docker Compose 文件格式。然而,在 GitHub 仓库中,应该有旧版和新版格式的 Docker Compose 文件,这些文件伴随本书提供。
你可以在 github.com/russmckendrick/extending-docker/
找到该代码库。
你的终端输出应该看起来类似于以下的会话:
现在一切都已启动运行,让我们使用 Flocker 卷启动我们的 WordPress 安装。
部署到 Flock 中
我们首先要做的是创建卷。我们可以让 Flocker 使用其默认值,即 75 GB 的 EBS 卷,但这对于我们的需求来说有些过大:
docker volume create -d flocker -o size=1G -o profile=bronze --name=database
docker volume create -d flocker -o size=1G -o profile=bronze --name=uploads
如您所见,这是一个更合适的大小,我们选择了与之前示例相同的卷名称。现在我们已经创建了卷,可以启动 WordPress。为此,我们有两个 Docker Compose 文件,一个将在 AgentNode1 上启动容器,另一个将在 AgentNode2 上启动容器。首先,创建一个文件夹来存储文件:
mkdir wordpress
cd wordpress
vim docker-compose-node1.yml
如前所述,在编写本书时,仅支持原始的 Docker Compose 文件格式,因此我们的文件应包含以下内容:
wordpress:
container_name: my-wordpress-app
image: wordpress
ports:
- "80:80"
links:
- mysql
environment:
- "constraint:flocker-node==1"
- "WORDPRESS_DB_HOST=mysql:3306"
- "WORDPRESS_DB_PASSWORD=password"
volume_driver: flocker
volumes:
- "uploads:/var/www/html/wp-content/uploads/"
mysql:
container_name: my-wordpress-database
image: mariadb
environment:
- "constraint:flocker-node==1"
- "MYSQL_ROOT_PASSWORD=password"
command: mysqld --ignore-db-dir=lost+found
volume_driver: flocker
volumes:
- "database:/var/lib/mysql/"
如您所见,它与新格式没有太大区别。需要注意的是绑定容器到节点的行,已在前面的代码中突出显示。
要启动容器,我们必须将文件名传递给 docker-compose
。为此,请运行以下命令:
docker-compose -f docker-compose-node1.yml up -d
docker-compose -f docker-compose-node1.yml ps
启动容器后,运行以下命令以设置 uploads
文件夹的正确权限:
docker exec -d my-wordpress-app chmod 777 /var/www/html/wp-content/uploads/
现在我们已经创建了卷并启动了容器,让我们快速查看 Volume Hub:
如您所见,两个卷已显示为附加到内部 IP 为 10.168.86.184
的节点。查看 Volumes 页面可以提供更多详细信息:
如您所见,我们有关于卷的大小、名称、唯一 ID 以及它附加到哪个节点的信息。我们还可以看到在集群中运行的容器信息:
在我们停止并移除容器之前,您应该配置 WordPress,然后登录并上传一个文件。您可以通过运行以下命令并在浏览器中打开映射到端口 80 的 IP 地址来获取可以访问 WordPress 的 IP 地址:
docker-compose -f docker-compose-node1.yml ps
完成这些更改后,您可以通过运行以下命令来停止并移除容器:
docker-compose -f docker-compose-node1.yml stop
docker-compose -f docker-compose-node1.yml rm -f
现在您已经移除了容器,是时候在第二个节点上启动它们了。您需要创建第二个 Docker Compose 文件,如下所示:
vim docker-compose-node2.yml
wordpress:
container_name: my-wordpress-app
image: wordpress
ports:
- "80:80"
links:
- mysql
environment:
- "constraint:flocker-node==2"
- "WORDPRESS_DB_HOST=mysql:3306"
- "WORDPRESS_DB_PASSWORD=password"
volume_driver: flocker
volumes:
- "uploads:/var/www/html/wp-content/uploads/"
mysql:
container_name: my-wordpress-database
image: mariadb
environment:
- "constraint:flocker-node==2"
- "MYSQL_ROOT_PASSWORD=password"
command: mysqld --ignore-db-dir=lost+found
volume_driver: flocker
volumes:
- "database:/var/lib/mysql/"
如您所见,唯一变化的是节点编号。要启动容器,请运行以下命令:
docker-compose -f docker-compose-node2.yml up -d
启动时间会稍长一些,因为 Flocker 需要取消附加并重新附加卷到第二个节点。一旦容器开始运行,您将看到它们现在显示为附加到第二个节点,在 Volume Hub 中,如下图所示:
这在 Volume Hub 的其他部分也有体现:
最后,您可以在 容器 页面上看到您的新容器:
运行以下命令并在浏览器中打开 IP 地址:
docker-compose -f docker-compose-node2.yml ps
如本章 REX-Rey 部分所提到的,打开 WordPress 时你应该看到一个破损的 WordPress 页面,但这不应该影响你,因为一些内容是从数据库卷中提供的;否则,你将看到安装 WordPress 页面。
所以,这就是你所做的。你使用 Flocker 和 Volume Hub 启动并查看了 Docker 卷,并将它们在主机之间移动。
如本节开头所述,你是按小时收费来运行集群的。要删除集群,你应该进入 AWS 控制台,切换到 CloudFormation 服务,选择你的堆栈,然后从操作下拉菜单中选择删除:
如果你收到无法删除 S3 存储桶的错误,别担心,所有昂贵的东西已经被终止。要解决此错误,只需在 AWS 控制台中找到它所抱怨的 S3 存储桶并删除内容。删除内容后,返回 CloudFormation 页面,再次尝试删除堆栈。
总结 Flocker
Flocker 是 Docker 卷的“祖父”,它在卷插件架构发布之前就是管理卷的原始解决方案之一。这意味着它既成熟,又是我们查看过的所有卷插件中最复杂的。
要了解其复杂性,你可以查看CloudFormation 模板。
如你所见,有很多步骤。在 CloudFormation 可视化工具中查看模板可以让你更清楚地了解所有内容是如何关联的:
再加上 Docker 本身也在定期更新,你就拥有了一个非常复杂的安装过程。这也是为什么我在本章中没有详细讲解如何手动安装它,因为到你阅读时,过程无疑会有所变化。
幸运的是,Cluster Labs 有一个非常好的文档,且定期更新。你可以在docs.clusterhq.com/en/latest/
找到它。
同时值得指出的是,在写这本书时,Volume Hub 仍处于早期 Alpha 阶段,功能正在不断添加。最终,我认为这将成为一个非常强大的工具组合。
总结
在本章中,我们查看了三种不同的卷驱动程序,它们都与 Docker 的插件架构兼容。
尽管这三种驱动程序提供了三种完全不同的方式来为容器提供持久化存储,但你可能已经注意到,Docker Compose 文件以及我们如何通过 Docker 客户端与卷交互的方式,在所有三种工具中几乎是一样的体验,可能到了一种我敢肯定它开始显得有点重复的地步。
在我看来,这种重复性展示了使用 Docker 插件的最佳特性之一——从客户端角度来看的一致体验。在我们配置好工具后,在任何时刻,我们都无需真正思考或考虑如何使用存储,我们只需要继续进行。
这使我们能够在多个环境中重用我们的资源,如 Docker Compose 文件和容器,包括本地虚拟机、基于云的 Docker 主机,甚至是 Docker 集群。
然而,目前我们仍然局限于单一的 Docker 主机。 在下一章中,我们将探讨如何通过查看 Docker 网络插件来开始跨多个 Docker 主机进行部署。
第四章:网络插件
在本章中,我们将介绍下一种插件类型:网络。我们将讨论如何使用 Docker 1.9 引入的新网络工具,以及第三方工具,这些工具为已经强大的内置工具增加了更多功能。我们将关注的两个主要工具如下:
-
Docker 覆盖网络:
docs.docker.com/engine/userguide/networking/dockernetworks/
-
Weave:
weave.works/
注意
本章假设您使用的是 Docker 1.10+ 版本,某些命令可能在旧版本中无法使用。
Docker 网络
在我们开始详细讨论 Docker 中的网络之前,我应该提到,我们已经顺利进入本书的第四章,而不需要真正思考网络问题,这是因为 Docker 默认在容器和主机机器的网络接口之间创建了一个网络桥接。这是 Docker 网络的最基本形式。
就像基本存储一样,这限制了您在单个主机上启动容器,即使您使用了像 Docker Swarm 这样的集群工具,正如您在第二章,引入第一方工具中所看到的,当我们启动 WordPress 安装时,Web 和数据库容器都是在集群中的单个主机上启动的。如果我们尝试将这两个容器绑定到不同的主机,它们将无法相互通信。
幸运的是,Docker 为您提供了支持,并提供了自己的多主机网络层,供 Docker Swarm 使用。
多主机网络与覆盖网络
Docker 在 Docker 1.9 中发布了其生产就绪的多主机覆盖网络功能。在此版本之前,这个功能被视为实验性的。
注意
覆盖网络是一种建立在另一个网络之上的计算机网络。覆盖网络中的节点可以被认为是通过虚拟或逻辑链路连接的,每条链路对应一个路径,可能通过许多物理链路,在底层网络中传输:
en.wikipedia.org/wiki/Overlay_network
在 Docker 术语中,它允许一个 Docker 主机上的容器直接与另一个 Docker 主机上的容器通信,就好像它们在同一主机上一样,如下图所示:
正如您从前面的图示中看到的,首先有一些前提条件。首先,您必须运行一个 Docker Swarm 集群。在这里,我们有一个由两个节点和一个主节点组成的 Docker Swarm 集群,它们都已配置了覆盖网络。您还需要一个服务发现服务,该服务可以被 Docker Swarm 集群访问。为此,您可以使用以下应用程序:
-
Consul:
www.consul.io/
-
Etcd:
coreos.com/etcd/
-
ZooKeeper:
zookeeper.apache.org/
在本章中,我们将使用 HashiCorp 的 Consul(hashicorp.com/
),并且我们还将通过 Docker Machine 在 DigitalOcean 上启动我们的集群。
启动发现
在第二章,介绍第一方工具,我们通过 Docker Hub 中的一次性令牌启动了我们的 Docker Swarm 集群。多主机网络的一个要求是持久化的键值存储,以便我们有一个永久且可访问的地方来存储有关集群的值,我们将在我们的示例集群中使用 Consul 来提供这个功能。
Consul 是由 HashiCorp 编写的开源工具,用于在基础设施中发现和配置服务。它提供了多个关键功能,包括服务发现、健康检查和键值存储,并且能够支持多数据中心。
要启动将运行 Consul 的 Docker 主机,运行以下命令:
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 512mb \
--digitalocean-private-networking \
service-discovery
你可能会注意到我们在docker-machine
命令中添加了一个额外的行,这样会启动具有私有网络的 DigitalOcean Droplet。一旦 Docker 主机启动完成,我们可以通过运行以下命令启动 Consul 服务:
docker $(docker-machine config service-discovery) run -d \
-p "8400:8400" \
-p "8500:8500" \
-h "consul" \
russmckendrick/consul agent -data-dir /data -server -bootstrap-expect 1 -ui-dir /ui -client=0.0.0.0
这将下载我的 Consul 容器镜像,现在也有一个官方镜像,可以在hub.docker.com/_/consul/
找到;然而,由于这个镜像是新的,可能不适用于前面的示例。
由于这是我们在此主机上需要运行的唯一命令,我们没有配置本地的 Docker 客户端以使用该主机;相反,我们是在运行时通过$(docker-machine config service-discovery)
传递配置。为了检查一切是否按预期运行,你可以运行以下命令:
docker $(docker-machine config service-discovery) ps
在这里,你应该看到一个正在运行的容器,类似于以下终端输出:
注意
在我们进一步推进之前,应该注意,使用-bootstrap-expect 1
标志启动 Consul 不应在生产环境中尝试。你应该考虑引入多个 Consul 主机。有关如何配置高度可用的 Consul 集群的更多信息,请参考以下网址,了解如何配置完整的 Consul 集群:
www.consul.io/docs/guides/bootstrapping.html
你还可以通过打开 Web 界面来了解 Docker 将会在 Consul 中存储哪些信息,方法是输入以下命令:
open http://$(docker-machine ip service-discovery):8500/ui
你应该看到一个几乎空白的 Consul 视图,如下图所示:
一旦启动了 Docker Swarm 集群并且服务发现容器正在运行并可以访问,我们将返回 Web 界面,开始启动其余的集群部分。
准备 Swarm
让我们开始启动 Docker Swarm 集群,首先是 Swarm 主节点。我们将其命名为chapter04-00
:
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 1gb \
--digitalocean-private-networking \
--swarm --swarm-master \
--swarm-discovery="consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-store=consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-advertise=eth1:2376" \
chapter04-00
如你所见,命令与第二章中的命令非常相似,介绍第一方工具;然而,我们这里提供了 Consul 安装的详细信息。我们通过使用docker-machine ip
命令传递service-discovery
主机的 IP 地址来做到这一点。
一旦 Swarm 主节点启动,我们将使用以下命令启动两个 Swarm 节点:
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 1gb \
--digitalocean-private-networking \
--swarm \
--swarm-discovery="consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-store=consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-advertise=eth1:2376" \
chapter04-01
对于第二个节点,我们将使用以下命令:
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 1gb \
--digitalocean-private-networking \
--swarm \
--swarm-discovery="consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-store=consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-advertise=eth1:2376" \
chapter04-02
现在我们已经启动了主节点和两个节点,让我们切换到环境中,并确保集群显示正确的主机数量:
eval $(docker-machine env --swarm chapter04-00)
docker info
运行docker info
时,你应该会看到类似于以下截图的内容:
所以,现在我们已经启动了集群,所有节点都能相互通信。接下来,我们将能够创建我们的覆盖网络。
添加覆盖网络
为了测试,我们将创建一个非常基础的网络并启动一个非常基础的容器。以下命令将创建覆盖网络,并且得益于 Consul 提供的服务发现,网络设置将分发到 Docker Swarm 集群中的每个节点:
docker network create --driver overlay --subnet=10.0.9.0/24 chapter04-overlay-network
如此,我们已经在集群中创建了一个名为chapter04-overlay-network
的覆盖网络,子网为10.0.9.0/24
。为了确保一切正常,你可以运行以下命令来列出集群中配置的网络:
docker network ls
你也可以通过运行以下命令来检查各个节点:
docker $(docker-machine config chapter04-01) network ls
docker $(docker-machine config chapter04-02) network ls
如你所见,每个节点都有自己的主机和桥接网络,这意味着如果你不想使用覆盖网络,完全可以不使用;不过,我们使用它,这样就可以启动一个容器并配置它使用我们新添加的网络。
使用覆盖网络
首先,我们将启动一个运行 NGINX 的容器:
docker run -itd \
--name=chapter04-web \
--net=chapter04-overlay-network \
-p 80:80 \
--env="constraint:node==chapter04-01" \
russmckendrick/nginx
如你所见,我们通过传递--net
标志来配置容器使用chapter04-overlay-network
。我们还确保容器在chapter04-01
节点上启动。接下来,让我们看看能否查看到 NGINX 容器提供的内容。
为此,我们将在第二个节点chapter04-02
上启动一个容器,并运行wget
来获取 NGINX 提供的页面:
docker run -it \
--rm \
--net=chapter04-overlay-network \
--env="constraint:node==chapter04-02" \
russmckendrick/base wget -q -O- http://chapter04-web
如果一切按计划进行,你将会看到命令返回Hello from NGINX
。我们也可以通过在第二个节点上运行以下命令来 ping NGINX 容器:
docker run -it \
--rm \
--net=chapter04-overlay-network \
--env="constraint:node==chapter04-02" \
russmckendrick/base ping -c 3 chapter04-web
你应该能看到一个 IP 地址,位于 10.0.9.0/24 子网范围内,正如下面的截图所示:
如果你想查看已经配置在chapter04-web
容器上的网络,可以运行以下命令:
docker exec chapter04-web ip addr
docker exec chapter04-web route -n
docker exec chapter04-web ping -c 3 google.com
你应该会看到类似于以下终端输出的内容:
最后,你可以通过运行以下命令在浏览器中访问容器:
open http://$(docker-machine ip chapter04-01)/
页面将会类似于以下截图所示:
虽然页面本身看起来并不复杂,但实际上后台有一些相当巧妙的操作你可能没有注意到,其中最大的一点是我们不再需要手动链接我们的容器。在前几章中,我们在启动多个容器时使用了 link 标志来将它们链接在一起。现在,我们在同一个 Overlay 网络中启动容器,Docker 假设这个网络中的所有容器都能相互通信,并且它会自动处理容器之间的链接。
Docker 还为容器配置了一个网关,以便能够默认将流量路由到我们的 Overlay 网络外部。如果你想创建一个仅限内部的网络,可以添加--internal
标志。
返回到 Consul
别忘了,在我们创建网络和启动容器时,服务发现容器一直在后台运行。返回到 Consul 的 Web 界面,你应该会注意到在Key/Value选项下,你会看到 Docker Swarm 集群内节点的列表:
点击查看,你还应该看到其他一些共享的值,例如网络相关的,这些都在 Docker Swarm 集群中共享:
在我们拆除 Docker Swarm 集群之前,让我们先看看如何使用 Docker Compose 启动 WordPress 栈。
创建多主机网络
如同前几章一样,我们将启动我们可靠的 WordPress 安装。我们将稍微有些不同,添加一些有趣的部分:
-
创建一个名为
wpoutside
的外部网络。这个网络将能够进行外部访问,我们的网站服务器将会在这里启动。 -
创建一个名为
wpinside
的内部网络。这个网络无法进行外部访问,但同一网络上的容器能够相互访问,我们将把网站服务器和数据库容器添加到这个网络中。 -
启动我们的网站服务器容器在一个节点上,数据库容器在第二个节点上。
在我们启动容器之前,我们应该终止chapter04-web
容器:
docker rm -f chapter04-web
现在,让我们创建两个覆盖网络:
docker network create --driver overlay --subnet=10.0.10.0/24 wpoutside
docker network create --driver overlay --internal --subnet=10.0.11.0/24 wpinside
如您所见,我们为网络分配了不同的子网,而对于wpinside
,我们传递了--internal
标志,这意味着该网络将没有外部网关。
现在,让我们来看一下我们的docker-compose.yml
文件:
version: '2'
services:
wordpress:
container_name: my-wordpress-app
image: wordpress
ports:
- "80:80"
networks:
- wpoutside
- wpinside
environment:
- "WORDPRESS_DB_HOST=mysql:3306"
- "WORDPRESS_DB_PASSWORD=password"
- "constraint:node==chapter04-01"
volumes:
- "uploads:/var/www/html/wp-content/uploads/"
mysql:
container_name: my-wordpress-database
image: mysql
networks:
- wpinside
environment:
- "MYSQL_ROOT_PASSWORD=password"
- "constraint:node==chapter04-02"
volumes:
- "database:/var/lib/mysql"
volumes:
uploads:
driver: local
database:
driver: local
networks:
wpoutside:
external: true
wpinside:
external: true
如您所见,我已经标出了自上一章以来文件中的更改。有趣的是,尽管可以在docker-compose.yml
文件中定义网络,但通过使用docker network create
命令设置网络,您将获得更多的控制权。为此,我们需要告诉 Docker Compose 使用为项目外部定义的网络。我们还使用标签将容器绑定到我们 Docker Swarm 集群中的主机。
现在我们已经创建了两个覆盖网络,您可以通过运行以下命令启动 WordPress 堆栈:
docker-compose up -d
您可以通过运行以下命令检查一切是否按预期启动:
docker-compose ps
为了确保容器在不同主机上启动,请运行以下命令并检查最后一列:
docker ps
要查看分配给容器的 IP 地址,请运行以下命令:
docker inspect my-wordpress-app | grep IPAddress
docker inspect my-wordpress-database | grep IPAddress
您应该会看到my-wordpress-app
的两个 IP 地址和my-wordpress-database
的单个 IP 地址:
在我们登录 WordPress 之前,可以尝试一些 ping 测试。首先,我们将在您的my-wordpress-app
容器上运行以下命令:
docker exec my-wordpress-app ping -c 3 google.com
docker exec my-wordpress-app ping -c 3 my-wordpress-database
对于第一条命令,您将看到返回的谷歌外部 IP 地址。对于第二条命令,您将获得my-wordpress-database
容器的 IP 地址,它位于我们为wpinside
覆盖网络定义的10.0.11.0/24
子网中:
在my-wordpress-database
上尝试类似命令应该会给您不同的结果,尝试运行以下命令:
docker exec my-wordpress-database ping -c 3 my-wordpress-app
docker exec my-wordpress-database ping -c 3 google.com
如您所见,ping my-wordpress-app
正常工作;但是,当您尝试 ping Google 时,会收到类似“网络无法访问”或其他错误的消息。这正是我们预期的结果,因为my-wordpress-database
没有外部网络访问权限,因此无法路由到www.google.com
:
最后,如果您想访问 WordPress,可以输入以下命令之一。首先,我们需要确认my-wordpress-app
容器启动在哪个主机上。要确认主机,请运行:
docker ps
然后,根据不同的主机,运行以下三条命令之一:
open http://$(docker-machine ip chapter04-00)/
open http://$(docker-machine ip chapter04-01)/
open http://$(docker-machine ip chapter04-02)/
您的浏览器将打开现在熟悉的 WordPress 安装页面。
在继续之前,您应该拆除 Docker Swarm 集群。为此,请运行以下命令:
docker-machine stop chapter04-00 chapter04-01 chapter04-02 service-discovery
docker-machine rm chapter04-00 chapter04-01 chapter04-02 service-discovery
总结多主机网络
尽管在 Docker 1.9 版本中,覆盖网络被视为生产就绪,但随着 Docker 1.10 版本的进步以及新的 Docker Compose v2 文件格式的推出,Docker 网络真的得到了更好的发展。
虽然覆盖网络功能已内置于 Docker 和 Swarm 中,正如我们在之前的示例中所看到的,它非常强大。当与我们在第三章中介绍的第三方卷插件、卷插件以及 Docker Swarm 结合使用时,我们可以开始构建高可用的部署。
编织网络
接下来,我们将看看 Weave Net 和 Weaveworks 的 Scope。这是最初的 Docker 网络工具之一,其核心是一个成熟的软件定义网络服务。
Weave Net 的描述如下:
“Weave Net 创建了一个容器 SDN,可以跨任何公共和私有云、虚拟机以及裸机运行。容器 SDN 可以承载任何二层和三层流量,包括多播。如果它可以通过以太网运行,您就可以在 Weave Net 上运行。”
事实上,Weave 提供了两个驱动,如下所示:
-
Weave Mesh 是一个本地作用域驱动,它在不需要集群存储的情况下运行。它可以用来创建跨非集群机器的网络。通过这种方式,您可以获得一个名为 Weave 的单一网络,覆盖您启动 Weave 的所有机器。
-
Weave 像 Docker 自己的覆盖驱动一样,是一个全局作用域驱动。这意味着它可以与 Docker Swarm 和 Docker Compose 一起使用,因此,您需要启动一个集群存储。
首先,我们来看一下 Weave 驱动及其如何与 Docker Swarm 一起使用,然后我们将看看如何使用 Weavemesh 驱动。
再次配置集群
像 Docker 的多主机网络一样,我们需要启动一个服务发现实例和我们的 Swarm 集群。让我们使用 Docker Machine 启动服务发现主机:
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 512mb \
--digitalocean-private-networking \
service-discovery
这次,我们不需要启用 Consul web 界面,因此运行以下命令:
docker $(docker-machine config service-discovery) run -d \
-p "8400:8400" \
-p "8500:8500" \
-h "consul" \
russmckendrick/consul agent -data-dir /data -server -bootstrap-expect 1 -client=0.0.0.0
现在启动 Docker Swarm 集群,首先是主节点:
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 1gb \
--digitalocean-private-networking \
--swarm --swarm-master \
--swarm-discovery="consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-store=consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-advertise=eth1:2376" \
chapter04-00
然后我们将启动我们的第一个节点:
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 1gb \
--digitalocean-private-networking \
--swarm \
--swarm-discovery="consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-store=consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-advertise=eth1:2376" \
chapter04-01
最后,我们将启动第二个节点:
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 1gb \
--digitalocean-private-networking \
--swarm \
--swarm-discovery="consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-store=consul://$(docker-machine ip service-discovery):8500" \
--engine-opt="cluster-advertise=eth1:2376" \
chapter04-02
为了检查一切是否按预期工作,运行以下命令切换我们的本地 Docker 客户端连接到 Swarm 集群,并检查三个节点是否可见:
eval $(docker-machine env --swarm chapter04-00)
docker info
安装和配置 Weave
现在我们的集群已经启动并运行,我们可以安装和配置 Weave。安装 Weave 非常简单,您只需下载二进制文件并给予正确的权限。在 Swarm 主节点上使用docker-machine ssh
连接到主机并运行install
命令来完成此操作:
docker-machine ssh chapter04-00 'curl -L git.io/weave -o /usr/local/bin/weave; chmod a+x /usr/local/bin/weave'
接下来,我们启动 Weave,再次使用docker-machine ssh
,我们可以运行以下命令:
docker-machine ssh chapter04-00 weave launch --init-peer-count 3
您会注意到,Weave 从 Docker Hub 部署了三个容器,它们如下所示:
-
weaveworks/weaveexec
-
weaveworks/weave
-
weaveworks/plugin
此外,我们告诉 Weave 预期有三个节点加入集群,通过传递--init-peer-count 3
标志来实现,这基本上就是我们在第一个集群节点上配置 Weave 所需做的全部。
接下来,我们需要在其他两个集群节点上安装 Weave,仍然使用 docker-machine ssh
命令运行以下内容:
docker-machine ssh chapter04-01 'curl -L git.io/weave -o /usr/local/bin/weave; chmod a+x /usr/local/bin/weave'
docker-machine ssh chapter04-01 weave launch --init-peer-count 3
既然 Weave 已经在节点上启动并运行,我们需要告诉它连接到 Swarm 主节点上运行的 Weave 安装。为此,请运行以下命令:
docker-machine ssh chapter04-01 weave connect "$(docker-machine ip chapter04-00)"
然后在我们最后一个集群节点上,我们将运行以下命令:
docker-machine ssh chapter04-02 'curl -L git.io/weave -o /usr/local/bin/weave; chmod a+x /usr/local/bin/weave'
docker-machine ssh chapter04-02 weave launch --init-peer-count 3
docker-machine ssh chapter04-02 weave connect "$(docker-machine ip chapter04-00)"
一旦 Swarm 集群中的所有三个节点都安装并配置了 Weave,我们将运行以下命令,以确保所有三个节点能够相互通信:
docker-machine ssh chapter04-00 weave status
该命令应该返回确认信息,显示有三个对等节点以及六个已建立的连接,并附带其他安装信息,如以下截图所示:
现在我们已经确认一切按预期工作,我们将使用以下命令列出 Docker 中的网络:
docker network ls
根据以下终端会话,你应该会看到集群中每个节点都有一个名为 weave
的 weavemesh
网络;我们稍后会进一步讨论这点:
Docker Compose 和 Weave
所以,让我们启动我们的 WordPress 安装。Docker Compose 文件看起来与 Overlay 网络的文件略有不同:
version: '2'
services:
wordpress:
container_name: "my-wordpress-app"
image: wordpress
ports:
- "80:80"
environment:
- "WORDPRESS_DB_HOST=mysql.weave.local:3306"
- "WORDPRESS_DB_PASSWORD=password"
- "constraint:node==chapter04-01"
hostname: "wordpress.weave.local"
dns: "172.17.0.1"
dns_search: "weave.local"
volumes:
- "uploads:/var/www/html/wp-content/uploads/"
mysql:
container_name: "my-wordpress-database"
image: mysql
environment:
- "MYSQL_ROOT_PASSWORD=password"
- "constraint:node==chapter04-02"
hostname: "mysql.weave.local"
dns: "172.17.0.1"
dns_search: "weave.local"
volumes:
- "database:/var/lib/mysql"
volumes:
uploads:
driver: local
database:
driver: local
networks:
default:
driver: weave
我突出显示了 Overlay Docker Compose 文件中的一些更改:首先,我们将定义一个主机名并提供 DNS 服务器和搜索域。为了获得 dns
和 dns_search
键的正确值,你可以运行以下命令,让 Weave 告诉你它已配置的内容:
docker-machine ssh chapter04-00 weave dns-args
如你所见,在我的情况下,它返回了172.17.0.1
和weave.local
:
此外,对于从 WordPress 容器到数据库容器的 MySQL 连接,我们也在使用内部 DNS 名称。
我们还让 Docker Compose 为我们创建一个使用 Weave 驱动程序的网络,这将添加一个以项目命名的单一网络。Docker Compose 从 Docker Compose 文件所在的文件夹中获取项目名称,在我的例子中,它是一个名为 wordpress
的文件夹。
要启动你的容器并检查它们是否按预期运行,请运行以下命令:
docker-compose up -d
docker-compose ps
docker ps
你应该会看到类似于以下终端输出的内容:
如果你真的想要,你可以通过运行以下命令访问你的 WordPress 安装:
open http://$(docker-machine ip chapter04-01)/
在后台有一些 Docker 的多主机网络无法提供的功能,例如内部 DNS。Weave 有自己的内部 DNS 系统,你可以在其中注册你的容器,正如我们在 Docker Compose 文件中为两个容器提供的记录详细信息所示。运行以下命令:
docker-machine ssh chapter04-00 weave status dns
它将显示 Weave 配置的所有 DNS 记录。在我的情况下,它看起来像以下截图:
Weave Scope
当我们的三节点 Swarm 集群启动并运行时,快速安装 Scope。Scope 是一个用于可视化你的容器和主机的工具。我们将只在本地安装它,但 Weave Works 会提供基于云的服务,可以在 scope.weave.works/
找到(在编写本书时,它还处于私有测试阶段)。
类似于我们安装 Weave Net 的方式,我们将使用 docker-machine ssh
命令来下载二进制文件并启动和配置服务。
我们首先在 Swarm 主节点上编写代码:
docker-machine ssh chapter04-00 'curl -L git.io/scope -o /usr/local/bin/scope; chmod a+x /usr/local/bin/scope'
docker-machine ssh chapter04-00 scope launch
然后,我们将为剩下的两个节点编写代码:
docker-machine ssh chapter04-01 'curl -L git.io/scope -o /usr/local/bin/scope; chmod a+x /usr/local/bin/scope'
docker-machine ssh chapter04-01 scope launch $(docker-machine ip chapter04-00)
docker-machine ssh chapter04-02 'curl -L git.io/scope -o /usr/local/bin/scope; chmod a+x /usr/local/bin/scope'
docker-machine ssh chapter04-02 scope launch $(docker-machine ip chapter04-00)
如你所见,在剩下的两个节点上,我们正在告诉 Scope 连接到在 Swarm 主节点上运行的 Scope 实例。
现在 Scope 已经安装,运行以下命令在浏览器中打开它:
open http://$(docker-machine ip chapter04-00):4040/
当你的浏览器打开时,你将看到一个关于你 Swarm 集群和正在运行的容器的可视化表示。
我在这里不打算深入讲解 Scope,因为目前它与网络配置关系不大,先浏览一下你的集群,看看它是如何连接在一起的。我的界面看起来类似于下面的截图:
取消 Swarm
如你所见,虽然 Weave 是一个功能强大的 SDN,但它的配置非常简单。然而,复制 Docker 提供的多主机网络仅仅是它的一个小技巧。
在开始查看 Weavemesh 网络驱动之前,让我们关闭我们的 Swarm 集群并终止主机:
docker-machine stop chapter04-00 chapter04-01 chapter04-02 service-discovery
docker-machine rm chapter04-00 chapter04-01 chapter04-02 service-discovery
在继续之前,登录到你的 DigitalOcean 控制面板,确保没有标记为 chapter04
的机器正在运行,记住,无论你是否使用这些机器,都会按小时收费。
Weavemesh 驱动
我们已经了解了如何将 Weave Net 与 Docker Swarm 集群一起使用来创建多主机网络,现在让我们来看看第二个 Weave 网络驱动——Weavemesh。正如你可能记得的那样,当我们第一次安装 Weave Net 时,系统会自动创建一个名为 “weave” 的网络,并使用 “weavemesh” 驱动程序在我们集群的每个节点上配置。
这一次,让我们使用 Docker Machine 在 DigitalOcean 上启动两个独立的 Docker 主机。为了增加趣味性,我们将在伦敦启动一台主机,另一个则在纽约市。由于这些将作为独立主机运行,我们不需要启动键值存储,也不需要配置 Docker Swarm。
首先,输入以下命令来启动一个位于伦敦的主机:
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region lon1 \
--digitalocean-size 1gb \
mesh-london
然后,下面的命令是用来启动另一个位于纽约市的主机。
docker-machine create \
--driver digitalocean \
--digitalocean-access-token sdnjkjdfgkjb345kjdgljknqwetkjwhgoih314rjkwergoiyu34rjkherglkhrg0 \
--digitalocean-region nyc2 \
--digitalocean-size 1gb \
mesh-nyc
现在我们的两台 Docker 主机都已启动并运行,接下来让我们安装并配置 Weave:
docker-machine ssh mesh-london 'curl -L git.io/weave -o /usr/local/bin/weave; chmod a+x /usr/local/bin/weave'
docker-machine ssh mesh-london weave launch --password 3UnFh4jhahFC
如你所见,这次我们告诉 Weave 启动时使用密码。这个标志将启用我们两个主机之间网络层的加密。现在我们已经配置了伦敦主机,让我们配置纽约市的主机,并让它与伦敦主机进行通信:
docker-machine ssh mesh-nyc 'curl -L git.io/weave -o /usr/local/bin/weave; chmod a+x /usr/local/bin/weave'
docker-machine ssh mesh-nyc weave launch --password 3UnFh4jhahFC
docker-machine ssh mesh-nyc weave connect "$(docker-machine ip mesh-london)"
现在我们已经在两台主机上配置了 Weave,我们可以通过运行以下命令来检查 Weave 的状态:
docker-machine ssh mesh-nyc weave status
如你所见,从下面的终端输出可以看出,已启用加密,并且我们在 Weave 网络中有两个对等节点:
所以,让我们看看 Weave 的绝招。我们将从启动我们的 NGINX 容器开始,保持简单:
docker $(docker-machine config mesh-nyc) run -itd \
--name=nginx \
--net=weave \
--hostname="nginx.weave.local" \
--dns="172.17.0.1" \
--dns-search="weave.local" \
russmckendrick/nginx
现在我们可以检查容器是否已启动并正在运行:
docker $(docker-machine config mesh-nyc) ps
让我们检查它是否在端口 80 上响应:
docker $(docker-machine config mesh-london) run -it \
--rm \
--net=weave \
--dns="172.17.0.1" \
--dns-search="weave.local" \
russmckendrick/base wget -q -O- http://nginx.weave.local
最后,让我们进行一次 ping 测试:
docker $(docker-machine config mesh-london) run -it \
--rm \
--net=weave \
--dns="172.17.0.1" \
--dns-search="weave.local" \
russmckendrick/base ping -c 3 nginx.weave.local
你的终端会话应该看起来像下面的截图:
从表面上看,这个测试似乎没有什么特别的;然而,如果你仔细查看我们使用的命令,你会看到 weavemesh 驱动程序的强大功能。
首先,当我们在纽约市的 Docker 主机上启动 NGINX 容器时,我们没有发布任何端口,这意味着端口 80 只在我们附加的 weave 网络上可用。
其次,当我们在端口 80 上进行检查并进行 ping 测试时,是在伦敦的 Docker 主机上进行的。我们临时启动了一个基本容器,将其附加到 weave
网络,并配置它使用 Weave DNS 服务,这样它就可以解析 nginx.weave.local
域名。
让我们再次进行测试,这次使用本地虚拟机:
docker-machine create -d virtualbox mesh-local
现在,按照我们在其他两个 Docker 主机上所做的步骤安装 Weave:
docker-machine ssh mesh-local 'sudo curl -L git.io/weave -o /usr/local/bin/weave; sudo chmod a+x /usr/local/bin/weave'
docker-machine ssh mesh-local sudo weave launch --password 3UnFh4jhahFC
docker-machine ssh mesh-local sudo weave connect "$(docker-machine ip mesh-london)"
然后再次运行测试:
docker $(docker-machine config mesh-local) run -it \
--rm \
--net=weave \
--dns="172.17.0.1" \
--dns-search="weave.local" \
russmckendrick/base wget -q -O- http://nginx.weave.local
运行 ping 测试,如下所示:
docker $(docker-machine config mesh-local) run -it \
--rm \
--net=weave \
--dns="172.17.0.1" \
--dns-search="weave.local" \
russmckendrick/base ping -c 3 nginx.weave.local
如你所见,它工作正常!
现在,我们的 Weavemesh 网络中有三台 Docker 主机,它们都可以互相通信。为了证明这一点,我们将进行最后一次测试。让我们在本地 Docker 主机上启动一个容器,并尝试从纽约市主机进行测试。
在我们的本地 Docker 主机上创建一个名为 vm.weave.local
的 NGINX 容器:
docker $(docker-machine config mesh-local) run -itd \
--name=vm \
--net=weave \
--hostname="vm.weave.local" \
--dns="172.17.0.1" \
--dns-search="weave.local" \
russmckendrick/nginx
然后尝试从纽约市的 Docker 主机连接到端口 80 并 ping 新容器:
docker $(docker-machine config mesh-nyc) run -it \
--rm \
--net=weave \
--dns="172.17.0.1" \
--dns-search="weave.local" \
russmckendrick/base wget -q -O- http://vm.weave.local
docker $(docker-machine config mesh-nyc) run -it \
--rm \
--net=weave \
--dns="172.17.0.1" \
--dns-search="weave.local" \
russmckendrick/base ping -c 3 vm.weave.local
我的终端会话看起来像下面的截图:
现在,由于没有 Docker Swarm 集群的常量,我们也可以开始执行一些只有在 Swarm 外部才可用的任务。
首先,在启动容器后将其连接到 Weave 网络,让我们在伦敦的 Docker 主机上启动一个名为 lonely
的 NGINX 容器:
docker $(docker-machine config mesh-london) run -itd \
--name=lonely \
russmckendrick/nginx
现在,让我们连接到伦敦的 Docker 主机并将容器连接到 weave 网络:
docker-machine ssh mesh-london weave attach lonely
当你运行命令时,它会返回一个 IP 地址。这将是我们容器的新 IP 地址;在我的例子中,它是 10.40.0.0。让我们从纽约市和本地 Docker 主机运行我们的测试:
docker $(docker-machine config mesh-nyc) run -it \
--rm \
--net=weave \
--dns="172.17.0.1" \
--dns-search="weave.local" \
russmckendrick/base wget -q -O- 10.40.0.0
docker $(docker-machine config mesh-local) run -it \
--rm \
--net=weave \
--dns="172.17.0.1" \
--dns-search="weave.local" \
russmckendrick/base ping -c 3 10.40.0.0
你的终端会话应该类似于下面的截图:
既然我们的容器已接入网络,我们可以通过运行以下命令手动为主机添加 DNS:
docker-machine ssh mesh-london weave dns-add lonely -h lonely.weave.local
如你所见,我们现在可以通过lonely.weave.local
从我们的纽约市 Docker 主机访问端口 80:
docker $(docker-machine config mesh-nyc) run -it \
--rm \
--net=weave \
--dns="172.17.0.1" \
--dns-search="weave.local" \
russmckendrick/base wget -q -O- lonely.weave.local
唯一的缺点是,无法轻松地为我们已连接到 “weave” 网络的主机添加 DNS 解析。
既然我们已经完成了 Docker 主机的配置,接下来让我们终止它们,以避免不必要的费用:
docker-machine stop mesh-local mesh-london mesh-nyc
docker-machine rm mesh-local mesh-london mesh-nyc
再次提醒,请检查你的 DigitalOcean 控制面板,确保主机已正确终止。
Weave 总结
正如你所看到的,我也已经提到过,Weave 是一个极其强大的软件定义网络,配置起来非常简单。根据我的经验,这是一个难得的组合,因为大多数 SDN 解决方案的安装、配置和维护都非常复杂。
我们只是初步接触了 “weave” 和 “weavemesh” 驱动的可能性。有关完整的功能列表以及一些高级用例的说明,请参考docs.weave.works/weave/latest_release/features.html
。
总结
在本章中,我们探讨了三种不同的网络驱动,它们都为你的基本 Docker 安装增添了非常强大的功能。与卷驱动一起,这些驱动真正扩展了 Docker,使你能够运行大规模的容错容器集群。
就我个人而言,当我第一次安装 Weave 并开始在不同托管提供商的 Docker 主机上轻松地与容器进行通信时,我简直是惊呆了。
在下一章,我们将探讨如何着手创建你自己的扩展。