使用-Docker-构建你指的-PAAS-全-

使用 Docker 构建你指的 PAAS(全)

原文:annas-archive.org/md5/339a09078e74fae7000ff71502fc65b8

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

Docker 是一个开源项目,提供了一个高层 API,可以通过软件容器在隔离的环境中运行进程。将应用打包到容器中,使其能够在任何 Linux 服务器(以及 OS X 和 Windows 上)上运行,帮助开发人员专注于开发应用,而不是服务器设置和其他 DevOps 操作。

本书涵盖的内容

第一章,安装 Docker,带你完成 Docker 的安装过程,以启动一个容器。

第二章,探索 Docker,让你了解 Docker 的工作原理及其使用的术语,并介绍公共镜像。

第三章,创建我们的第一个 PaaS 镜像,展示了如何创建属于你自己的定制 Docker 镜像,这个镜像将成为你 PaaS 的一部分。

第四章,给容器提供数据和参数,教你了解可用的数据存储替代方案以及如何将参数传递给你的 PaaS 容器。

第五章,连接容器,展示了如何手动连接容器,以形成一个完整的平台,并介绍了两种工具,它们可以让你更好地控制多容器平台。

第六章,反向代理请求,解释了这个问题,并提供了在同一主机上有多个容器时的解决方案,其中多个主机应该能够在同一端口上访问。

第七章,在我们的 PaaS 上部署,带你完成将代码部署到 PaaS 的过程。在这里,你将学习如何使用 Dokku 创建你自己的迷你 Heroku。

第八章,接下来是什么?,介绍了几个处于初期阶段且对基于 Docker 的 PaaS 未来有潜力的项目。

本书所需条件

  • 一台运行 OS X、Linux 或 Windows 的 PC/笔记本电脑

  • 互联网连接

本书适合人群

本书面向那些希望学习如何充分利用将服务分离成模块化容器并将其连接成一个完整平台的人。你可能听说过 Docker,但从未安装或使用过它;或者你可能已经安装并运行了一个完整堆栈的容器,而没有将服务分离到连接的模块化容器中。不管是哪种情况,本书都会为你提供运行自己 PaaS 所需的所有见解和知识。

约定

在本书中,你将看到多种文本样式,用于区分不同类型的信息。以下是这些样式的示例及其含义的解释。

文本中的代码词汇、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 账号如下所示:“在下载一些依赖的图像后,当我们执行docker.ps时,应该能够看到正在运行的容器。”

一块代码如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello</title>
</head>
<body>
<h1>First edit!</h1>
</body>
</html>

任何命令行输入或输出如下所示:

curl -sSL https://get.docker.com/ubuntu/ | sudo sh

新术语重要词汇以粗体显示。您在屏幕上看到的词语,例如菜单或对话框中的内容,像这样出现在文本中:“打开Finder窗口并导航到您的Applications文件夹;找到boot2docker并双击它。”

注意

警告或重要提示会以类似这样的框显示。

提示

提示和技巧如下所示。

读者反馈

我们始终欢迎读者的反馈。请告诉我们您对本书的看法——您喜欢或不喜欢的部分。读者反馈对我们非常重要,因为它帮助我们开发出您真正能从中获益的书籍。

要向我们发送一般反馈,只需发送电子邮件至<feedback@packtpub.com>,并在邮件主题中提及书名。

如果您在某个领域具有专业知识,并且有兴趣为书籍撰写或贡献内容,请参见我们的作者指南:www.packtpub.com/authors

客户支持

现在,您已经是 Packt 图书的骄傲拥有者,我们有一些资源帮助您最大限度地发挥您的购买价值。

下载示例代码

您可以从您的账户中下载示例代码文件,网址为www.packtpub.com,适用于您购买的所有 Packt Publishing 书籍。如果您是从其他地方购买的此书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。

勘误

尽管我们已尽最大努力确保内容的准确性,但错误还是可能发生。如果您在我们的书籍中发现错误——可能是文本或代码中的错误——我们将非常感激您能向我们报告。通过这样做,您可以帮助其他读者避免困扰,并帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问www.packtpub.com/submit-errata报告,选择您的书籍,点击勘误提交表单链接,并输入您的勘误详情。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站,或添加到该书籍的勘误部分中的现有勘误列表。

要查看之前提交的勘误,请访问www.packtpub.com/books/content/support,并在搜索框中输入书名。所需的信息将显示在勘误部分。

海盗行为

互联网中的版权材料盗版问题在所有媒体中普遍存在。在 Packt,我们非常重视对版权和许可证的保护。如果你在互联网上遇到我们作品的任何非法复制版本,请立即提供相关网址或网站名称,以便我们采取必要的措施。

如发现涉嫌盗版的材料,请通过 <copyright@packtpub.com> 联系我们,并附上相关链接。

我们感谢你在保护我们的作者和我们提供有价值内容方面的帮助。

问题

如果你在本书的任何部分遇到问题,可以通过 <questions@packtpub.com> 联系我们,我们将尽力解决问题。

第一章:安装 Docker

在本章中,我们将了解如何在各种操作系统上下载和安装 Docker。我们将使用一些基本的 Docker 命令,以便我们可以验证安装是否成功,并第一次与 Docker 交互。

本章涵盖的主题有:

  • 什么是 Docker?

  • Ubuntu Trusty 14.04 LTS 上的 Docker

  • Mac OS X 上的 Docker

  • Windows 上的 Docker

  • 亚马逊 EC2 上的 Docker

本书将带你完成从安装 Docker 到运行自己的平台即服务PaaS)的所有步骤,这样你就可以推送代码而无需考虑基础设施或服务器配置。

本书的主题将是使用隔离的 Web 服务器和数据库创建一个模块化的 Web 应用程序。

什么是 Docker?

在 Docker 的官方网站 www.docker.com 上,提供了以下对 Docker 的定义:

"Docker 是一个开放平台,供开发者和系统管理员构建、运输和运行分布式应用程序。"

更实际的意义是,Docker 是一种将服务封装在隔离环境中(称为容器)的方法,这样它们就可以与所需的所有库和依赖项一起打包,并且开发者可以确保该服务将在 Docker 运行的任何地方运行。

Ubuntu Trusty 14.04 LTS 上的 Docker

最容易安装 Docker 的操作系统、版本和发行版是 Ubuntu Trusty 14.04 LTS。这个任务非常快速,因为我们可以使用内置的包管理器apt-get

注意

请注意,在这里 Docker 被称为docker.io,而在其他平台上仅称为docker,因为 Ubuntu(和 Debian)已经有一个名为docker的包。

首先打开终端并逐一执行这些命令:

sudo apt-get update
sudo apt-get install docker.io
source /etc/bash_completion.d/docker.io

在这里,我们首先更新包管理器apt-get的包列表,以便获取所有可用的包、版本和依赖项的信息。接下来的命令实际上会安装 Docker,之后,我们启用 Ubuntu 来自动完成 Docker 命令的补全。

当你完成这些操作并没有遇到错误时,运行sudo docker.io version,以验证它是否按预期工作。

注意

请注意,这将安装最新发布的 Ubuntu 包版本,但这不一定是最新发布的 Docker 版本。

为了从 Docker 维护的替代仓库获得最新版本,我们可以执行以下命令:

curl -sSL https://get.docker.com/ubuntu/ | sudo sh

这将添加一个由 Docker 团队维护的替代仓库,并为你安装比 Ubuntu 仓库中提供的版本更为更新的 Docker。请注意,当以这种方式安装时,Docker 包名为lxc-docker。运行 Docker 命令使用的命令仍然是docker

在 Ubuntu Trusty 14.04 LTS 上升级 Docker

要检查并下载升级,你只需要在终端中执行以下命令:

sudo apt-get update && sudo apt-get upgrade

用户权限

为了方便起见,建议将我们的用户添加到系统的 Docker 用户组中,这样我们就可以无需使用 sudo 来控制 Docker。这样会给我们的用户执行 Docker 命令的权限。

在运行代码之前,请将 USER 替换为你的用户名:

sudo gpasswd -a USER docker

你可能需要注销并重新登录才能使其生效。重新登录后,运行 docker ps 来验证是否没有权限问题。

注意

更详细的信息可以在官方安装指南中找到,网址为 docs.docker.com/installation/ubuntulinux/

Mac OS X 上的 Docker

为了能够在 Mac OS X 上使用 Docker,我们必须在虚拟机(VM)中运行 Docker 服务,因为 Docker 使用特定于 Linux 的功能来运行。我们不必对此感到害怕,因为安装过程非常简单且直接。

安装

有一个 OS X 安装程序可以安装我们需要的所有工具,即 VirtualBox、boot2docker 和 Docker。

VirtualBox 是一个虚拟化工具,我们可以在其中运行轻量级的 Linux 发行版,而 boot2docker 是一个完全在内存中运行的虚拟机,占用的空间仅约 27 MB。

注意

最新发布的 OS X 安装程序版本可以在 github.com/boot2docker/osx-installer/releases/latest 找到。

现在,让我们看看如何按照以下步骤完成安装:

  1. 点击名为 Boot2Docker-1.3.0.pkg 的按钮下载安装程序,以获取 .pkg 文件,如下图所示:安装

  2. 双击下载的 .pkg 文件并完成安装过程。

  3. 打开 Finder 窗口并导航到 Applications 文件夹;找到 boot2docker 并双击它。会打开一个终端窗口并执行一些命令,如下所示:安装

    这将运行一个名为 boot2docker-vm 的 Linux 虚拟机,该虚拟机在 VirtualBox 中预安装了 Docker。虚拟机中的 Docker 服务以守护进程(后台)模式运行,Docker 客户端安装在 OS X 中,并通过 Docker 远程 API 直接与虚拟机中的 Docker 守护进程通信。

  4. 你将看到一个类似于以下截图的界面,提示你设置一些环境变量:安装

    我们打开 ~/.bash_profile 文件,并将输出的三行代码粘贴到该文件的末尾,如下所示:

    export DOCKER_HOST=tcp://192.168.59.103:2376
    export.DOCKER_CERT_PATH=/Users/xxx/.boot2docker/certs/boot2docker-vm
    export DOCKER_TLS_VERIFY=1
    
    

    我们这样做的原因是让 Docker 客户端知道在哪里找到 Docker 守护进程。如果将来你想找到 IP,可以通过执行 boot2docker ip 命令来实现。添加前面这些行将在每次新的终端会话开始时设置这些变量。完成后,关闭终端窗口。然后,打开一个新窗口并输入 echo $DOCKER_HOST 来验证环境变量是否设置正确。你应该能看到你的 boot2docker 虚拟机的 IP 和端口。

  5. 输入 docker version 来验证你是否可以使用 Docker 命令。如果输出的结果与前面截图的最后几行相似,那就意味着我们成功了。

在 Mac OS X 上升级 Docker

由于 Docker 相对较新,每次更新时可能会有很多变化,因此确保定期检查更新。时不时地,访问 Mac OS X 安装程序下载页面,检查是否有可用的升级。如果有,请执行以下命令来进行更新:

boot2docker stop
boot2docker download
boot2docker start

Windows 上的 Docker

就像在 OS X 上安装 Docker 时,我们需要安装一个 Linux 虚拟机一样,在 Windows 上运行 Docker 也需要这样做,这是因为 Docker 构建依赖于 Linux 内核特性。OS X 拥有一个原生的 Docker 客户端,可以直接与虚拟机中的 Docker 守护进程通信,但 Windows 上目前还没有此类客户端。虽然有一个即将发布的 Windows 版 Docker 客户端,但在本书出版时它仍未发布。

安装

有一个 Windows 安装程序,可以安装我们运行 Docker 所需的一切。为此,请访问 github.com/boot2docker/windows-installer/releases/latest

现在,让我们通过以下步骤来看看如何完成安装:

  1. 点击 docker-install.exe 按钮以下载 .exe 文件,如下图所示:Installation

  2. 下载完成后,运行下载的安装程序。按照安装过程操作,你将安装 VirtualBox、msysGit 和 boot2docker。

  3. 前往你的 Program Files 文件夹,点击新安装的 boot2docker 开始使用 Docker。如果系统提示你输入密码短语,只需按 Enter

  4. 输入 docker version 来验证你是否可以使用 Docker 命令。

在 Windows 上升级 Docker

新软件经常发生变化,为了保持 boot2docker 更新,请执行以下命令:

boot2docker stop
boot2docker download
boot2docker start

在 Amazon EC2 上的 Docker

在本书中,我将使用一个 Amazon EC2 实例,鉴于它是托管 PaaS 的理想场所,我会推荐你也这么做。

EC2 代表 Elastic Compute Cloud,它是一种基础设施类型的服务。Amazon EC2 提供的虚拟服务器可以在订购后的一分钟内创建并可用。

注意

亚马逊提供名为t[x].micro的实例,您可以每月免费使用 750 小时。您可以在aws.amazon.com/free上了解更多信息。

亚马逊有自己命名的 Linux 版本——Amazon Linux AMI,可用于运行 Docker。

安装

让我们看看如何按照以下步骤进行安装:

  1. aws.amazon.com 上创建一个账户,然后访问亚马逊的创建 EC2 实例向导 console.aws.amazon.com/ec2/v2/home?#LaunchInstanceWizard

    接下来的步骤如下所示:

    Installation

  2. 在左侧菜单中点击社区 AMI,选择最新的 amzn-ami-pv。确保选择 pv 版本,而不是 hvm 版本,以便使用更稳定、开销更小的虚拟化,如下所示:Installation

  3. 选择实例类型时,如果有可用的 t1.microt2.micro,可以选择它们。微型实例的性能非常有限,但由于在一些区域它们属于免费使用层,并且目前不用于实际站点,因此我们可以使用它们。点击下一步:配置实例详细信息,然后点击审查并启动按钮,如下所示:Installation

  4. 摘要页面核对所有细节,然后点击启动实例按钮。

  5. 系统会提示你是否要使用现有的密钥对,还是创建一个新的密钥对。如果这是你第一次创建 Amazon EC2 实例,你应该创建一个新的密钥对。这使得连接到实例更加安全。

  6. 下载新的密钥对,将其移动到 ~/.ssh/ 文件夹,并去掉 .txt 扩展名。

  7. 设置文件的正确用户权限也很重要,否则 SSH 将拒绝使用该文件。

    在 Linux 或 Mac 上,执行该操作的终端命令如下所示:

    mv ~/Downloads/amz.pem.txt ~/.ssh/amz.pem
    chmod 600 ~/.ssh/amz.pem
    
    

    在 Windows 上,将密钥保存在任何位置,并使用如 PuTTYgen 这样的工具将其转换为 .ppk 文件,以便在使用 PuTTY 连接时使用。

  8. 系统会提示你选择一个安全组给你的实例。由于这不是生产服务器,选择默认的安全组即可。当需要使用生产服务器时,我们可能需要为实例添加更多的安全措施。

  9. 现在我们已经启动并运行了!让我们连接到它。点击查看实例按钮,并在列表中选择你新创建的实例,如下所示:Installation

  10. 在屏幕底部,你可以看到一些关于实例的信息。你应该查找公有 DNS 信息。它应该是这样子的:

    ec2-54-187-234-27.us-west-2.compute.amazonaws.com
    
    
  11. 在 Linux 或 Mac 上,打开终端并连接到它:

    ssh ec2-user@ec2-54-187-234-27.us-west-2.compute.amazonaws.com -i ~/.ssh/amz.pem
    
    

    截图显示如下:

    Installation

    我们使用ec2-user用户,这是 Amazon Linux 实例的默认用户,amz.pem是我们之前下载的密钥。用你上一步的公有 DNS 信息替换 URL。

    当被问及是否因为未知主机继续时,输入yes

    在 Windows 上,使用 PuTTY,并确保你在 PuTTY 身份验证选项卡中指定了第 4 步中转换的私钥。

  12. 一旦连接到实例,安装 Docker:

    sudo yum update
    sudo yum install -y docker
    sudo service docker start
    
    
  13. 为了测试它是否按预期工作,输入docker version并确保没有错误。你应该看到几行信息,包括客户端版本、API 版本等。

打开端口

Amazon 的默认安全策略是阻止暴露服务的默认端口,因此我们需要修改此设置。

  • 我们回到 EC2 仪表盘,点击菜单中的安全组选项

  • 选择你的 EC2 实例所使用的安全组,并选择入站选项卡

  • Docker 使用的端口范围是49000 - 50000,所以我们需要为此添加一个规则,如下图所示:打开端口

在 Amazon EC2 上升级 Docker

升级 Amazon Linux AMI 实例和升级 Ubuntu 一样简单。输入sudo yum update并确认是否有可用的更新。此命令将列出所有可用的更新,并在你确认后进行安装。

用户权限

Docker 要求命令由docker用户组的用户运行。为了方便,我们将用户添加到 Docker 组中,这样我们就可以在不使用sudo的情况下控制 Docker:

sudo gpasswd -a ec2-user docker

你可能需要注销并重新登录才能使其生效。重新登录后,运行docker ps来验证没有权限问题。你应该看到一行大写字母,类似于CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

显示 Hello World

现在我们已经在选择的机器上运行了 Docker,接下来是让 Docker 为我们服务。以下是一些非常基础的命令,可以帮助我们与 Docker 守护进程进行基本交互。

在下一章中,将解释 Docker 中使用的所有概念和术语:

  • docker ps:此命令列出正在运行的容器

  • docker ps -a:此命令列出所有容器,包括正在运行的和已退出的容器

  • docker images:此命令列出本地(已下载和本地创建的)镜像

  • docker run:此命令从镜像启动一个新的实例容器

  • docker stop:此命令用于停止容器

让我们尝试下图中的第一个命令:

显示 Hello World

正如预期的那样,我们还没有运行任何内容。

启动容器就像docker run [image] [command]一样简单。如果镜像在本地不存在,Docker 会从 Docker Registry Hub 下载它,并在下载完成后启动你的容器。

以下步骤显示如下:

显示 Hello World

在终端中输入以下命令,启动一个容器,该容器打印字符串Hello, let me out of here,然后退出:

docker run oskarhane/hello echo "Hello, let me out of here"

这并不是非常有用,但我们刚刚在容器内的 Ubuntu 中运行了一个命令。

如果我们再次输入docker ps,我们可以看到没有正在运行的容器,因为我们刚刚启动的容器已被立即退出。尝试使用docker ps -a,并尝试docker images

总结

在本章中,我们学习了 Docker 可以在大多数操作系统上使用,并且安装过程根据操作系统的不同差异很大。我们首次与 Docker 守护进程进行了交互,并启动了第一个 Docker 容器。即使容器所做的只是写入一个命令,但这就是在来宾操作系统中启动和运行某些东西是多么简单。

我们还介绍了本书的主题,即运行一个多容器的 Web 应用程序,包括 Web 服务器容器和 MySQL 容器:你自己的 PaaS。

在下一章中,我们将进一步探索 Docker、它的术语以及围绕它的社区。

第二章:探索 Docker

阅读完这一章后,你会发现自己更能舒适地谈论和使用 Docker。本章将涵盖以下话题:

  • Docker 镜像

  • Docker 容器

  • Docker 命令行界面

  • Docker Registry Hub

在构建 PaaS 时,你会发现这些话题非常重要,整个书中你都会使用和与它们交互。

Docker 镜像

一开始,很难理解 Docker 镜像和 Docker(或 Linux)容器之间的区别。

想象一下,我们的 Linux 内核是零层。每当我们运行一个 Docker 镜像时,会在内核层上方添加一层。这层镜像(层一)是只读的,无法更改,也无法持有状态。

一个 Docker 镜像可以建立在另一个 Docker 镜像的基础上,而另一个 Docker 镜像又建立在另一个 Docker 镜像的基础上,依此类推。第一个镜像层称为基础镜像,除了最后一个镜像层之外的所有其他镜像层都称为父镜像。它们继承了父镜像的所有属性和设置,并在 Dockerfile 中添加了自己的配置。

Docker 镜像通过镜像 ID 进行标识,它是一个 64 字符长的十六进制字符串,但在操作镜像时,我们几乎从不使用该 ID 引用镜像,而是使用镜像名称。要列出所有本地可用的 Docker 镜像,我们使用docker images命令。请查看以下图像,看看镜像是如何列出的:

Docker 镜像

镜像可以分发不同的版本供我们选择,这个机制被称为标签。前面的截图通过neo4j镜像展示了latest2.1.5标签。这是拉取特定标签镜像的命令示例:

docker pull ubuntu:14.04
docker pull ubuntu:12.02

Docker 容器

当我们执行docker run imagename时,会创建一个 Docker 容器。在所有镜像层之上,添加了一个可写层。这个层上有在 CPU 上运行的进程,并且可以有两种不同的状态:运行中或已退出。这就是容器。当我们通过 Docker 运行命令启动一个容器时,它进入运行状态,直到因为某种原因停止,或者被我们停止,然后进入退出状态。

当我们有一个容器正在运行时,对其文件系统所做的所有更改都是在启动和停止之间保持永久的。请记住,对容器文件系统所做的更改不会写入底层的 Docker 镜像。

我们可以从同一个镜像启动任意多个容器实例,它们将彼此并排运行,并且完全隔离。我们对容器所做的所有更改仅限于该容器本身。

如果对容器的底层镜像进行了更改,运行中的容器不会受到影响,并且不会自动更新。如果我们想要将容器更新为其镜像的新版本,我们必须小心,确保正确设置了数据结构,否则可能会丢失容器中的所有数据。本书稍后会向你展示如何在没有丢失数据风险的情况下存储重要数据。

对应的截图如下所示:

Docker 容器

一个由 64 个字符组成的十六进制字符串被称为 容器 ID,它用于标识 Docker 容器。这个 ID 在与容器交互时可以使用,并且根据我们运行的容器数量,通常只需要输入容器 ID 的前四个字符。我们也可以使用容器的名称,但通常输入 ID 的前几个字符更方便。

Docker 命令行界面

命令行界面是我们与守护进程使用 Docker 命令进行通信的地方。Docker 守护进程是后台进程,它接收我们输入的命令。

在上一章中,我们运行了一些 Docker 命令来启动和停止容器,并列出容器和镜像。现在,我们将学习一些更多的命令,这些命令将帮助我们在处理 PaaS 容器时,具体如下:

  • docker logs <container-ID|name>:所有写入到 STDOUT 的容器信息都将被记录在文件中,通过此命令可以访问。这是从容器内部输出信息的一个非常方便的方式,如下所示:Docker 命令行界面

  • docker export <container-ID|name>:如果你有一个容器包含你想要导出的数据,可以使用这个命令。它会创建一个 tar 存档并将其发送到 STDOUTDocker 命令行界面

  • docker cp CONTAINER:PATH HOSTPATH:如果你不想导出整个容器文件系统,而只想导出某个目录或文件,你可以使用 docker cp,而不是 export,如下截图所示:Docker 命令行界面

Docker 注册中心 Hub

Docker 流行的一个重要原因是它的社区,以及你可以轻松分享、查找和扩展 Docker 镜像的方式。其核心平台是 Docker 注册中心 Hub,可以在 hub.docker.com/ 找到。

浏览仓库

在这里,我们可以搜索并以多种方式浏览镜像仓库,找到我们需要的内容。如果查看流行的镜像,我们可以看到其他人使用最多的镜像。

如果我们点击 Ubuntu 仓库,我们会看到很多关于该镜像的信息,包括可用的标签、用户评论、它的星标数量以及最后更新时间。

截图显示如下:

浏览仓库

如果我们点击主视图中的标签,我们将看到一个叫做 Dockerfile 的文件。这是镜像描述文件,它在镜像创建时运行。在本书的后面,我们也会自己写一个。

如果你对 Docker Hub 上的某个镜像感兴趣,我建议你阅读它的 Information/README 文件,以及其他用户的评论。通常,你会在其中找到有价值的信息,帮助你选择正确的镜像,并展示如何按照维护开发者的意图运行它。

通常,你会发现一些镜像几乎符合你的需求,因为大多数镜像都非常通用,但作为开发者,你可能需要安装特定的设置或服务。

探索发布的镜像

以官方的 WordPress Docker 镜像为例(registry.hub.docker.com/_/wordpress/)。你可以在 Docker Hub 的浏览页面上找到它,或者直接搜索。

现在先不考虑这些缺点,看看信息页面上说了些什么:

探索发布的镜像

这个镜像会读取 Docker 容器的环境变量。这意味着镜像必须通过使用 docker run –e 命令注入环境变量,或者你可以 --link 另一个容器到它,并注入这些变量。我们将在本书后面讨论容器链接。

让我们看看如果拉取这个镜像会得到什么。点击 Apache 目录中的 Dockerfile 链接:

FROM php:5.6-apache

RUN a2enmod rewrite

# install the PHP extensions we need
RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev && rm -rf /var/lib/apt/lists/* \
 && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \
 && docker-php-ext-install gd
RUN docker-php-ext-install mysqli

VOLUME /var/www/html

ENV WORDPRESS_VERSION 4.1.1
ENV WORDPRESS_UPSTREAM_VERSION 4.1.1
ENV WORDPRESS_SHA1 15d38fe6c73121a20e63ccd8070153b89b2de6a9

# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress
RUN curl -o wordpress.tar.gz -SL https://wordpress.org/wordpress-${WORDPRESS_UPSTREAM_VERSION}.tar.gz \
 && echo "$WORDPRESS_SHA1 *wordpress.tar.gz" | sha1sum -c - \
 && tar -xzf wordpress.tar.gz -C /usr/src/ \
 && rm wordpress.tar.gz

COPY docker-entrypoint.sh /entrypoint.sh

# grr, ENTRYPOINT resets CMD now
ENTRYPOINT ["/entrypoint.sh"]
CMD ["apache2-foreground"]

好的,我们看到它基于 Debian Wheezy,并安装了 Apache2、PHP5 和一些其他的东西。之后,它设置了一些环境变量,然后下载了 WordPress。

我们看到几行以 COPY 命令开头。这意味着文件随 Docker 镜像一起传送,并在容器启动时复制到容器内部。这就是 WordPress 镜像随附的 docker-apache.conf 文件的样子:

<VirtualHost *:80>
 DocumentRoot /var/www/html
 <Directory /var/www/html>
 AllowOverride all
 </Directory>
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

上面的代码行告诉 Apache 在哪里查找文件。

那么 docker-entrypoint.sh 文件呢?

ENTRYPOINT 关键字告诉 Docker 守护进程,如果没有指定其他内容,那么每次运行容器时都应该执行这个文件。就好像整个容器是一个可执行文件一样。

如果我们看看这个文件的内容,我们会发现它基本上是设置了与 MySQL 数据库的连接,并配置了 .htaccess 和 WordPress:

#!/bin/bash
set -e
if [ -z "$MYSQL_PORT_3306_TCP" ]; then
 echo >&2 'error: missing MYSQL_PORT_3306_TCP environment variable'
 echo >&2 '  Did you forget to --link some_mysql_container:mysql ?'
 exit 1
fi

首先检查的是用户是否为 MySQL 连接设置了环境变量。如果没有,它将退出并将一些信息写入 STDERR

你不妨尝试一下,看看你是否能触发写入 STDERR 的 MySQL 错误,错误信息是 error: missing MYSQL_PORT_3306_TCP environment variable,如以下所示:

docker run –-name some-wordpress –d wordpress

探索发布的镜像

--name some-wordpress 命令为容器命名,这样我们以后可以通过这个名字引用它。此外,–d 参数告诉容器在分离模式下运行,这意味着它不再接受我们启动它时发出的命令。最后的 wordpress 参数是我们要运行的 Docker 镜像名称。

如果我们查看新容器的日志,就会看到截图所示的错误信息。

让我们运行一个 MySQL 容器,看看是否能让它正常工作。访问 registry.hub.docker.com/_/mysql/ 来访问 Docker 注册表中心的官方 MySQL 镜像库。在这里,它指出为了启动 MySQL 实例,我们需要在 shell 中执行 docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql。由于我们目前只是为了教育目的,因此不需要选择一个强密码作为 root 用户密码。在下载一些依赖镜像后,我们应该能够通过执行 docker ps 来看到我们正在运行的容器。如果看到,使用 docker logs some-mysql 查看安装日志,如下所示:

探索发布的镜像

很棒,现在我们有了一个运行中的 MySQL 容器,它是启动 WordPress 实例所需要的。让我们在 MySQL 链接就位的情况下启动一个新的 WordPress 实例:

docker run --name some-wordpress --link some-mysql:mysql –p 80 -d wordpress

--link 参数通过将环境变量注入到 some-wordpress 容器中,暴露 some-mysql 容器的环境变量、接口和暴露端口。

要打开一个可以从外部访问的端口,通过 –p 80 参数暴露端口 80。

如果你收到如下错误信息 Error response from daemon: Conflict, The name some-wordpress is already assigned to a11c101cacaf.,你需要删除(或重命名)该容器,才能再次将 some-wordpress 分配给容器。你需要给新容器起一个新名字,或删除旧的(失败的)WordPress 容器。调用 docker rm some-wordpress 删除旧容器并使用所需的名称。

当容器正在运行时,调用 docker ps 命令以找出我们的端口是如何分配给容器的私有端口 80。

我们可以查看容器列表中的端口栏,或者可以通过调用 docker port some-wordpress 80 明确查找,如下所示:

探索发布的镜像

在我的案例中,它是端口49155

在你的网页浏览器中输入 Docker 主机的ip:port,查看是否能访问它。如果你使用的是 Windows 或 OS X 本地计算机,可以通过调用 boot2docker ip 查找 Docker IP。如果你使用的是本地 Linux,127.0.0.1 应该可以。

我是在 Amazon EC2 上进行的操作,所以我需要进入 EC2 管理控制台获取我的公共 IP 或公共 DNS。

在浏览器中输入 http://yourip:yourport(在我的案例中是 http://myamazon-dns.com:49155),你应该会看到这个界面:

探索发布的镜像

注意

默认的 Amazon AWS 安全策略会阻止默认的 Docker 公共端口,因此我们需要在 EC2 控制台的安全组部分进行更改。有关如何操作,请参见 第一章中的 Docker 在 Amazon EC2 上 部分,安装 Docker

太棒了,成功了!

概要

Docker 镜像可以视为容器的只读模板,指定了在容器启动时需要安装、复制、配置和暴露的内容。

我们进一步了解了如何与 Docker 守护进程及单独的 Docker 容器进行交互,以读取日志、复制文件并导出完整的文件系统。

介绍了 Docker Hub,我们查看了官方 WordPress Docker 镜像的构成,以及它们如何在 Dockerfile 和一定程度上在 ENTRYPOINT 文件中配置操作系统。

我们下载并运行了预期失败的 WordPress 镜像,之后通过将所需的 MySQL 容器与之链接修复了它。

在下一章,我们将创建一个 Dockerfile,并将 Docker 镜像发布到 Docker 注册中心,以便我们能够将定制的 Docker 镜像传输到我们决定放置 PaaS 的任何地方。

第三章:创建我们的第一个 PaaS 镜像

现在你已经准备好编写自己的 Dockerfile,将它们发布到 Docker Registry Hub,并为它们创建容器。在本章中,你将:

  • 在另一个镜像基础上构建自己的镜像

  • 将你的 Dockerfile 存放在 GitHub 账户中

  • 在 Docker Registry Hub 上发布镜像

WordPress 镜像

对于这个项目,我们将使用官方的 WordPress Docker 镜像作为基础,它的 web 服务器是 Apache2。

注意

如果你计划托管流量较大的站点,我建议使用基于 Nginx 的镜像,而不是 Apache2 作为 web 服务器。我曾成功地在 Nginx 上运行 WordPress 站点,并使用 memcached 插件 WP-FFPC。设置可能会有些棘手,因此这不在本书的范围内。

首先,让我们运行一个 MySQL 容器和一个 WordPress 容器,并将它们链接在一起,看看会发生什么:

docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql
docker run --name some-wordpress --link some-mysql:mysql -d -p 80 wordpress

–p 80 选项告诉 Docker 将私有端口 80 映射到外部世界。要找出哪个公共端口与私有端口 80 绑定,可以运行 docker ps 命令,并查看端口列,或者执行 docker port <container-ID|name> 80 命令。

截图如下所示:

WordPress 镜像

在我的例子中,公共端口是 49154。在你的 web 浏览器中输入完整的 URL,格式为 http://public_ip:public_port。我正在 Amazon EC2 实例上操作,得到的公共域名是 http://ec2-54-187-234-27.us-west-2.compute.amazonaws.com:49154

截图如下所示:

WordPress 镜像

WordPress 安装页面欢迎我们,这意味着 WordPress 和 MySQL 容器都在正常工作。

移动默认设置

现在,我们有一个基于 Apache2 运行的 WordPress 默认安装。某些 WordPress 插件需要你修改 web 服务器的配置。我们该怎么做呢?如果我们想编辑 WordPress 目录中的一些文件呢?

我们首先需要做的是获取官方 WordPress 仓库的副本,这样我们就可以探索 Dockerfile。获取仓库的当前 URL 是 github.com/docker-library/wordpress。从 Docker Registry Hub 上的 WordPress 仓库页面点击这个链接。

你可以克隆、分叉或仅下载此 Docker 镜像的源代码。无论你如何获取它都没关系,因为我们后续不会再使用它。这个镜像用于测试和探索。我是通过我的 EC2 实例完成这个的。

移出默认设置

用任何文本编辑器打开文件查看其内容。如果你像我一样使用终端,可以用 vi apache/Dockerfilevi 编辑器中打开它。当前官方 WordPress 镜像的 Dockerfile 如下所示:

FROM php:5.6-apache

RUN a2enmod rewrite

# install the PHP extensions we need
RUN apt-get update && apt-get install -y libpng12-dev libjpeg-dev && rm -rf /var/lib/apt/lists/* \
 && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \
 && docker-php-ext-install gd
RUN docker-php-ext-install mysqli

VOLUME /var/www/html

ENV WORDPRESS_VERSION 4.1.1
ENV WORDPRESS_UPSTREAM_VERSION 4.1.1
ENV WORDPRESS_SHA1 15d38fe6c73121a20e63ccd8070153b89b2de6a9

# upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress
RUN curl -o wordpress.tar.gz -SL https://wordpress.org/wordpress-${WORDPRESS_UPSTREAM_VERSION}.tar.gz \
 && echo "$WORDPRESS_SHA1 *wordpress.tar.gz" | sha1sum -c - \
 && tar -xzf wordpress.tar.gz -C /usr/src/ \
 && rm wordpress.tar.gz

COPY docker-entrypoint.sh /entrypoint.sh

# grr, ENTRYPOINT resets CMD now
ENTRYPOINT ["/entrypoint.sh"]
CMD ["apache2-foreground"]

该镜像使用 php:5.6-apache 镜像作为基础,并将 WordPress 4.1 下载并解压到 /usr/src/wordpress。然后,它添加了 ENTRYPOINT 并在前台启动 Apache2。

我们的目标

为了使这个 WordPress 镜像除了演示用途外可用,我们需要通过三种方式修改 Dockerfile。我们的目标如下:

  • 为缓存准备 Apache(通过 WP Super Cache 插件)

  • 在 PHP 和 Apache2 中提高上传限制

  • 安装两个插件:WP Super Cache 和 WP Mail SMTP

为缓存准备

为了通过 WP Super Cache 获取网站缓存,有两个小步骤需要执行——我们需要在 Apache2 中启用 mod_headersmod_expires 模块。

在 Dockerfile 的第 5 行,你可以看到 RUN a2enmod rewritea2enmod 命令在 Apache2 中启用模块,a2dismod 命令则禁用模块。启用我们想要的模块就像把它们附加到那行一样简单:

RUN a2enmod rewrite expires headers

我们进行这些修改,构建一个新镜像,然后看看会发生什么。构建这些镜像需要很长时间,因为 PHP 是从源代码构建的。我们关注的是显示我们所需模块已启用的行。它们会在构建过程中显示几秒钟。

你通过执行以下命令来启动 Dockerfile 的构建:

docker build –t mod-wp .

–t mod-wp 命令将我们新图像的名称设置为 mod-wp

截图如下所示:

为缓存准备

构建应该顺利完成整个过程而没有任何错误,然后缓存插件的准备工作就完成了。

提高上传限制

默认上传大小被 PHP 限制为 2 MB。这个限制太低,尤其是现在手机博客非常流行,而手机拍摄的照片或视频的大小通常比这个还要大。我希望能够直接在我的博客中上传视频,视频大小可以达到 32 MB。

为了提高这个限制,我们需要在 PHP 配置文件中更改两个参数的限制:upload_max_filesizepost_max_size

查看 php:5.6-Apache 镜像,它是 WordPress 镜像的基础镜像,在 Dockerfile 中我们看到它运行的是 Debian,并且 PHP 配置文件应该位于 /usr/local/etc/php/conf.d/ 目录中。这意味着,如果我们将一个文件添加到该目录,它应该会被读取和解析。

注意

PHP 5.6 的 Dockerfile 可以在 github.com/docker-library/php/blob/master/5.6/Dockerfile 找到。

为了验证上传限制是否如前所述的那样低,我启动并安装了一个未经修改的 WordPress 容器。然后我点击了 添加新媒体 按钮。

提高上传限制

它显示上传限制为 2 MB。

我们来为配置目录添加一个名为 upload-limit.ini 的配置文件,并在文件中添加这两个参数。

这些命令应该放在一行内,添加到我们的 Dockerfile 中,位于我们在准备 Apache 缓存时修改的那一行之上:

RUN touch /usr/local/etc/php/conf.d/upload-limit.ini \
 && echo "upload_max_filesize = 32M" >> /usr/local/etc/php/conf.d/upload-limit.ini \
 && echo "post_max_size = 32M" >> /usr/local/etc/php/conf.d/upload-limit.ini
#Paste above this line.
RUN a2enmod rewrite expires headers

再次构建镜像,以确保不会产生错误。如果出现错误提示显示镜像名称已存在,你可以使用docker rmi mod-wp命令删除旧镜像,或者将名称改为mod-wp:latest,这将更新镜像的标签为latest

构建完成后,我们从新镜像运行一个新容器,查看 WordPress 管理界面显示的内容。我们可以像这样运行一个新镜像的容器:

docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql
docker run --name some-wordpress --link some-mysql:mysql -d -p 80 mod-wp:latest

提高上传限制

现在我们可以看到可以上传更大的文件。为了验证,如果你上传一个大于 2MB 的文件,这将证明上传限制已被提高。

插件安装

在这里,我们将下载并安装两个插件,这些插件是我们未来所有 WordPress 网站都需要的。所有与这些插件相关的任务都将在入口文件中完成,因为我们需要编辑 WordPress 安装中的一些文件。

第一个插件是 WP Super Cache。我们之前为此配置了 Apache2,现在可以使用它了。通过这个插件,我们的网站运行会更快,并且对主机的资源要求较少。

第二个插件是 WP Mail SMTP,它帮助 WordPress 发送外发邮件。这个容器不包含(也不应该包含)邮件服务器。通过这个插件,我们可以让 WordPress 通过外部 SMTP(如 Gmail、ISP 或其他任何服务)发送邮件。

注意

尽管我已经托管并管理了几年的邮件服务器,但它需要不断更新,还需要管理垃圾邮件过滤器和冗余。我们还是把这项工作交给专业人士更好。

所有插件将通过 CURL 下载,并用 unzip 解压。CURL 已经安装,但 unzip 尚未安装,因此我们需要将它添加到 Dockerfile 中,放在apt-get install命令运行的地方。

RUN apt-get update && apt-get install -y unzip rsync && rm -r /var/lib/apt/lists/*

如果我们不这么做,构建过程中将会出现错误信息。

由于有两个插件需要下载、解压和激活,我们将在docker-entrypoint.sh文件中创建一个函数。

这个函数将去 WordPress 的插件站点查找最新版本插件的下载 URL。它会下载并解压到我们 WordPress 安装的插件文件夹中:

dl_and_move_plugin() {
  name="$1"
  curl -O $(curl -i -s "https://wordpress.org/plugins/$name/" | egrep -o "https://downloads.wordpress.org/plugin/[^']+")
  unzip -o "$name".*.zip -d $(pwd)/wp-content/plugins
}

现在我们已经把函数放在了那里,可以在文件的末尾附近,chown –R www-data:www-data ..那行上方添加以下几行:

dl_and_move_plugin "wp-super-cache"
dl_and_move_plugin "wp-mail-smtp"

将函数和函数调用放置在接近文件底部的位置——在docker-entrypoint.sh文件中,紧接着exec命令上方。

我们将再次构建镜像并启动容器,以验证一切是否如我们所愿:

docker build –t mod-wp:latest

这需要一些时间,准备好后,你可以启动一个 MySQL 容器和一个mod-wp容器:

docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql
docker run --name some-wordpress --link some-mysql:mysql -d -p 80 mod-wp:latest

如果你收到一个错误提示,说你已经有一个同名的容器,可以通过docker rm some-wordpress删除旧的容器,或者为新容器使用另一个名称。

通过运行docker ps获取端口,并查找绑定到端口80的 WordPress 容器端口。然后将 URL 加载到浏览器中。这时,安装 WordPress,登录并进入插件页面,如下截图所示:

插件安装

这看起来正是我们想要的!太棒了!

让我们继续激活并设置这些插件,以验证它们是否正常工作。首先从 WP Mail SMTP 插件开始。我将使用我的 Gmail 帐户作为发件人,但你可以选择任何你想用的 SMTP。以下是 Gmail 设置的截图:

插件安装

在此页面的底部,你可以发送测试电子邮件。我强烈推荐这样做,因为 Gmail 有时会阻止新的 SMTP 客户端。如果你收到一条错误信息,说请通过你的网页浏览器登录,然后再试一次,说明你触发了该问题。在这种情况下,你很快会收到来自 Google 的电子邮件,解释可疑活动并要求你执行一些步骤以使其正常工作。这很烦人,但从长远来看这是好事。

接下来,让我们继续设置 WP Super Cache 插件。请从插件页面激活该插件。在启用它之前,我们需要先进入设置 | 固定链接,勾选文章名按钮,然后保存。

然后进入设置 | WP Super Cache

点击启用缓存,然后点击更新状态。接下来,点击高级标签,启用mod_rewrite 缓存,如图所示:

插件安装

向下滚动到其他设置部分,勾选以下截图中显示的框。如果你想确切了解这些复选框的作用,可以参考插件文档。

插件安装

保存后,你会看到顶部出现一条通知,提醒你需要更新重写规则,如下所示:

插件安装

向下滚动页面,点击更新 Mod_Rewrite 规则按钮以更新重写规则,如下所示:

插件安装

现在,缓存插件的状态应该显示为绿色,所有设置也应该完成。由于我们已登录到这个浏览器,我们不会看到缓存的页面。了解这一点很重要,优势是你无需禁用整个缓存插件就能看到未缓存版本的站点。请打开另一个浏览器(不要仅仅在当前浏览器中打开另一个窗口或标签页,除非你使用的是隐身模式或私密模式),然后访问你的 WordPress 实例。点击文章中的Hello World标题,然后返回到首页,再次点击标题。感觉很快,对吧?

为了验证它是否有效,您可以打开浏览器中的开发者工具。确保在开发工具打开时,您的浏览器没有禁用缓存。点击 Network 标签,然后再次点击帖子标题,接着检查该调用,如下图所示:

插件安装

这正是我们想要看到的。太好了!

使我们的更改持久化

现在我们已经做了更改,我们想要创建我们自己的 Dockerfile,以便在官方 WordPress 镜像基础上构建。

这就是 Dockerfile 应该看起来的样子:

FROM wordpress:latest
RUN apt-get update && apt-get install -y unzip && rm -r /var/lib/apt/lists/*
RUN touch /usr/local/etc/php/conf.d/upload-limit.ini \
 && echo "upload_max_filesize = 32M" >> /usr/local/etc/php/conf.d/upload-limit.ini \
 && echo "post_max_size = 32M" >> /usr/local/etc/php/conf.d/upload-limit.ini
RUN a2enmod expires headers
VOLUME /var/www/html
COPY docker-entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["apache2", "-DFOREGROUND"]

在 GitHub 上托管镜像源

Docker Registry Hub 对从 Bitbucket 和 GitHub 自动获取镜像更新提供了非常好的支持。您可以选择任何您喜欢的服务,但对于本书来说,我将使用 GitHub。我在这两个服务上都有账户,它们都非常优秀。

在 GitHub 上托管镜像源

在 GitHub 上创建一个新的空仓库,命名为 my-docker-images,如果愿意,可以添加适当的许可证。

注意

本书不会介绍如何将您的 SSH 密钥添加到 GitHub 等内容。网上有很好的教程可以参考。GitHub 有一篇很棒的指南,地址是 help.github.com/articles/generating-ssh-keys/

让我们创建一个分支,并将修改后的 Docker 镜像文件复制到该分支中。

将仓库克隆到本地,这样您就可以向其中添加文件。确保您不在 wordpress-master 目录内,而是在与其同一层级的目录下:

git clone git@github.com:yourusername/my-docker-images.git

该命令的输出如下:

在 GitHub 上托管镜像源

我们将一一执行这些命令:

cd my-docker-images
git checkout -b wordpress
git add .
git commit –m "Adding new files."
git push origin wordpress

访问您的 GitHub 页面,尝试找到 WordPress 分支。

对于我们想要创建并发布到 Docker Registry Hub 的每一个新的 Docker 镜像,我们都需要在这个 GitHub 仓库中创建一个新的分支。如果您有很多 Docker 镜像,并且这些镜像有很多版本,您可能需要考虑采用不同的结构,但对于本书来说,这种方法非常合适!

在 GitHub 上托管镜像源

所有文件都已到位,您可以点击它们,验证内容是否符合预期。

在 Docker Registry Hub 上发布镜像

如果您还不是 Docker Registry Hub 的成员(hub.docker.com),现在是时候注册了,以便您可以将镜像发布到公共 Docker 仓库,并且可以从任何地方访问。

自动构建

当您添加一个仓库时,应该选择 Automated Build 选项,这样您就可以从 GitHub(或 Bitbucket)中获取代码,如下图所示:

自动构建

我们将连接到我们的 GitHub 账户,并选择我们刚刚创建并推送到 my-docker-images 的仓库。

我们将开始添加我们的 WordPress 镜像,因此在下一个屏幕上将仓库名称设置为 wordpress。确保正确输入这个名称,因为之后无法更改。

此时,我们将只使用一个标签——latest 标签。确保源:类型 设置为 分支,并且你已将其名称输入为 wordpress

选择将其作为公共仓库添加,并勾选 激活 复选框。这意味着如果你在 GitHub 上推送任何更新,Registry Hub 将自动拉取并发布其更改,如下图所示:

自动构建

Registry Hub 现在将拉取你的分支并尝试构建 Docker 镜像,以验证它是否正常工作。你可以前往 构建详情 标签查看进度。由于它是官方的 WordPress 镜像基础,如果他们在构建服务器上缓存了镜像,构建速度应该会很快。如果没有,可能需要几分钟时间,因为 PHP 是从源代码编译的。

以下是显示的截图:

自动构建

哇!我们刚刚在 Docker Registry Hub 上发布了一个镜像,这意味着任何人都可以拉取并运行基于它的容器。状态会从 构建中 变为 已完成,当镜像发布时。

下一步就是我们自己拉取这个镜像,验证它是否按预期工作:

docker pull oskarhane/wordpress
docker images
docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql
docker run --name my-wordpress --link mysql:mysql -d -p 80 oskarhane/wordpress
docker ps

打开你的网页浏览器,访问你的新容器。你应该会看到 WordPress 设置页面。

总结

在这一章中,你学到了很多内容。大部分内容都是关于修改 Dockerfile 和 ENTRYPOINT 文件,以便得到我们想要的 Docker 镜像。Bash 知识和编程技能非常便利,但由于所有这些操作主要是安装、移动文件和编辑设置文件,基本的知识已经足够。

GitHub 是一个托管 Docker 仓库的绝佳平台,而且设置一个新的仓库非常简单,便于开始使用。Docker Registry Hub 会将你的 GitHub 仓库与之连接,并让你选择一个分支。这个分支将作为公共 Docker 镜像的源,任何人都可以拉取并使用。

然而有一个问题出现了;我们的数据怎么办?它被困在这些 MySQL 和 WordPress 容器里。下一章将向你展示如何处理这些数据。

第四章:为容器提供数据和参数

WordPress 容器中的 WordPress 数据以及 MySQL 容器中的数据库数据可能并不是我们所希望的。最佳实践是将数据保存在服务容器之外,因为你可能希望将数据与服务容器分开。在本章中,我们将讨论以下主题:

  • 数据卷

  • 创建数据卷镜像

  • GitHub 上的主机

  • 在 Docker Registry Hub 上发布

  • 在 Docker Registry Hub 上运行

  • 向容器传递参数

  • 创建一个带参数的镜像

数据卷

我们可以通过两种方式将外部卷挂载到容器中。数据卷允许你在容器之间共享数据,并且如果你更新、停止甚至删除服务容器,数据卷中的数据不会受到影响。

数据卷通过docker run语句中的–v选项挂载:

docker run –v /host/dir:container/dir

你可以通过添加多个–v指令,将任意数量的数据卷添加到容器中。

数据卷的一个非常好的地方是,传递给容器的数据卷容器本身并不需要了解它的存在,也不需要做任何处理。容器不需要做任何更改,它就像写入本地文件系统一样工作。你可以覆盖容器内的现有目录,这也是常见的做法。其一用途就是将 Web 根目录(通常位于容器内的/var/www)放在 Docker 主机上的某个目录下。

将主机目录挂载为数据卷

你可以将主机上的目录(或文件)挂载到容器中:

docker run –d --name some-wordpress –v /home/web/wp-one:/var/www wordpress

这将把主机上的本地目录/home/web/wp-one挂载到容器的/var/www。如果你只想赋予容器读取权限,可以将指令改为–v /home/web/wp-one:/var/www:ro,其中:ro表示只读权限。

在生产环境中使用host目录作为数据卷并不常见,因为目录中的数据并不具有很好的可移植性。但在测试服务容器如何响应源代码更改时,这种做法非常方便。

你在主机目录中做的任何更改都会直接影响到容器中挂载的数据卷。

挂载数据卷容器

处理数据的一个更常见的方法是使用一个专门用于存储数据的容器。该容器中运行的服务应尽量少,以保持其稳定性。

数据卷容器可以通过 Dockerfile 中的VOLUME关键字暴露卷,这些卷将在使用数据卷容器时通过--volumes-from指令挂载到服务容器上。

一个非常简单的 Dockerfile,带有VOLUME指令,可能如下所示:

FROM ubuntu:latest
VOLUME ["/var/www"]

使用前面提到的 Dockerfile 的容器会挂载/var/www。为了将数据容器的卷挂载到服务容器上,我们首先创建数据容器,然后按如下方式挂载:

docker run –d --name data-container our-data-container
docker run –d --name some-wordpress --volumes-from data-container wordpress

数据卷的备份与恢复

由于数据卷中的数据是多个容器共享的,所以通过将数据挂载到临时容器上来访问数据非常简单。以下是如何从一个包含 VOLUME ["/var/www"] 的数据卷容器中的数据创建一个 .zip 文件(从你的主机中):

docker run --volumes-from data-container -v $(pwd):/host ubuntu zip -r /host/data-containers-www /var/www

这将创建一个名为 data-containers-www.zip.zip 文件,里面包含了来自 var 目录中 www 数据容器的内容。这个 .zip 文件会将内容放入你当前的主机目录中。

创建数据卷镜像

由于我们的数据卷容器仅用于存储数据,因此我们应尽量将其保持尽可能小,以免占用服务器上过多不必要的空间。容器中的数据当然可以增长到与服务器磁盘空间相等的大小。我们不需要任何复杂的东西;只需要一个正常工作的文件存储系统。

对于本书,我们将所有数据(MySQL 数据库文件和 WordPress 文件)保存在同一个容器中。当然,你也可以将它们分开存储到两个数据卷容器中,命名为 dbdatawebdata 之类的名称。

数据卷镜像

我们的数据卷镜像除了一个可以读写的工作文件系统外,不需要其他任何东西。这就是为什么我们选择的基础镜像是 BusyBox。以下是 BusyBox 对自己的描述:

“BusyBox 将许多常见的 UNIX 工具的精简版本组合成一个小型可执行文件。它提供了 GNU 文件工具、shell 工具等大多数工具的替代品。BusyBox 中的工具通常比它们功能齐全的 GNU 版本选项更少;然而,包含的选项提供了预期的功能,且行为非常像它们的 GNU 版本。BusyBox 为任何小型或嵌入式系统提供了一个相当完整的环境。”

听起来不错!我们将继续将其添加到 Dockerfile 中:

FROM busybox:latest

显示挂载点

Dockerfile 中有一个 VOLUME 指令,你可以定义在使用 --volumes-from 属性添加此数据卷容器时,哪些目录将暴露给其他容器。在我们的数据卷容器中,我们首先需要为 MySQL 数据添加一个目录。让我们查看将要使用的 MySQL 镜像,看看哪个目录用于数据存储,并将该目录暴露给我们的数据卷容器,以便我们能够使用它:

RUN mkdir –p /var/lib/mysql
VOLUME ["/var/lib/mysql"]

我们还希望在此容器中安装 WordPress,包括所有的 .php 文件和图像文件。再次,我们查看将要使用的镜像,找出哪个目录将被使用。在这种情况下,是 /var/www/html。将此添加到 Dockerfile 时,不要添加新行;只需在现有行后追加 MySQL 数据目录:

RUN mkdir -p /var/lib/mysql && mkdir -p /var/www/html
VOLUME ["/var/lib/mysql", "/var/www/html"]

Dockerfile

以下是用于数据镜像的简单 Dockerfile:

FROM busybox:latest
MAINTAINER Oskar Hane <oh@oskarhane.com>
RUN mkdir -p /var/lib/mysql && mkdir -p /var/www/html
VOLUME ["/var/lib/mysql", "/var/www/html"]

就是这样!在将镜像发布到 Docker Registry Hub 时,最好在 Dockerfile 中包含 MAINTAINER 指令,这样如果有人有任何原因需要联系你,就能找到你。

托管在 GitHub 上

当我们利用如何在 GitHub 上托管 Docker 镜像源和如何在 Docker 注册中心发布镜像的知识时,创建数据卷镜像就不成问题了。

创建一个分支和一个 Dockerfile,并为我们的数据卷镜像添加内容:

git checkout -b data
vi Dockerfile
git add Dockerfile

在前面代码的第 2 行,你可以使用你喜欢的文本编辑器。我个人觉得 vi 很适合我。你应该添加到 Dockerfile 的内容是这样的:

FROM busybox:latest
MAINTAINER Oskar Hane <oh@oskarhane.com>
RUN mkdir /var/lib/mysql && mkdir /var/www/html
VOLUME ["/var/lib/mysql", "/var/www/html"]

用你的姓名和电子邮件替换维护者信息。

你可以——而且应该——始终确保它在提交和推送到 GitHub 之前正常工作。为此,你需要从你的 Dockerfile 构建一个 Docker 镜像:

docker build –t data-test .

确保你注意到行末的点,这意味着 Docker 应该在当前目录中查找 Dockerfile。Docker 将尝试根据我们 Dockerfile 中的指令构建镜像。由于这是一个小的基础镜像,上面只有几个 VOLUME 指令,它的构建应该相当快。

截图如下:

托管在 GitHub 上

当一切按我们预期工作时,是时候提交更改并将其推送到我们的 GitHub 仓库了:

git commit –m "Dockerfile for data volume added."
git push origin data

当你将其推送到仓库后,前往 GitHub 验证你的新分支是否已经出现。

以下截图显示了 GitHub 仓库:

托管在 GitHub 上

在 Docker 注册中心发布

现在我们在 GitHub 上有了新的分支,我们可以去 Docker Hub 注册中心创建一个新的自动构建,命名为 data。它将以我们的 GitHub 数据分支为源。

在 Docker 注册中心发布

等待构建完成,然后尝试通过你的 Docker 守护进程拉取镜像,验证它是否存在并且可以正常工作。

截图如下:

在 Docker 注册中心发布

太棒了!看看这个镜像的大小;它不到 2.5 MB。非常完美,因为我们只是想在其中存储数据。基于这个镜像的容器当然可以大到你的硬盘允许的大小。这只是为了展示镜像的大小。镜像是只读的,记住了吗?

运行数据卷容器

数据卷容器是特殊的;它们可以被停止,仍然能够完成它们的任务。就我个人而言,我喜欢在执行 docker ps 命令时看到所有容器的使用情况,因为我喜欢定期删除停止的容器。

这个完全取决于你。如果你可以接受保持容器停止,你可以使用这个命令来启动它:

docker run –d oskarhane/data true

true 参数仅用于输入有效命令,–d 参数使容器处于分离模式,在后台运行。

如果你希望容器保持运行,你需要将一个服务放在前台,像这样:

docker run –d oskarhane/data tail –f /dev/null

前面命令的输出如下:

运行数据卷容器

tail –f /dev/null 命令是一个永不结束的命令,因此容器会一直运行,直到我们停止它。从资源角度来看,tail 命令是相当无害的。

向容器传递参数

我们已经看到,如何在启动官方 MySQL 容器时给容器传递参数或环境变量:

docker run --name mysql-one -e MYSQL_ROOT_PASSWORD=pw -d mysql

–e MYSQL_ROOT_PASSWORD=pw 命令是一个示例,展示了如何进行操作。它表示容器内的 MYSQL_ROOT_PASSWORD 环境变量的值为 pw

这是一个非常方便的方式,可以创建可配置的容器,在这些容器中,你可以将设置脚本作为 ENTRYPOINT,或者配置前台脚本来设置密码;主机;测试、预发布或生产环境;以及容器所需的其他设置。

创建一个参数化镜像

为了熟悉这个非常好的功能,我们来创建一个小的 Docker 镜像,它根据环境变量的状态将字符串转换为大写或小写。

这个 Docker 镜像将基于最新的 Debian 发行版,并且只会有一个 ENTRYPOINT 命令。以下是 Dockerfile

FROM debian:latest
ADD ./case.sh /root/case.sh
RUN chmod +x /root/case.sh
ENTRYPOINT /root/case.sh

这条命令将从当前目录中取出 case.sh 文件,添加到容器中,使其可执行,并将其指定为 ENTRYPOINT

case.sh 文件可能是这样的:

#!/bin/bash

if [ -z "$STR" ]; then
 echo "No STR string specified."
 exit 0
fi

if [ -z "$TO_CASE" ]; then
 echo "No TO_CASE specified."
 exit 0
fi

if [ "$TO_CASE" = "upper" ]; then
 echo "${STR^^*}"
 exit 0
fi
if [ "$TO_CASE" = "lower" ]; then
 echo "${STR,,*}"
 exit 0
fi
echo "TO_CASE was not upper or lower"

该文件检查 $STR$TO_CASE 环境变量是否已设置。如果检查 $TO_CASE 是否为 upperlower 时失败,系统会显示一个错误信息,提示我们只处理 upperlower

如果 $TO_STR 被设置为 upperlower,环境变量 $STR 的内容会分别转换为大写或小写,并打印到 stdout

让我们来试试这个!

创建参数化镜像

这里有一些我们可以尝试的命令:

docker run –i case
docker run –i -e STR="My String" case
docker run –i -e STR="My String" –e TO_CASE=camel case
docker run –i -e STR="My String" –e TO_CASE=upper case
docker run –i -e STR="My String" –e TO_CASE=lower case

这看起来按预期工作,至少在这个目的下是如此。现在我们已经创建了一个容器,可以接受参数并对其进行处理。

总结

在这一章中,你学到可以通过数据卷将数据与服务容器分离。数据卷可以是主机文件系统中的目录、文件或数据卷容器。

我们探讨了如何将参数传递给容器,并如何从 ENTRYPOINT 内部读取它们。参数是配置容器的一个很好的方式,使得创建更加通用的 Docker 镜像变得更加容易。

我们创建了一个数据卷容器,并将其发布到 Docker 注册中心,为下一章做准备,在下一章中,我们将连接三个容器,形成一个松耦合的单元。

第五章:连接容器

现在是时候将我们三个容器连接在一起,形成一个模块化的单元了。我将向你介绍两个服务,Docker ComposeCrane,它们可以用于自动化这个过程。本章将涵盖以下主题:

  • 手动连接容器

  • 探索数据卷容器的内容

  • 使用 Docker Compose 将容器连接到配置文件

  • 使用 Crane 将容器连接到配置文件

手动连接容器

让我们来看看如何将我们的服务容器连接到数据卷容器。首先,我们需要启动数据卷容器,然后启动 MySQL 容器,最后启动 WordPress 容器,如下命令所示:

docker run -d --name data-one oskarhane/data tail -f /dev/null
docker run --name mysql-one --volumes-from data-one -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql
docker run --name wordpress-one --volumes-from data-one --link mysql-one:mysql -d -p 80 oskarhane/wordpress

在这里,我们启动并命名了数据卷容器为 data-one。下一行启动了 MySQL 容器,命名为 mysql-one,并为其分配了数据卷容器。最后一行启动了我们的 WordPress 容器,命名为 wordpress-one,将 mysql-one 作为 MySQL 链接,并分配了数据卷容器。

显示以下输出:

手动连接容器

打开你的网页浏览器,前往容器的 URL 和端口,验证所有服务是否正在运行,并且容器是否按预期连接在一起。你应该会看到现在熟悉的 WordPress 安装页面。

如你现在可能已经猜到,你可以使用相同的 MySQL 链接和相同的数据卷容器启动另一个 WordPress 容器。你觉得会发生什么呢?

新的 WordPress 容器将是相同 WordPress 网站的另一个实例,具有相同的文件和相同的数据库。

当你连接容器时,Docker 会在目标容器中设置一些环境变量,以便你可以获取有关链接的源容器的信息。在我们的例子中,这些环境变量将在我们链接 MySQL 容器时设置,如下命令所示:

MYSQL_NAME=/wordpress-one/mysql-one
MYSQL_PORT=tcp://ip:3306
MYSQL_3306_TCP=tcp://ip:3306
MYSQL_3306_TCP_PROTO=tcp
MYSQL_3306_TCP_PORT=3306
MYSQL_3306_TCP_ADDR=ip

探索数据卷容器的内容

数据是写入数据卷容器吗?还是,当容器连接时,数据存储在 MySQL 和 WordPress 容器内部?你怎么判断呢?

确定这一点的一种方法是通过 shell 进入容器,以便你可以浏览它的文件系统。自 1.3 版本起,Docker 具备启动容器 shell 新实例的能力。运行旧的 docker attach 命令只是让你进入当前的 shell 实例,而在我们的例子中,这个实例正在运行 tail –f /dev/null。如果我们退出这个 tail 命令,容器将退出并关闭。因此,我们需要一个新的 shell 实例,以便在容器中调用任何命令,而不会导致容器退出。以下命令可以用来做到这一点:

docker exec -i -t data-one /bin/sh

–i–t 标志表示我们希望保持会话交互式并分配一个伪终端。data-one 是容器的名称,但你也可以使用容器 ID。如果你喜欢,我会选择 /bin/bash 而不是 /bin/sh,但该容器运行的是 BusyBox,那里没有 /bin/bash。对于我们接下来要执行的任务,使用哪个 shell 并不重要。

我们要做的是查看我们在这个数据卷容器中暴露为 VOLUMES 的目录。目录是 /var/www/html/var/lib/mysql

让我们在接下来的命令中探索:

ls -la /var/www/html
ls -la /var/lib/mysql

以下输出会显示:

探索数据卷容器的内容

我们在这两个目录中都看到了文件,这表明另外两个容器正在向这个容器写入数据。它通过数据来分隔服务。如果你需要更多的证据,可以在 shell 中启动 vi,编辑文件并在浏览器中重新加载站点。

这非常顺利且简单,对吧?容器之间相互作用,我们所需要做的就是通过一个命令将它们链接在一起。

使用 Docker Compose 连接容器

Docker Compose 以前叫做 Fig,但 Docker 收购了 Fig 并更改了名称。以下是 Docker 对 Docker Compose 的描述:

"Compose 是一个用于定义和运行复杂应用程序的工具。通过 Compose,你可以在一个文件中定义一个多容器应用程序,然后通过一个命令启动你的应用程序,执行所有需要的操作,使它能够运行。"

Docker Compose 本质上为我们提供了一种在配置文件中定义设置的方法,这样我们就不必在链接容器时记住所有容器的名称、需要暴露的端口、要使用的数据卷容器等等。

安装 Docker Compose

Docker Compose 在 GitHub 上有定期发布的版本,在撰写本书时,最新版本是 1.0.1。

我们将使用 Python 包管理器 pip 来安装 Docker Compose。我们的 EC2 实例没有预装 pip,所以我们需要先安装它,如下所示:

sudo su
wget https://bootstrap.pypa.io/get-pip.py && python ./get-pip.py

以下输出会显示:

安装 Docker Compose

在安装了 pip 之后,你可以继续安装 Docker Compose:

sudo pip install -U docker-compose

现在,你会看到 Docker Compose 已经安装,并且包含了所有的依赖项。输入 docker-compose --version 来验证它是否按预期工作。

基本的 Docker Compose 命令

以下是你应该熟悉的基本 Docker Compose 命令:

  • build:用于构建或重建服务

  • kill:强制停止服务容器

  • logs:查看服务的输出

  • port:用于打印端口绑定的公共端口

  • ps:用于列出容器

  • pull:用于拉取服务镜像

  • rm:用于移除已停止的服务容器

  • run:用于在服务上运行一次性命令

  • scale:设置要运行的服务容器数量

  • start:用于启动服务的现有容器

  • stop:停止正在运行的容器,但不将其移除

  • up:用于构建、重建、启动并附加到服务的容器;相关联的容器将被启动,除非它们已经在运行

如你所见,这些命令与 Docker 客户端命令非常相似,且大多数命令通过转发命令到 Docker 守护进程来执行相同的操作。我们将对其中一些命令进行更详细的讲解。

服务

当在 Docker Compose 中使用service一词时,它指的是docker-compose.yml配置文件中命名的容器。

使用运行命令

我们通常使用 run 命令来启动 Docker 客户端容器。使用 docker-compose 时,run 命令的含义非常不同。当你使用 docker-compose 执行命令时,它是在某个服务上执行的一次性命令。这意味着,如果我们将容器配置命名为 Ubuntu 并执行 docker-compose run ubuntu /bin/bash echo hello,容器将启动并执行 /bin/bash echo hello,然后关闭。与直接用 Docker 执行命令的不同之处在于,使用 docker-compose 时,所有关联的容器和 VOLUME 容器都将启动并连接。

使用 scale 命令

scale 命令非常有意思。当我们执行 docker-compose scale web=3 时,实际上是启动了我们命名为 web 的服务的三个容器。

使用 Docker Compose 设置我们的 PaaS

每个 Docker Compose 实例都位于自己的目录中,并且该目录下有一个名为docker-compose.yml的配置文件:

mkdir docker-compose-wp && cd $_
touch docker-compose.yml

这是我们docker-compose.yml文件的内容示例:

wp:
 image: oskarhane/wordpress
 links:
 - mysql:mysql
 ports:
 - "80"
 volumes_from:
 - paasdata
mysql:
 image: mysql
 volumes_from:
 - paasdata
 environment:
 - MYSQL_ROOT_PASSWORD=myrootpass
paasdata:
 image: oskarhane/data
 command: tail -f /dev/null

你可以看到我们在这里定义了三个服务,分别是wpmysqlpaasdata

让我们尝试这些服务,接下来会显示以下输出:

使用 Docker Compose 设置我们的 PaaS

执行 docker-compose up –d 以在守护进程模式下运行 docker-compose 和容器。

就是这么简单。打开你的网页浏览器,访问 Docker 主机和表格中列出的端口(在我的例子中是端口 49155);你应该会看到非常熟悉的 WordPress 安装页面。

使用 Crane 连接容器

Crane 很像 Docker Compose,但它提供了更多的配置选项。其创建者是这样描述 Crane 的:

“Crane 是一个用于协调 Docker 容器的工具。它通过读取一些配置(JSON 或 YAML),描述如何获取镜像以及如何运行容器。这样可以大大简化开发环境的设置,因为你不必手动启动每个容器,也不需要记住所有需要传递的参数。通过将配置与数据和应用程序存储在同一个仓库中,你可以轻松共享整个环境。”

如你所见,这一段也可以讲解 Docker Compose。

安装 Crane

Crane 安装简单,但保持更新并不容易。安装和更新使用相同的命令,因此我们必须时不时地执行一次命令以获得最新版本。

执行以下命令来安装 Crane:

bash -c "`curl -sL https://raw.githubusercontent.com/michaelsauter/crane/master/download.sh`" && sudo mv crane /usr/local/bin/crane

Crane 现在已安装在/usr/local/bin目录中。

用法

我不会在这里讲解所有的命令,因为它们与 Docker Compose 的命令类似,但我会在这里对一些命令做一些评论:

  • lift: 这个命令像 Docker Compose 的 up 命令一样,从你的配置文件中构建并运行容器

  • graph: 这将显示你的容器与配置文件之间的关系

  • logs: 这映射到 Docker Compose 的命令,但在这里你可以获取整个组的日志

  • status: 这也映射到 Docker Compose 的命令,但可以让你获取整个组的日志

配置

这就是 Crane 真正超越 Docker Compose 的地方。Crane 应用有更多的配置选项。配置文件必须命名为 crane.jsoncrane.yaml。对于每个容器,以下是你可以配置的内容:

  • image (string, required): 这是要构建/拉取的镜像名称

  • dockerfile (string, optional): 这是指向 Dockerfile 的相对路径

  • run (object, optional): 这些参数映射到 Docker 的 runcreate 命令:

    • add-host (array): 这将添加自定义的主机到 IP 的映射

    • cpuset (integer)

    • cpu-shares (integer)

    • detach (boolean) sudo docker attach <container name> 将正常工作

    • device (array): 这将添加主机设备

    • dns (array)

    • entrypoint (string)

    • env (array)

    • expose (array): 这表示要暴露给链接容器的端口

    • hostname (string)

    • interactive (boolean)

    • link (array): 这将链接容器

    • memory (string)

    • privileged (boolean)

    • publish (array): 这将映射网络端口到容器

    • publish-all (boolean)

    • restart (string) 重启策略

    • rm (boolean)

    • tty (boolean)

    • volume (array): 与普通 Docker 相比,主机路径可以是相对路径

    • volumes-from (array): 用于从其他容器挂载卷

    • workdir (string)

    • cmd (array/string): 这个命令用于附加到 docker run(覆盖 CMD)

  • rm (object, optional): 这些参数映射到 Docker 的 rm 命令:

    • volumes (boolean)
  • start (object, optional): 这些参数映射到 Docker 的 start 命令:

    • attach (boolean)

    • interactive (boolean)

设置与 Docker Compose 中相同的配置,它看起来类似以下代码。正如你可能理解的,你也可以用 JSON 格式写这个文件,但为了与 Docker Compose 版本进行方便比较,我将保持使用 yaml 格式:

containers:
 wp:
 image: oskarhane/wordpress
 run:
 volumes-from: ["mydata"]
 link: 
 - mymysql:mysql
 publish: ["80"]
 detach: true
 mymysql:
 image: mysql
 run:
 volumes-from: ["mydata"]
 detach: true
 env: ["MYSQL_ROOT_PASSWORD=rootpass"]
 mydata:
 image: oskarhane/data
 run:
 detach: true
 cmd: "tail -f /dev/null"

在这里,我们指定了三个容器,其中数据容器作为数据卷容器添加到其他容器中,而 MySQL 容器则与 WordPress 容器相链接。

将此文件保存为 crane.yaml,并输入 crane lift 来运行你的应用。

以下输出被显示:

配置

要查看容器的当前状态,可以输入 crane status。查看 wp 容器的最后一列,它显示容器未运行。输入 crane logs wp,然后查看以下命令输出的内容:

wp * WordPress not found in /var/www/html - copying now...
wp * Complete! WordPress has been successfully copied to /var/www/html
wp | 
wp | Warning: mysqli::mysqli(): (HY000/2002): Connection refused in - on line 5
wp * MySQL Connection Error: (2002) Connection refused

看起来我们的 WordPress 容器启动速度快于 MySQL 容器,因此 WordPress 容器在启动时无法找到 MySQL 容器。

在 Docker Compose 中也可能发生这种情况,因为没有检查 --link:ed 容器是否已经启动,至少在本文写作时是没有的。

这个问题无法通过 Docker Compose 解决;我们只能靠纯粹的运气,期望 MySQL 容器在 WordPress 容器尝试使用链接的 MySQL 容器之前已经准备好。

使用 Crane,您可以在配置文件中将容器分组,并对该组执行 run 命令,而不是对整个配置进行操作。

这非常简单;我们只需在 crane.yaml 文件末尾添加以下几行:

groups:
 default: ['mydata', 'mymysql', 'wp']
 data_db: ['mydata', 'mymysql']
 web: ['wp']

在这里,我们将 WordPress 容器与其他两个容器分开,这样我们就可以分别对它们执行 run 命令。

让我们首先通过调用 crane lift data_db --recreate 命令启动 data_db 组。我添加了 --recreate 标志,以确保我们正在创建新容器,而不是重用旧的容器。运行 crane status data_db 来确保它们正在运行。

既然我们知道 MySQL 容器正在运行,我们可以通过调用 crane lift web --recreate 命令来启动 WordPress 容器。

以下输出被显示:

配置

总结

现在,我们可以以不同的方式连接容器,以保持不同的服务在不同的容器中分开。我们学会了如何手动执行此操作,当容器之间有大量依赖关系时,这可能会非常困难。

我们简要地看了两个编排工具:Docker Compose 和 Crane。Crane 是一个独立的、更先进的工具,适用于那些希望对容器拥有更多控制的管理员。Crane 能够将容器分组,使其在存在依赖性时可能出现的时序问题中更加可靠。

在下一章中,我们将使用 Crane 运行两个应用实例,看看当我们希望让两个博客在常规 HTTP 端口(80)上对外公开时,可能会遇到哪些问题和机遇。

第六章:反向代理请求

拥有许多容器并在同一服务器上公开端口的一个大问题是,它们无法都监听其服务类型的标准端口。如果我们有一个 MySQL 后端服务并且有 10 个 MySQL 容器在运行,那么只有其中一个可以监听 MySQL 的标准端口 3306。对于那些暴露 Web 服务器的容器,标准端口 80 只能由其中一个 WordPress 容器使用。在这一章中,我们将讨论以下主题:

  • 解释问题

  • 想出问题的解决方案

  • 使用 Nginx 和 HAProxy 实现解决方案

  • 自动化域名映射的过程

解释问题

在同一主机上有多个容器运行相同服务的问题在于,用户应用程序会使用标准端口。使用 Web 浏览器并输入运行 WordPress 容器的 Docker 主机的 IP 地址时,默认会请求端口 80 上的资源。你不能指望用户记住非标准端口来访问你的网站。

解释问题

到达这三个容器的唯一方式是手动输入容器的暴露端口号。

寻找解决方案

在我们进入解决方案之前,让我解释一下什么是常规代理服务器,以防你不熟悉它。

代理服务器是一个代表你连接服务并将所有结果转发给你的服务器。在你设置将所有流量通过代理服务器路由之后,你—作为用户—不会察觉到它的存在。所有内容将像平常一样工作。

然而,服务所有者只看到某台机器(代理服务器)与他们连接。如果另一个用户使用相同的代理服务器并与您使用相同的服务,服务所有者无法区分并且会将您视为同一个用户。

寻找解决方案

通过代理服务器连接的不同用户将显示为一个用户。

如前面的图示所示,服务所有者仅看到某个 IP 地址为 213.12.12.3 的用户连接到他们。

那么,如果我们在 Docker 主机上使用这个方法呢?如果我们在所有容器前放置一些东西呢?根据请求的是哪个域名,这个东西会将请求转发到正确的容器和端口,然后将请求的响应转发给请求的用户。

有些专门设计来解决这种问题的工具,叫做 反向代理(反向是因为代理位于另一端,使得用户只能看到一个 IP,并转发请求)。

如果我们在 Docker 主机服务器上安装并配置了反向代理,那么图示将如下所示:

寻找解决方案

反向代理使所有 Docker 容器看起来像是一个。

反向代理监听 80 端口——标准的 Web 端口——当 domain1.com 的请求到来时,代理会查看其配置,看看是否有为该域名指定的转发端点。如果有,反向代理会将请求转发到正确的 Docker 容器,等待其响应,并在响应到达时将容器的响应转发给请求的用户。

这是我们想要的解决方案。现在唯一的问题是我们要使用哪种反向代理。市面上有很多种反向代理;其中一些反向代理有更具体的用途,比如负载均衡,而另一些则是多功能服务,同时也包含了反向代理功能,比如 Web 服务器。

实现解决方案

在选择解决问题的工具时,你总会有偏好。有时你选择一个工具是因为你对它熟悉并且它足够好;有时你选择它是因为它的性能优秀,或者只是因为你想尝试一些新的东西。

这就是我们通过两个不同工具来解决这个问题的原因。最终结果是相同的,但这两个工具的配置方式略有不同。

在我们开始实现解决方案之前,我们使用 Crane 启动一个包含三个容器的应用实例,并通过连接到网站来验证它是否正常工作。让 Docker 决定公用端口,因此它是 491XX。记住这个端口,因为我们在实施解决方案时将会使用它。

我们需要将我们希望使用的域名指向 Docker 主机的 IP 地址。我们可以通过将域名的 A 记录设置为我们服务器的 IP 地址,或者在本地的 /etc/hosts 文件中添加一行,将域名请求指向我们服务器的 IP 地址。

我会选择后者,并将此内容添加到我 Mac 的 /etc/hosts 文件中:

54.148.253.187 domain1.com
54.148.253.187 domain2.com
54.148.253.187 domain3.com

注意

确保将上面的 IP 地址替换为你的服务器 IP 地址。

使用 HAProxy 实现

HAProxy (www.haproxy.org) 是一个负载均衡器,负责将流量转发到其背后的不同服务。

这是 HAProxy 对自身的描述:

“HAProxy 是一个免费的、非常快速且可靠的解决方案,提供高可用性、负载均衡和基于 TCP 和 HTTP 的应用程序代理。它特别适用于流量非常大的网站,支撑了世界上一些最受欢迎的网站。多年来,它已成为事实上的标准开源负载均衡器,现在大多数主流 Linux 发行版都已默认安装,且通常在云平台上作为默认部署工具。”

听起来这正是我们所需要的。

安装 HAProxy

正如引用中所述,许多系统已经预装并随系统发布。如果找不到它,使用 Ubuntu 或 Debian 的包管理器(apt-get install haproxy)或其他发行版的包管理器,应该可以找到。

在我的 Amazon EC2 实例上,该实例运行的是 Amazon Linux,HAProxy 可以通过yum install haproxy安装。

以下输出将会如下所示:

安装 HAProxy

这不是最新版本,但对于我们接下来的操作来说已经足够了。

配置 HAProxy

我们将在文件/etc/haproxy/docker.cfg中编写 HAProxy 配置,这样我们就不需要删除默认配置文件中的所有内容,因为它将来可能作为参考。

HAProxy 将其配置分为四个部分:global、defaults、frontend 和 backend。不要将 frontend 和 backend 与前端和后端开发混淆。在这里,frontend 是面向互联网的服务器部分,而 backend 是 HAProxy 后面的服务器部分,在我们这个例子中是 Docker 容器。

打开配置文件并从这里开始输入通用设置,如下所示:

global
    daemon
    maxconn 4096
    pidfile /var/run/haproxy.pid
defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

现在,我们输入要监听的端口和要使用的后端配置,针对哪个域名:

frontend http-in
    bind *:80
    acl is_site1 hdr_end(host) -i domain1.com
    use_backend site1 if is_site1

我们定义了常规的传入 HTTP 流量应该在端口80上被捕获。acl是访问控制列表(Access Control List),是一种灵活的解决方案,可以根据请求中的内容做出决策。hdr_end(host) -i domain1.com函数调用意味着对 header host 的末尾进行不区分大小写的匹配,匹配字符串domain1.com。这个匹配的结果(布尔值)将被保存在is_site1变量中。

请注意,这意味着domain1.com的所有子域都将与此设置匹配。如果你只想匹配www.domain1.com,可以改用hdr(host) -i www.domain1.com

现在我们已经将匹配结果保存在is_site1变量中,接下来可以向名为site1的后端配置发送请求。

我们将其附加到配置文件中:

backend site1
    balance roundrobin
    option httpclose
    option forwardfor
    server s1 127.0.0.1:49187 maxconn 450

我们将后端名称定义为site1,设置一些选项,并将服务器和端口添加到我们的 WordPress 容器。

注意

确保在之前的代码中输入的是你 WordPress 容器暴露的端口,而不是49187

是时候尝试这个配置了。保存配置文件,并在终端中使用以下命令进行测试:

haproxy -f /etc/haproxy/docker.cfg –c

输出应该显示配置文件有效

确保你的机器上没有其他应用在监听端口80。你可以使用类似netstat –a的命令来验证80或 HTTP 是否已经列出。如果已经有应用在监听,找到并关闭它。

使用以下命令启动 HAProxy:

haproxy -f /etc/haproxy/docker.cfg –D

-D选项意味着我们希望它作为后台进程运行。你运行这个命令时应该不会看到任何输出。

让我们通过运行ps aux | grep haproxy来检查 HAProxy 是否在运行。你应该能看到它列在那儿。最后,我们通过运行netstat –a | grep http来验证它是否在监听端口80。现在,你应该在列表中看到相应的内容。

这里显示的是获得的输出:

配置 HAProxy

一切看起来都很不错!

简单回顾一下我们所做的:我们设置了一个服务,它在服务器的端口 80 上监听传入的请求。当该端口接收到请求时,检查请求头中的主机,看看是否与 domain1.com 匹配。如果匹配,请求将被转发到 IP 地址 127.0.0.1 和端口 49187。来自该 IP 和端口的响应将返回给请求者。

现在到了关键时刻。打开你的网页浏览器,输入 URL domain1.com

确保在你的主机文件中有 domain1.com 的条目,并指向你的服务器。

执行上述步骤后,你将看到以下网站界面:

配置 HAProxy

你可以看到,在位置栏中没有指定端口号。太棒了!

向 HAProxy 添加更多域

我们并不是为了仅在端口 80 上提供单个 Web 应用程序而进行这么多工作,这可以通过不使用反向代理来实现。使用 Crane 启动另一个 WordPress 应用程序,方法是将旧的配置复制到一个新目录,并更改服务名称,如下所示:

cd..
cp –r crane-wp crane-wp2
cd crane-wp2
sed -i "s/wp/wp2/g" crane.yaml
sed -i "s/mydata/mydata2/g" crane.yaml
sed -i "s/mymysql/mymysql2/g" crane.yaml
crane lift data_db
crane lift wp2

#check out port for new container named wp2
docker ps

再次打开 HAProxy 配置文件,并在前端添加两行:

acl is_site2 hdr_end(host) -i domain2.com
use_backend site2 if is_site2

然后,添加一个名为 site2 的新后端配置:

backend site2
 balance roundrobin
 option httpclose
 option forwardfor
 server s2 127.0.0.1:49188 maxconn 450

确保你替换了你获得的端口号。重启 HAProxy 并进行我们上次启动时所做的检查。

要重启 HAProxy,运行 /etc/init.d/haproxy restart

注意

HAProxy 可以使用以下命令重新加载新配置,而不会丢失活动会话:

haproxy -f /etc/haproxy/docker.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)

打开浏览器,访问 domain1.com,确保旧站点正常运行。如果它正常工作,再访问 domain2.com。你应该会看到另一个 WordPress 安装站点。为了确保它不是相同的,安装其中一个。或者,访问 domain3.com,看看当一个域指向服务器而在 HAProxy 中没有匹配时会发生什么。

使用 Nginx 实现

现在,我们将做与 HAProxy 相同的操作,但我们将使用出色的 Web 服务器 Nginx(nginx.org/en/)作为反向代理。Nginx 是一个功能全面且速度极快的 Web 服务器,占用内存极少。

下面是对 Nginx 的描述:

"nginx [engine x] 是一个 HTTP 和反向代理服务器,同时也是一个邮件代理服务器,由 Igor Sysoev 编写。长期以来,它已经在许多重载的俄罗斯网站上运行,包括 Yandex、Mail.Ru、VK 和 Rambler。根据 Netcraft 的数据,nginx 在 2014 年 11 月服务或代理了 20.41% 的最繁忙网站。以下是一些成功的案例:Netflix、Wordpress.com、FastMail.FM。"

这也听起来像是我们需要的,就像之前使用 HAProxy 时一样。

安装 Nginx

Nginx 在所有 Linux 包管理器中都可用,如 aptitude/aptyum 等,因此可以通过 apt-get install nginxyum install nginx 来简单安装。由于它是开源的,你当然也可以从源代码进行安装。

配置 Nginx

我们将把配置添加到名为/etc/nginx/conf.d/wp1.conf的文件中。

在您喜欢的文本编辑器中创建并打开此文件:

server {
    listen 80;
    server_name domain1.com;
    charset UTF-8;

    if ($host !~ ^(domain1.com)$ ) {
         return 444;
    }
}

如您所见,这个块使服务器监听80端口,并且匹配域名domain1.com,以便此配置生效。始终指定服务器字符集是一个好习惯,这样在转发过程中网站文本就不会出现错误编码;因此,我们也添加了这一行。为了只监听domain1.com而不监听其他域名(如果在服务器名称部分没有匹配,Nginx 会使用找到的第一个配置作为默认配置),我们在其他进入的请求上返回 HTTP 状态码444(无响应)。

那么,我们该如何处理domain1.com80端口的请求呢?

将此添加到服务器的范围内(大括号内):

location / {
    proxy_pass http://wp1;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-NginX-Proxy true;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_redirect off;
}

location块将匹配所有请求,因为它匹配/。稍后我们将回到proxy_pass部分。除此之外,您会看到我们设置了很多头信息,其中大部分告诉我们的 Docker 容器请求者的真实 IP 地址等等。

回到proxy_pass部分。这部分实际上是将请求转发到名为wp1的内容。这叫做上游,我们需要定义它。

将此添加到服务器范围之外:

upstream wp1 {
    server 127.0.0.1:49187;
}

完整的配置文件/etc/nginx/conf.d/wp1.conf现在应该是这样的:

upstream wp1 {
  server 127.0.0.1:49187;
}

server {
  listen 80;
  server_name domain1.com;
  charset UTF-8;

  if ($host !~ ^(domain1.com)$ ) {
    return 444;
  }

  location / {
    proxy_pass http://wp1;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-NginX-Proxy true;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_redirect off;
  }
}

保存文件后,在大多数 Linux 系统上,您可以使用命令sudo /etc/init.d/nginx configtestsudo service nginx configtest来测试语法错误。

注意

确保在启动 Nginx 之前关闭 HAProxy,否则您会收到错误提示,说明 Nginx 无法绑定到80端口。您可以使用以下命令做到这一点:

/etc/init.d/haproxy stop

如果测试成功,现在我们可以重新启动(或启动)Nginx 服务器。再次使用sudo /etc/init.d/nginx restartsudo service nginx restart在大多数系统上执行此操作。

打开您的浏览器,输入domain1.com的 URL,查看我们的 WordPress 安装页面。为了确保只有domain1.com有效,尝试访问domain2.com并期待没有响应。

向 Nginx 添加更多域名

要向 Nginx 添加另一个匹配域名,您可以在/etc/nginx/conf.d/目录下创建一个新文件,并重新加载 Nginx 配置,如下所示:

cp /etc/nginx/conf.d/wp1.conf /etc/nginx/conf.d/wp2.conf
sed -i "s/wp1/wp2/g" /etc/nginx/conf.d/wp2.conf
sed -i "s/domain1/domain2/g" /etc/nginx/conf.d/wp2.conf
sed -i "s/49187/49188/g" /etc/nginx/conf.d/wp2.conf

#test config
/etc/init.d/nginx configtest

#reload config
/etc/init.d/nginx reload

复制配置文件,替换几个名称,运行configtest,然后重新加载 Nginx。

在浏览器中尝试domain1.com,确保它仍然有效。您应该仍然能看到 WordPress 安装页面(除非您已经安装了 WordPress);之后,前往domain2.com查看我们的新配置是否生效。

如果您想关闭一个网站,只需将文件扩展名从.conf更改为其他扩展名,并重新加载 Nginx。

自动化域名映射过程

这种设置的局限性在于每次添加新域名时都需要手动操作。在我的网站(oskarhane.com)上,我写了一些关于如何自动化这个过程的博客文章,那些文章是我有史以来阅读最多的文章。

当我找到 nginx-proxy 时,我非常高兴。nginx-proxy 通过通过 Docker 远程 API 监控 Docker 事件,比我更聪明地解决了这个问题。

注意

你可以在其 GitHub 页面上查看更多关于 nginx-proxy 的信息(github.com/jwilder/nginx-proxy)。

nginx-proxy 作为一个容器提供,我们可以通过执行以下命令来运行它:

docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock jwilder/nginx-proxy

我们将 Docker 套接字提供给容器,这样它就可以监听我们感兴趣的事件,即容器的启动和停止。我们还将 Docker 主机的端口 80 绑定到这个新容器,使其成为所有传入 Web 请求的入口容器。在启动 nginx-proxy 容器之前,请确保停止 Docker 主机上的 Nginx。你可以使用以下命令来执行此操作:

/etc/init.d/nginx stop

当容器启动时,nginx-proxy 会创建一个 nginx 反向代理 config 文件并重新加载 Nginx——就像我们所做的那样,但通过 nginx-proxy 完全自动化。

要告诉 nginx-proxy 我们希望将哪个域名映射到哪个容器,我们必须使用名为 VIRTUAL_HOST 的环境变量运行容器。

在我们的 crane.yaml 文件 中,我们在 wp 运行部分添加了一个环境变量:

containers:
 wp:
 image: oskarhane/wordpress
 run:
 volumes-from: ["mydata"]
 link:
 - mymysql:mysql
 publish: ["80"]
 detach: true
 env: ["VIRTUAL_HOST=domain1.com"]

现在,我们只需再次用 crane 启动它,就能将该容器映射到 domain1.com 域名上的端口 80

crane lift web --recreate

总结

在本章中,我们看到如何解决多个容器希望在同一公共端口提供数据的问题。我们了解了什么是代理服务器和反向代理服务器,以及反向代理如何在负载均衡中使用。

我们安装并配置了两个不同的反向代理:HAProxy 和 Nginx。在我的工作流程中,Nginx 配置更适合,只需复制一个文件,替换几个词,然后重新加载 Nginx 就能使其工作。HAProxy 在你的设置中可能表现更好;选择权在你,不能说哪一个比另一个更好。

nginx-proxy 自动化了为启动的容器创建反向代理的过程,是 PaaS 的一个不错的解决方案,除了一个问题:简单直接的部署。这就是下一章的内容。

第七章:在我们的 PaaS 上部署

在之前的章节中,我们通过结合 Crane 和 nginx-proxy 等工具,从非常实操的方式逐步过渡到“拼凑自动化”的方式。仍然有一部分缺失——如何部署你的代码。

在这一章中,我们将讨论以下主题:

  • 我们当前设置的问题

  • 可用的工具/服务

  • Dokku——迷你版 Heroku

  • 使用 Dokku 设置 WordPress 应用程序

我们当前设置的问题

我们当前的设置由三个容器组成:一个 WordPress 容器,一个 MySQL 容器和一个数据卷容器,它们通过 Crane 连接在一起。

我们当前使用 VOLUME 容器作为文件存储的主要问题是,我们需要一种方式进入该卷以编辑文件。目前,进入它的唯一方法是将它挂载到另一个容器上。

另一个问题是我们没有对源代码进行版本控制。我们只是下载了 WordPress 和一些插件并放在那里。如果我们更新了 WordPress 或做了其他更改怎么办?我们肯定希望将这些操作纳入版本控制中。

如果我们希望保持当前的应用架构不变,有两种选择:

  • 创建一个新容器,挂载我们的数据卷容器,安装它,并通过 SSH 访问它

  • 在我们的 WordPress 容器中安装并打开 SSH 访问

安装了 SSH 后,我们可以从远程机器访问容器的 shell,因此,我们可以安装 Git 以对文件进行版本控制。通过这种方式,当我们需要时,可以连接并将新代码推送到数据卷容器中。

连接到 SSH 时,你可以直接进入容器,而不需要连接到 Docker 主机的 shell。

如果你可以连接到 Docker 主机,并且从那里打开一个新的 shell 进入你的数据卷容器,那么第三个选择是通过 SSH 进入 Docker 主机,然后使用 docker exec –it container_name /bin/sh 访问容器。

虽然这样肯定有效,但也有更简单的方法。

可用的工具/服务

当我们看到今天可用的托管 PaaS 提供商时,有两个会浮现在脑海中——OpenShift 和 Heroku。许多开发者喜欢 Heroku,因为它的易用性。它们的理念给出了一个提示,为什么会这样:

“开发者生产力:

开发者生产力是我们的口号,是我们所做一切的核心。为什么要做三步操作,明明一步就够?为什么要做任何操作,当零步就能完成的时候?”

开发人员通常希望将时间花在他们的代码上,而不是管理服务器、部署等任务。

可用的工具/服务

在 Heroku 上,你会得到一个远程 Git 仓库,你可以将代码推送到其中。你的应用程序的语言和依赖项会通过特定的文件来识别,这些文件根据你使用的语言而有所不同。环境变量用于配置,并且你通过在一个特殊的文件中指定命令来指示 Heroku 执行什么操作,这个文件称为Procfile,你需要将其包含在源代码中。

每当你将代码推送到远程 Heroku Git 仓库时,应用程序会重建,并立即在线发布。如果你有特殊的构建需求,Heroku 允许你创建自己的构建包,其中可以精确指定要执行的操作。

基本上,如果你想在 Heroku 上设置一个 WordPress 博客,你需要按照以下步骤操作:

  1. 本地下载 WordPress 的最新版本。

  2. 创建一个 Procfile 并定义要执行的内容(在此情况下是运行 PHP 和 Apache2 的构建包)。

  3. 创建一个 composer.json 文件,指定 PHP 是一个依赖项。

  4. 对 WordPress 配置文件进行一些更改。

  5. 创建 Heroku 应用程序,添加附加组件(如数据库),并在 Heroku 上定义环境变量。

  6. 将本地代码推送到 Heroku。

当你对代码进行更改时,只需通过 Git 推送到 Heroku 来部署新代码。你不能直接在 Heroku 服务器上编辑代码,也不能安装主题或插件(必须在本地做,然后推送新代码)。

可用的工具/服务

如果你选择了 OpenShift 等提供商,你将对你的 PaaS 拥有更多的控制权。你可以通过 SSH 连接并存储应用程序下载的静态文件。

我们正在寻找的就是这样的东西;只是我们想要托管自己的平台,并在后台使用 Docker 容器。

Dokku – 基于 Docker 的迷你 Heroku

Dokku 可以在 github.com/progrium/dokku 找到。它是一个由作者描述如下的项目:

“大约 100 行 Bash 脚本,构建 Docker 支持的迷你 Heroku。”

在功能方面,Dokku 执行部署的方式与 Heroku 相同。让我们安装 Dokku,看看它能为我们的 PaaS 做些什么。

安装

Dokku 需要 Ubuntu 14.04 来运行,我们从创建一个运行该版本的 EC2 实例开始。

这里是我们看到的截图:

安装

当我们创建好实例并使其运行后,我们可以开始安装 Docker 本身:

sudo apt-get install docker.io

完成后,我们继续安装 Dokku。

推荐的引导式 bash 安装方法对我来说不起作用,因此我改为克隆了仓库:

cd /tmp
git clone https://github.com/progrium/dokku.git
cd dokku
sudo make install
dokku version

注意

你可以在官方安装页面 progrium.viewdocs.io/dokku/installation 阅读关于安装过程的信息。

安装部分可能需要一段时间,但应该会成功。

根据前面链接中的文档,我们应该编辑 /home/dokku/VHOST 文件,填写我们计划使用的域名。我们暂时跳过这一步,因为它涉及到设置一些 DNS 记录。当我们将该文件留空时,我们将通过 http://ip:port 的形式访问我们的 PaaS。稍后我们会回到这一点。

现在剩下的唯一步骤是,在本地机器上创建一对 ssh 密钥,并将公钥部分添加到服务器用户 Dokku 的 authorized_keys 文件中,这样我们就可以通过 Git 以非常安全的方式进行连接,而无需使用密码。

在本地机器上,使用以下命令:

cd ~/.ssh
ssh-keygen –t rsa
#I named my key pair id_rsa
cat id_rsa.pub
#copy the output so you have it in your clipboard

在服务器上,使用以下命令:

#As your ubuntu user
#Replace <publickey> with the key you just copied
#<remoteuser> can be replaced with anything, like "remoteuser".
echo "<publickey>" | sudo sshcommand acl-add dokku <remoteuser>

如果你将 ssh-key 命名为其他名称而不是 id_rsa,你将需要编辑本地的 .ssh/config 文件才能使其工作。

现在 Dokku 配置已完成,我们应该能够开始使用它了。

创建一个示范的 Dokku 应用

现在是时候设置一个示范应用程序,让你可以学习这个过程。在这种情况下,我们以 Heroku 的 Node.js 示例应用为例。

我们首先通过克隆 Heroku 的 node-js-sample GitHub 仓库来获取应用的内容。接下来的所有任务都应该在你的本地机器上完成,当我输入 server.com 时,你应该输入你服务器的 URL 或 IP 地址。如果你使用的是域名,确保你已经为其设置了 DNS 记录,或者在本地的 /etc/hosts 文件中输入了记录:

#Clone the repo
git clone git@github.com:heroku/node-js-sample.git
cd node-js-sample
#Add a Dokku git remote
git remote add dokku dokku@server.com:first-app

#Push to Dokku
git push dokku master

当我们将代码推送到一个不存在的分支或应用名时,Dokku 会创建一个新应用并进行部署。当推送完成时,你应该会在输出的底部看到如下内容:

=====> Application deployed:
 http://54.191.69.5:49154

当然,IP 地址和端口号对你来说不会相同。

输出结果如下所示:

创建一个示范的 Dokku 应用

在你的网页浏览器中输入 ip:port,你会看到一个页面显示 Hello World。我们刚刚在 Dokku 上部署了第一个应用!

为了修改并重新部署该网站,我们可以在 node-js-sample 项目的 public/ 文件夹内创建一个名为 index.html 的文件。这个 Node 应用始终会在公共文件夹中查找文件。如果请求的文件未找到,应用会回退到打印 Hello World。因此,如果我们创建一个文件并请求它,Node 服务器会将其提供给我们。

将以下内容粘贴到 index.html 文件中:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hello</title>
  </head>
  <body>
    <h1>First edit!</h1>
  </body>
</html>

这只是一个简单的 HTML 页面。

让我们继续创建文件并推送,代码如下所示:

nano public/index.html
#paste the HTML
#save the file

#commit your changes
git add public/index.html
git commit –m "Added first HTML page."

#push to dokku
git push dokku master

注意

从输出中可以注意到,端口号每次部署时都会发生变化,因为会创建一个新的容器,旧容器会被关闭。

以后,当我们添加一个域名进行部署时,URL 当然会保持不变。Nginx 配置文件会在部署时更新。将浏览器指向新的 ip:port,你应该会看到一个巨大的标题,写着 First edit!

每当你进行编辑时,只需推送即可。Dokku 会处理剩下的部分。

Dokku 的工作原理

正如我之前描述 Heroku 的基本步骤一样,当你在 Dokku 上进行部署时,你可能会认出这些步骤,这也是 Dokku 的目标。他们希望像我们这样的人在部署过程中感到舒适。

Dokku 可以看作是以下工具之间的粘合剂:Docker、Buildstep、ssh-command、pluginhook、ssh、git 和 nginx。源代码大约有 100 行,并且附带了一些插件,这些插件加起来大约有 500 行代码。这就是 Dokku 的强大之处——任何人都可以编写插件来扩展 Dokku 的功能。

我们还没有安装任何插件,而像我们这样一个干净的安装只能执行一些基本操作,如部署、查看应用日志、删除应用和在应用容器中运行命令。插件有很多,所有插件都列在progrium.viewdocs.io/dokku/plugins上。

接收过程

如果我们查看主 Dokku 文件(位于项目根目录下,名为dokku),我们会注意到每当触发receive操作时(即当我们推送到主分支时),会看到以下代码:

case "$1" in
  receive)
    APP="$2"; IMAGE="dokku/$APP"
    echo "-----> Cleaning up ..."
    dokku cleanup
    echo "-----> Building $APP ..."
    cat | dokku build $APP
    echo "-----> Releasing $APP ..."
    dokku release $APP
    echo "-----> Deploying $APP ..."
    dokku deploy $APP
    echo "=====> Application deployed:"
         dokku urls $APP | sed "s/^/       /"
    echo
    ;;

通过此输出,我们可以识别出何时已经推送到主分支。

如果我们跟随插件链,当调用deploy时,最终会调用一个名为post-deploy的插件钩子。一个标准插件nginx-vhosts会被触发,进而调用该插件中的nginx:build-config函数。

以下是前面文件中的一段代码:

case "$1" in
  nginx:build-config)
    APP="$2"; DOKKU_APP_LISTEN_PORT="$3"; DOKKU_APP_LISTEN_IP="${4}"
    VHOST_PATH="$DOKKU_ROOT/$APP/VHOST"
    WILDCARD_SSL="$DOKKU_ROOT/tls"
    SSL="$DOKKU_ROOT/$APP/tls"

    if [[ -z "$DOKKU_APP_LISTEN_PORT" ]] && [[ -f "$DOKKU_ROOT/$APP/PORT" ]]; then
      DOKKU_APP_LISTEN_PORT=$(< "$DOKKU_ROOT/$APP/PORT")
    fi
    if [[ -z "$DOKKU_APP_LISTEN_IP" ]] && [[ -f "$DOKKU_ROOT/$APP/IP" ]]; then
      DOKKU_APP_LISTEN_IP=$(< "$DOKKU_ROOT/$APP/IP")
    fi

    [[ -f "$DOKKU_ROOT/$APP/ENV" ]] && source $DOKKU_ROOT/$APP/ENV

    if [[ ! -n "$NO_VHOST" ]] && [[ -f "$DOKKU_ROOT/$APP/VHOST" ]]; then
      ...
      NGINX_CONF="$PLUGIN_PATH/nginx-vhosts/templates/nginx.conf"
      SCHEME="http"
      ...
      APP_NGINX_TEMPLATE="$DOKKU_ROOT/$APP/nginx.conf.template"
      if [[ -f $APP_NGINX_TEMPLATE ]]; then
        echo "-----> Overriding default nginx.conf with detected nginx.conf.template"
        NGINX_CONF=$APP_NGINX_TEMPLATE
      fi

      xargs -i echo "-----> Configuring {}..." < $VHOST_PATH
      # Include SSL_VHOSTS so we can redirect http to https on that hostname as well
      NOSSL_SERVER_NAME=$(echo $NONSSL_VHOSTS $SSL_VHOSTS| tr '\n' ' ')

      if [[ -n "$DOKKU_APP_LISTEN_PORT" ]] && [[ -n "$DOKKU_APP_LISTEN_IP" ]]; then
        echo "-----> Creating $SCHEME nginx.conf"
        echo "upstream $APP { server $DOKKU_APP_LISTEN_IP:$DOKKU_APP_LISTEN_PORT; }" > $DOKKU_ROOT/$APP/nginx.conf
        eval "cat <<< \"$(< $NGINX_CONF)\" >> $DOKKU_ROOT/$APP/nginx.conf"

        echo "-----> Running nginx-pre-reload"
        pluginhook nginx-pre-reload $APP $DOKKU_APP_LISTEN_PORT $DOKKU_APP_LISTEN_IP

        echo "       Reloading nginx"
        restart_nginx
      fi
    else
      if [[ -f "$DOKKU_ROOT/$APP/VHOST" ]]; then
        echo "-----> VHOST support disabled, deleting $APP/VHOST"
        rm "$DOKKU_ROOT/$APP/VHOST"
      fi
      if [[ -f "$DOKKU_ROOT/$APP/nginx.conf" ]]; then
        echo "-----> VHOST support disabled, deleting nginx.conf"
        rm "$DOKKU_ROOT/$APP/nginx.conf"

        echo "-----> VHOST support disabled, reloading nginx after nginx.conf deletion"
        restart_nginx
      fi
    fi
    ;;

如果我们查看这段代码,可以看到它会在$DOKKU_ROOT/$APP/VHOST文件中查找域名,如果找到,则会设置一些配置变量,并将其插入到templates/nginx.conf文件的副本中。

该文件的内容如下:

server {
  listen      [::]:80;
  listen      80;
  server_name $NOSSL_SERVER_NAME;
  location    / {
    proxy_pass  http://$APP;
    proxy_http_version 1.1;
    proxy_set_header Upgrade \$http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host \$http_host;
    proxy_set_header X-Forwarded-Proto \$scheme;
    proxy_set_header X-Forwarded-For \$remote_addr;
    proxy_set_header X-Forwarded-Port \$server_port;
    proxy_set_header X-Request-Start \$msec;
  }
  include $DOKKU_ROOT/$APP/nginx.conf.d/*.conf;
}

现在,这看起来非常像我们在上一章创建的 nginx 配置,对吧?Dokku 的 post-deploy 部分基本上就是 Jason Wilder 的nginx-proxy。它们实现了相同的结果,但采用了完全不同的方法。

Dokku 插件

Heroku 中的附加组件在 Dokku 中称为插件。由于我们无法直接从 Dokku 指定docker run命令参数,因此我们需要插件来连接容器并添加数据卷容器。

这是一些可用的 Dokku 插件列表,我们很快就会使用它们。

Dokku 域名插件

Dokku 域名插件允许您在一个应用中指定多个域名。默认情况下,一个应用只能映射一个 URL:

dokku domains:set myawesomeapp.com www.myawesomeapp.com

URL: github.com/wmluke/dokku-domains-plugin

Dokku-docker-options

使用此插件,您可以在执行docker run命令时传递任何选项给 Docker 守护进程。它可以用于挂载卷、链接容器等:

dokku docker-options:add myapp "-v /host/path:/container/path"
dokku docker-options:add myapp "-link container_name:alias"

URL: github.com/dyson/dokku-docker-options

Dokku 的卷插件

这是一个插件,允许您在服务容器上挂载卷。它还提供了导出和恢复数据的命令:

dokku volume:add foo /path/in/container
dokku volume:dump foo /path/in/container > foo.tar.gz

URL: github.com/ohardy/dokku-volume

您可以使用此插件链接容器:

dokku link:create <app> NAME [ALIAS]
dokku link:delete <app> NAME [ALIAS]

URL: github.com/rlaneve/dokku-link

MariaDB 插件 for Dokku

这个插件允许你创建和使用 MariaDB 容器。MariaDB 可以作为 MySQL 的替代品,通常速度更快:

dokku mariadb:create <app>
dokku mariadb:link <app> <db>
dokku mariadb:dumpraw <app>

URL: github.com/Kloadut/dokku-md-plugin

MySQL 插件: github.com/hughfletcher/dokku-mysql-plugin

使用 Dokku 设置 WordPress 应用

现在我们已经玩了一会儿 Dokku,探索了它是如何工作的以及有哪些插件可用,是时候设置一个 WordPress 站点了。毕竟,这就是我们最初探索它的原因。

我们将要做的是:

  1. 创建一个新的本地 Git 仓库,并下载 WordPress。

  2. 安装 MariaDB 插件,创建数据库,并将其与我们的应用连接。

  3. 配置 WordPress 连接到我们链接的数据库。

在你的本地计算机上,下载并解压最新版本的 WordPress,并创建一个新的 Git 仓库。创建一个composer.json文件,告诉 Dokku 这是一个 PHP 应用。

你可以在devcenter.heroku.com/articles/buildpacks阅读更多关于如何提示 Dokku 你正在创建哪种类型的应用的内容(是的,Dokku 使用 Heroku 的 buildpacks),并会尝试检测功能。Dokku 使用一个名为 Buildstep 的库,通过 Docker 和 Buildpacks 来构建应用。

现在让我们开始吧。

我在我的域名服务器上使用了ohdokku.com来托管这个应用:

#Download Wordpress
curl -O https://wordpress.org/latest.zip
unzip latest.zip
mv wordpress wp1
cd wp1

#Create a new Git repo
git init
git add .
git commit –m "Initial commit."

#Create a composer.json file to tell Dokku we are using php
echo '{}' > composer.json
git add .
git commit -am "Add composer.json for PHP app detection."

#Add a remote so we can push to Dokku
git remote add dokku dokku@ohdokku.com:wp1

在服务器上,我们需要安装 MariaDB 或 MySQL 插件:

cd /var/lib/dokku/plugins
sudo git clone --recursive https://github.com/Kloadut/dokku-md-plugin mariadb
cd mariadb/dockerfiles/
git checkout master
cd ../../
sudo dokku plugins-install

回到客户端(你也可以在服务器上执行这些操作,但这种 PaaS 的核心是让你能够在客户端完成所有这些重复的工作)。

结果如下:

使用 Dokku 设置 WordPress 应用

如你所见,创建命令的输出会显示我们的数据库凭证。

现在数据库已经设置好了,我们可以开始第一次推送我们的应用:

git push dokku master

你应该注意到 Dokku 检测到你正在推送一个 PHP 应用。由于我们在composer.json文件中没有指定任何内容,因此会启动一个 PHP 和 Apache2 的默认包。

创建一个名为 wp1_db 的 MariaDB 数据库:

ssh dokku@ohdokku.com mariadb:create wp1_db
ssh dokku@ohdokku.com mariadb:link wp1 wp1_db

如果我们在浏览器中输入ip:port,一个熟悉的页面欢迎我们——WordPress 安装页面。当我们点击继续按钮时,我们会看到在创建wp-config.php文件之前,我们无法继续。

我们刚刚建立了 MariaDB 容器和 WP 容器之间的连接,但我们还没有在代码中建立连接。WordPress 不知道如何连接到数据库。

我们首先将wp-config-sample.php文件重命名为wp-config.php,然后在编辑器中打开这个文件:

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for Wordpress */
define('DB_NAME', getenv('DB_NAME'));

/** MySQL database username */
define('DB_USER', 'root');

/** MySQL database password */
define('DB_PASSWORD', getenv('DB_PASSWORD'));

/** MySQL hostname */
define('DB_HOST', getenv('DB_HOST').":".getenv('DB_PORT'));

编辑你刚才看到的凭证,使 WordPress 查找我们的 MariaDB 容器所提供的环境变量:

git add –A .
git commit –m "Add wp-config.php and add credentials."
git push dokku master

等到你获得我们应用部署的新ip:port,然后在浏览器中输入信息。

现在你应该能够安装 WordPress 了。

输出如下:

使用 Dokku 设置 WordPress 应用程序

启动多个应用程序

要使用 Dokku 启动多个应用程序,只需重复以下简单的过程:

  1. 创建一个包含 WordPress 的本地 Git 仓库,并创建一个远程 Git 仓库。

  2. 使用 MariaDB 插件创建并链接数据库。

    编辑你的wp-config.php文件。

  3. 推送以进行部署。

你在使用 Git 命令添加remote时为你的应用设置的名称:

git remote add dokku dokku@ohdokku.com:wp1

该命令将创建指向 WordPress 网站的 URL(wp1.ohdokku.com)。你可以设置一个完整的自定义域名作为名称,如:git remote add dokku dokku@ohdokku.com:wp1.oskarhane.com,如果我将wp1.oskarhane.com指向我的服务器,这将有效。

向 Dokku 添加域名

我在将域名设置到 Dokku 时等了一下,因为这需要登录到 DNS 提供商并设置 DNS 记录,将域名指向我们的服务器。我们设置了 DNS 记录,将我们的域名指向服务器的 IP 地址,这样就可以通过在浏览器的地址栏输入我们的域名来访问服务器。

我通常使用 Amazon Route 53 来处理域名的 DNS,因为它们非常稳定且易于使用。对于低流量站点,每月费用大约为一美元。任何 DNS 提供商的设置方式都是一样的。你需要添加两条记录,一条是yourdomain.com,另一条是*.yourdomain.com

我们将要输入的记录是 A 记录,这意味着我们将域名指向特定的 IPv4 地址。生存时间TTL)值目前不重要,但它代表 TTL,并告诉所有其他请求该域名的 DNS 服务器可以缓存当前值的时间。

输出如下:

向 Dokku 添加域名

当然,你应该将 IP 地址更改为服务器的公共 IP。在为通配符子域设置 A 记录时,只需在顶部的输入框中输入***。

要查看你的 DNS 提供商是否可以解析你的域名,可以在终端中执行ping yourdomain.com。你会在命令行中看到解析后的 IP。如果你刚刚购买了域名,应该能够立即看到结果,但如果你使用域名已经有一段时间,旧的 TTL 值可能会稍微延迟效果。

如果你想等待 DNS 记录的设置(这在开发过程中很常见),你可以通过编辑/etc/hosts文件,在你的计算机上设置本地记录,如下所示的命令片段:

sudo nano /etc/hosts

#Add this line to the file
54.191.69.5 ohdokku.com
#Save and exit

这里要记住的一点是,不能为通配符子域名输入记录。如果你计划在子域上开发多个应用程序,则必须为每个子域输入一条记录。另外,完成后别忘了删除这些记录;如果你忘记了曾为使用过的域名设置记录,它会变得相当混乱(甚至有点有趣)。

在 Dokku 服务器上,创建一个名为/home/dokku/VHOST的文件,并在其中输入yourdomain.com

从现在开始创建的所有应用程序都会是该域名的子域,除非你为应用程序提供完整的域名。

更多 Dokku 相关说明

就像 Heroku 一样,Dokku 让开发人员轻松部署和推送代码。如果你直接从 Dokku 应用下载 WordPress 插件,重新启动 Dokku 应用时它会消失。建议保持一个本地副本,可以轻松启动,或者使用开发、测试和预发布服务器,在这些服务器上下载新的插件并推送到你的 Dokku 应用,以确保插件持久存在。

提示

使用这种基础设施时,图像和视频应该通过插件上传到像 Amazon 这样的服务。

你还必须让你的 WordPress 站点通过外部电子邮件服务商(如 Mandrill)发送电子邮件。像 WP Mail SMTP 这样的插件可以解决这个问题。

在 Dokku 上部署 WordPress 应用时,我们仍然需要执行一些手动步骤(例如,下载 WordPress 并编辑wp-config.php),但是创建自定义构建步骤以去除手动部分超出了本书的范围。

另一个选择是让 Composer 通过composer.json文件来处理 WordPress 的安装,但 WordPress 并不官方支持这种方式,并且需要进行一些黑科技操作,所以我将留给你自己决定是否使用。

注意

如果你想了解更多关于 composer 的信息,可以访问提供的链接wpackagist.org

总结

在本章中,我们通过向过程添加部署,最终创建了我们自己的 PaaS。到本章为止,我们所探讨的内容都是关于组织容器并引导传入流量,以便访客能够访问正确的容器。

使用 Dokku,我们不需要担心这些;我们只需要关注我们的代码。只要我们推送代码,Dokku 就会接管并执行正确的操作。Dokku 让这一切看起来非常简单,这也是我从手动创建和连接容器以及配置反向代理开始的原因——这样你就能理解 Dokku 的工作原理。

下一章将带我们走向前沿:现在正在开发的技术,能够让使用 Docker 的私人 PaaS 更进一步?

第八章. 接下来是什么?

到目前为止,我们的 PaaS 在单个主机上运行,如果需要扩展,这可能会成为一个问题。这个领域有很多进展,我挑选了一些项目将在本章中介绍。这些项目在成熟度上差异很大,有的已经可以投入生产使用,而有的仍处于原型阶段。在本章中,我们将涵盖以下主题:

  • 什么是十二要素应用?

  • Flynn

  • Deis

  • 火箭

  • 编排工具

什么是十二要素应用?

今天的许多应用程序,实际上都是您在网页浏览器中运行的 Web 应用程序。Gmail、Slack、Wunderlist、Trello 等都属于 Web 应用或软件即服务。

正是这种类型的应用程序适合在 PaaS 上运行。

十二要素应用是一种构建软件即服务应用的方法论,符合以下标准:

  • 使用声明性格式设置自动化,并最小化新加入项目的开发人员的时间和成本

  • 与底层操作系统有一个清晰的契约,提供在执行环境之间的最大可移植性

  • 适合部署在现代云平台上,避免了对服务器和系统管理的需求

  • 最小化开发与生产之间的差异,实现最大敏捷性的持续部署

  • 在不对工具、架构或开发实践做出重大改变的情况下进行扩展

十二要素定义如下:

  • 代码库(一个代码库在版本控制中追踪,多个部署):将您的代码放在版本控制系统中,例如 Git。

  • 依赖关系(显式声明并隔离依赖关系):这会列出您的应用程序依赖的所有库的所有版本,并将其集中在一个地方。

  • 配置(将配置存储在环境中):由于配置会在不同环境之间变化,例如数据库的用户名或密码,它不应成为代码的一部分。您可以将配置文件设置在环境变量中,并让您的应用程序在运行时读取它们。

  • 后台服务(将后台服务视为附加资源):这些包括所有后台服务,如邮件服务器、数据库和缓存系统等。这些将通过 URL 端点进行引用。这样,您的代码就不必关心后台服务是运行在同一台机器上,还是跨越全球。

  • 构建、发布、运行(严格区分构建和运行阶段):构建阶段创建包、资产和二进制文件。这是开发人员的工作。当您将一个包放置到服务器上时,您就准备进入运行阶段,启动应用程序并使其在服务器上可用。这个阶段应该尽可能简单,以便任何人都可以执行。

  • 进程(将应用程序作为一个或多个无状态进程执行):正如本书前面所述,您应该将应用程序数据与应用程序服务分开,也就是说,这使得服务成为无状态的。所有状态应该存储在共享存储和数据库中。

  • 端口绑定(通过端口绑定导出服务):例如,后台服务;你的服务应该可以通过 URL 端点访问。

  • 并发性(通过进程模型进行扩展):这保持每个进程都是一个独立的服务。这样,你可以仅扩展那些真正需要扩展的应用程序部分。

  • 可处置性(通过快速启动和优雅关闭最大化健壮性):这是针对应用程序启动的要求,启动应该快速,且你的应用程序应该能够自行从崩溃中恢复。

  • 开发/生产一致性(保持开发、暂存和生产环境尽可能相似):这使得你的开发环境和设置尽可能与生产环境和设置一致。Docker 在这一点上表现尤为出色。

  • 日志(将日志视为事件流):将应用程序的错误日志放置在一个集中位置,在出现新错误时会收到通知。

  • 管理进程(将管理/维护任务作为一次性进程运行):如果你在做管理任务,将它们运行在生产环境中的一台机器上,且该机器使用的是最新的代码库。你应该直接对数据库运行查询。

我鼓励你访问12factor.net以便阅读更多关于十二因子的内容。这是一个很好的阅读,你会理解为什么在以下项目中做出了一些设计决策。

Flynn

Flynn

创建 Dokku 的那位人,Jeff Lindsay,也共同创建了 Flynn。Flynn 就像一个超级 Dokku,除了其他功能外,它允许你在多个主机上运行 PaaS。

“Flynn 有两个特性:

一个组件的发行版,开箱即用地为公司提供一个合理的起点,用于构建运行其应用程序和服务的内部平台。

一组独立项目的横幅,这些项目一起构成了构建分布式系统的工具包或松散框架。

Flynn 既是一个整体,也是许多部分,这取决于对你最有用的是什么。共同的目标是将多年构建分布式系统的经验和最佳实践民主化。它是操作员和开发者之间的软件层,使他们的工作更轻松。

我尝试过几次使用 Flynn,但总是回到使用 Dokku,因为我发现 Dokku 更容易使用,而且我的客户不需要像多主机 PaaS 这样的额外功能。

URL:flynn.io

状态:由于处于测试阶段,因此不适合在生产环境中使用。

Deis

Deis

Deis 构建在一个轻量级的 Linux 发行版上,旨在运行容器,名为 CoreOS,并基于 Docker,利用那里提供的分布式服务,如 etcd。

“Deis 是一个轻量级的应用平台,将十二因子应用作为 Docker 容器部署并扩展,在一组 CoreOS 机器上运行。”

我发现 Deis 是一个非常有前景的项目,想要更深入地使用它。我目前仅仅了解了一些,但到目前为止看到的效果不错。

Deis 可以使用 Docker 部署在 Linux 上运行的任何语言或框架,还包括 Heroku 的构建包,支持 Ruby、Python、Node.js、Java、Clojure、Scala、Play、PHP、Perl、Dart 和 Go。

工作流程类似于 Heroku,你只需部署十二因子应用,也就是将应用状态保存在后端服务中。

有趣的事实:Deis 在财政上支持/资助 Dokku。

网址deis.io

状态:Deis 从版本 1.0 起已准备好用于生产环境。

Rocket

Rocket

CoreOS 一直是运行多主机 Docker PaaS 最流行的方式之一。他们做得非常出色,并构建了一些多主机 PaaS 工具,如 Deis,这些工具使用 CoreOS 的工具和服务来提供他们版本的 PaaS。

2014 年 12 月,CoreOS 团队决定宣布他们自己的容器运行时:Rocket。Rocket 是 Docker 的直接竞争对手。他们推出 Rocket 的原因是因为他们认为 Docker 已经偏离了最初的理念——运行可重用的标准容器。CoreOS 团队认为,Docker 通过增加越来越多的功能和服务,正在逐渐远离最初的理念。

“Rocket 是一个新的容器运行时,旨在可组合性、安全性和速度方面进行优化。今天我们在 GitHub 上发布了一个原型版本,开始收集社区反馈,并解释为什么我们要构建 Rocket。”

根据 CoreOS 团队的说法,他们将继续使 CoreOS 成为运行 Docker 的完美平台。我猜我们将看到未来会发生什么,但我希望他们能坚持自己的承诺。

网址github.com/coreos/rocket

状态:Rocket 目前处于非常早期的阶段,尚未准备好用于生产环境。

编排工具

我现在介绍的工具是帮助你集中精力编写代码,并为你提供一种轻松将应用部署到生产环境的方法。如果你更感兴趣的是一个编排工具——帮助你管理容器集群的工具——现在也有一些这样的工具可供选择。目前让我想起的工具有 Google 的 Kubernetes、Apache Mesos/Marathon、CoreOS Fleet,以及即将发布的 Docker Swarm。

总结

当你觉得是时候将你的 PaaS 从单一主机迁移到多个主机进行扩展时,这些工具正是你应该寻找的。我相信未来会出现一些有竞争力的工具,因为这是一个目前非常热门的领域。

posted @ 2025-06-29 10:40  绝不原创的飞龙  阅读(22)  评论(0)    收藏  举报