Docker-DevOps-入门指南-全-
Docker DevOps 入门指南(全)
原文:
annas-archive.org/md5/a074db026a63dfd63d361454222593a5译者:飞龙
前言
《DevOps 与 Docker》概述了容器化的强大功能以及这一创新对开发团队和日常运营的影响。我们还将了解什么是真正的 DevOps,涉及的原则,以及如何通过实施 Docker 工作流促进产品健康。Docker 是一个开源容器化工具,它简化了产品交付的流程,缩短了从商业构思到实施交付的时间。
本书将提供以下知识:
-
Docker 和 DevOps,以及它们为何以及如何集成
-
容器是什么,如何创建和管理它们
-
使用 Docker 扩展交付管道和多次部署
-
容器化应用的编排与交付
第一章,镜像与容器,展示了 Docker 如何改进 DevOps 工作流,并介绍了本书中将使用的基本 Docker 终端命令。我们将学习 Dockerfile 语法,以便构建镜像。我们将从镜像中运行容器,然后对镜像和 Docker Hub 进行版本管理,并将 Docker 镜像部署到 Docker Hub。
第二章,应用容器管理,介绍了 docker-compose 工具,并概述了多容器应用的配置。我们将管理多个容器并分发应用程序包。最后,我们将使用 docker-compose 进行网络配置。
第三章,编排与交付,为我们提供了 Docker Swarm 的概述。接着,我们将使用 Docker 引擎创建一个 Swarm,并管理 Swarm 中的服务和应用。最后,我们将根据实际应用场景测试服务的扩展和缩放。
硬件
本书要求以下最低硬件配置:
-
处理器:1.8 GHz 或更高(Core 2 Duo 及以上)
-
内存:最低 2GB RAM
-
硬盘:最低 10 GB
-
稳定的互联网连接(用于拉取和推送镜像)
软件
-
操作系统:Windows 8 或更高版本
-
浏览器:Google Chrome 或 Mozilla Firefox(安装了最新更新)
-
安装 Docker
本书适合的人群
本书适合希望采用 Docker 工作流来实现应用的一致性、速度和隔离性的开发人员、系统架构师、初级和中级站点可靠性工程师。我们将深入讲解 Docker,因此您需要具备关于 UNIX 概念(如 ssh、端口和日志)的基础知识。
约定
在本书中,您将看到多种文本样式,用于区分不同类型的信息。以下是这些样式的一些示例以及它们的含义解释。
文本中的代码词、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 账号将如下所示: "一旦创建了一个新的目录,访问该目录并创建一个名为 run.js 的文件。"
任何命令行输入或输出都如下所示:
docker pull
新术语 和 重要词汇 以粗体显示。您在屏幕上看到的单词,例如在菜单或对话框中,通常会以这样的形式出现在文本中:“点击下一步按钮将您带到下一个屏幕。”
注意
警告或重要说明会以框的形式出现,如下所示。
小贴士
小贴士和技巧如下所示。
读者反馈
我们欢迎读者的反馈。请告诉我们您对本书的看法——您喜欢或不喜欢的内容。读者反馈对我们非常重要,因为它帮助我们开发出您能真正从中受益的图书。
要发送一般反馈,只需通过电子邮件 <feedback@packtpub.com> 与我们联系,并在邮件主题中注明书名。
如果您对某个话题有专业知识,并且有兴趣写书或为书籍做贡献,请查看我们的作者指南,地址为 www.packtpub.com/authors。
客户支持
既然您已经成为 Packt 图书的骄傲拥有者,我们为您提供了一些帮助,帮助您充分利用您的购买。
下载示例代码
您可以从您的账户下载本书的示例代码文件,链接为 github.com/TrainingByPackt/Beginning-DevOps-with-Docker。如果您在其他地方购买了本书,可以访问 www.packtpub.com/support 并注册,以便将文件直接通过电子邮件发送给您。
您可以按照以下步骤下载代码文件:
-
使用您的电子邮件地址和密码登录或注册我们的网站。
-
将鼠标指针悬停在顶部的 SUPPORT 标签上。
-
点击 代码下载 和 勘误表。
-
在 搜索 框中输入书名。
-
选择您想要下载代码文件的书籍。
-
从下拉菜单中选择您购买此书的地点。
-
点击 代码下载。
您还可以通过点击 Packt 出版社网站上书籍网页中的代码文件按钮下载代码文件。您可以通过在搜索框中输入书名来访问此页面。请注意,您需要登录到您的 Packt 账户。
下载文件后,请确保使用以下最新版本的工具解压或提取文件夹:
-
WinRAR / 7-Zip for Windows
-
Zipeg / iZip / UnRarX for Mac
-
7-Zip / PeaZip for Linux
本书的代码包也托管在 GitHub 上,地址为 github.com/PacktPublishing/。我们还有其他来自我们丰富书籍和视频目录的代码包,您可以在 github.com/PacktPublishing/ 上查看它们!
安装
在开始本课程之前,我们将安装 Docker。您可以在这里找到安装步骤:
请访问下面的 Docker Toolbox 页面:docs.docker.com/toolbox/toolbox_install_windows/。
-
点击安装程序链接进行下载。
-
双击安装程序以安装 Docker Toolbox。
-
按“下一步”接受所有默认设置,然后进行安装。
勘误表
尽管我们已经尽最大努力确保内容的准确性,但错误还是可能发生。如果您在我们的书籍中发现错误——可能是文本或代码中的错误——我们将非常感激您能报告给我们。通过这样做,您可以帮助其他读者避免困惑,并帮助我们改进后续版本。如果您发现任何勘误,请通过访问www.packtpub.com/submit-errata进行报告,选择您的书籍,点击“勘误提交表单”链接,并填写勘误的详细信息。您的勘误一经验证,将被接受并上传至我们的网站,或添加到该书籍的勘误表部分中的现有勘误列表。
要查看先前提交的勘误表,请访问www.packtpub.com/books/content/support,并在搜索框中输入书名。所需信息将显示在勘误表部分下。
盗版
互联网盗版问题普遍存在于所有媒体上。在 Packt,我们非常重视保护我们的版权和许可证。如果您在互联网上发现我们作品的任何非法复制品,请立即提供该位置地址或网站名称,以便我们采取措施。
如发现疑似盗版资料,请通过<copyright@packtpub.com>联系我们,提供相关链接。
我们感谢您的帮助,保护我们的作者以及我们为您提供有价值内容的能力。
问题
如果您对本书的任何部分有疑问,可以通过<questions@packtpub.com>联系我们,我们将尽力解决问题。
第一章. 镜像与容器
本课程将涵盖有关容器化的基本概念,为我们后续构建的镜像和容器打下基础。我们还将了解 Docker 如何以及为何参与 DevOps 生态系统。在开始之前,我们将了解虚拟化与 Docker 中容器化的区别。
课程目标
本课程结束时,您将能够:
-
描述 Docker 如何改善 DevOps 工作流
-
解析 Dockerfile 语法
-
构建镜像
-
设置容器和镜像
-
设置本地动态环境
-
在 Docker 容器中运行应用程序
-
获取关于如何通过 Docker Hub 管理镜像的基本概述
-
将 Docker 镜像部署到 Docker Hub
虚拟化与容器化
这个框图概述了典型的虚拟机设置:

在虚拟机中,物理硬件被抽象化,因此我们可以在一台服务器上运行多个服务器。一个虚拟机监控程序(Hypervisor)帮助实现这一点。
虚拟机有时需要一些时间启动,并且在容量上比较昂贵(它们的大小可以达到 GB 级别),尽管它们相对于容器的最大优势是能够运行不同的 Linux 发行版,如 CentOS,而不仅仅是 Ubuntu:

在容器化中,只有应用层(即代码和依赖项打包的地方)被抽象化,这使得多个容器可以在相同的操作系统内核上运行,但在独立的用户空间中。
容器使用更少的空间并且启动速度快。这使得开发变得更容易,因为您可以快速删除和启动容器,而无需考虑服务器或开发人员工作空间的大小。
让我们通过简要概述 Docker 在 DevOps 工作流和 Docker 环境中的作用来开始本课程。
Docker 如何改善 DevOps 工作流
DevOps 是一种思维方式,一种文化,以及一种思考方式。最终目标是尽可能地提高和自动化流程。用通俗的话来说,DevOps 要求人们从最懒的角度思考,将大部分,甚至是所有流程尽可能自动化。
Docker 是一个开源容器化平台,改善了开发生命周期中软件交付的过程。请注意,它既不是现有平台的替代品,也不是组织希望它成为替代品。
Docker 抽象了配置管理的复杂性,类似于 Puppet。通过这种设置,shell 脚本变得不再必要。Docker 还可以在小型或大型部署中使用,从一个简单的 Hello World 应用到一个完整的生产服务器。
作为不同级别的开发者,无论是初学者还是专家,您可能已经使用过 Docker,而您可能并没有意识到。如果您已经设置了一个持续集成管道来在线运行测试,大多数服务器都使用 Docker 来构建和运行您的测试。
由于 Docker 的灵活性,它在技术社区中获得了广泛支持,因此许多组织开始为其服务运行容器。这些组织包括以下几家:
-
持续集成和持续交付平台,如 Circle CI、Travis CI 和 Codeship
-
云平台如 Amazon Web Services (AWS) 和 Google Cloud Platform (GCP) 允许开发者从容器中运行应用程序
-
思科和阿里巴巴集团也在容器中运行他们的一些服务
Docker 在 DevOps 工作流中的作用包括但不限于以下几点:
注意
Docker 在开发工作流中的应用案例示例。
统一需求意味着使用单一配置文件。Docker 将需求抽象化并限制为一个 Dockerfile 文件。
操作系统的抽象意味着我们不需要担心构建操作系统,因为已经存在预构建的镜像。
Velocity 必须定义一个 Dockerfile 并构建容器进行测试,或者直接使用已经构建好的镜像,而无需编写 Dockerfile。Docker 使开发团队能够避免因为“自动化工具 X”过于复杂而需要投入大量学习曲线的成本。
Docker 环境回顾
我们之前已经介绍了容器化的基本概念。让我再强调一下 Docker 为我们带来的替代工作流。
通常,我们有两个组成部分来构建一个有效的应用程序:项目代码库和配置脚本。代码库是应用程序代码,它由版本控制管理,并托管在 GitHub 等平台上。
配置脚本可以是一个简单的 shell 脚本,在主机机器上运行,该机器可以是从 Windows 工作站到云中的完全专用服务器的任何地方。
使用 Docker 并不会干扰项目代码库,而是在配置方面进行创新,改善工作流和交付速度。以下是 Docker 如何实现这一点的示例设置:

Dockerfile 取代了配置脚本。两者结合(项目代码和 Dockerfile)构成了 Docker 镜像。一个 Docker 镜像可以作为一个应用程序运行。这个从 Docker 镜像启动的运行应用程序被称为 Docker 容器。
Docker 容器允许我们在计算机上运行应用程序,且该环境是完全新的、一次性的。这意味着什么呢?
这意味着我们能够在计算机上声明并运行 Linux 或任何其他操作系统,然后在其中运行我们的应用程序。这也强调了我们可以无限次地构建和运行容器,而不会干扰我们计算机的配置。
通过这点,我向你介绍了四个关键字:镜像、容器、构建 和 运行。接下来我们将深入探讨 Docker CLI 的细节。
基本的 Docker 终端命令
打开命令提示符,检查 Docker 是否已安装在你的工作站中。输入 docker 命令,终端应显示以下内容:

这是 Docker 可用子命令的列表。要了解每个子命令的作用,在终端输入 docker-subcommand –help:

运行 docker info 并注意以下内容:
-
容器
-
镜像
-
服务器版本

这个命令显示系统范围的信息。服务器版本号在某些时候很重要,尤其是当新版本引入了不兼容的内容时。Docker 为其社区版提供了稳定版和边缘版。
现在我们将查看一些常用命令。
这个命令从 Docker Hub 搜索镜像:
docker search <term> (for example, docker search ubuntu)
Docker Hub 是默认的 Docker 注册表。Docker 注册表保存命名的 Docker 镜像。Docker Hub 基本上是“Docker 镜像的 GitHub”。之前,我们看过如何在不构建容器的情况下运行 Ubuntu 容器;这里存储并版本化了 Ubuntu 镜像:

“有私有 Docker 注册表,了解这一点很重要。”? Docker Hub 位于 hub.docker.com。一些镜像托管在 store.docker.com,但 Docker Store 包含官方镜像。然而,它主要关注 Docker 镜像商店的商业化方面,并提供工作流程。
注册页面如下所示:

登录页面如下所示:

从结果中,你可以通过星标数量了解用户如何评价该镜像。你还可以看出该镜像是否为官方镜像。这意味着该镜像是由注册表推荐的,在这种情况下是 Docker Hub。建议新用户使用官方镜像,因为它们有很好的文档,安全,推广最佳实践,并且适用于大多数使用场景。一旦你选定了一个镜像,你就需要将其保存在本地。
注意
确保你能够从 Docker Hub 搜索到至少一个镜像。镜像种类从操作系统到库都有,例如 Ubuntu、Node.js 和 Apache。
这个命令允许你从 Docker Hub 搜索:
docker search <term>
例如,docker search ubuntu。
这个命令从注册表拉取镜像到你的本地机器:
docker pull
例如,docker pull ubuntu。
一旦这个命令开始运行,你会注意到它正在使用默认标签:latest。在 Docker Hub 中,你可以看到标签的列表。对于 Ubuntu,它们列在这里:hub.docker.com/r/library/ubuntu/ 以及它们各自的 Dockerfiles:

从 Docker Hub 下载 Ubuntu 镜像配置文件:hub.docker.com/r/library/ubuntu/。
活动 1 — 使用 docker pull 命令
让你熟悉docker pull命令。
本次活动的目标是通过运行列出的命令,并在探索过程中通过操作构建的容器,帮助你牢固掌握docker-pull CLI,同时还可以查找其他命令的帮助。
-
Docker 是否正常运行?在终端或命令行应用中输入
docker。 -
该命令用于从 Docker Hub 拉取镜像
。docker pull
镜像种类从操作系统到库不等,例如 Ubuntu、Node.js 和 Apache。该命令允许你从 Docker Hub 拉取镜像:
例如,docker pull ubuntu。
该命令列出我们本地拥有的 Docker 镜像:
docker images
当我们运行该命令时,如果我们已经从 Docker Hub 拉取了镜像,我们将能看到一份镜像列表:

它们会根据仓库、标签、镜像 ID、创建日期和大小列出。如果镜像是从其他仓库获取的,仓库名将是镜像名称,除非它来自不同的注册中心。在这种情况下,你将看到没有http://和顶级域名(TLD)的 URL,如>registry.heroku.com/<image-name>,这是来自 Heroku 注册中心的镜像。
该命令将检查名为hello-world的镜像是否存在本地:
docker run <image>
例如,docker run hello-world:

如果镜像不在本地,它将默认从 Docker Hub 拉取,并作为容器运行。
该命令列出正在运行的容器:
docker ps
如果没有正在运行的容器,你应该看到一个空白屏幕,显示以下标题:

活动 2 — 分析 Docker CLI
确保通过在终端中输入docker来启动 Docker CLI。
你被要求展示迄今为止涵盖的命令。
让你熟悉 Docker CLI。本次活动的目标是通过运行列出的命令,并在探索过程中通过操作构建的容器,帮助你牢固掌握docker-compose CLI,同时还可以查找其他命令的帮助。目标是能够灵活使用 CLI,以便在实际场景中应用,如运行自动化脚本。
-
Docker 是否正常运行?在终端或命令行应用中输入
docker。 -
使用 CLI 搜索官方的 Apache 镜像,使用
docker search apache:![活动 2 — 分析 Docker CLI]()
-
尝试使用
docker pull apache拉取镜像。 -
使用
docker images确认镜像是否在本地可用。 -
奖励:使用
docker run apache将镜像作为容器运行。 -
奖励:使用
docker stop <container ID>停止容器。 -
奖励:使用
docker rm <container ID>删除容器和镜像。
Dockerfile 语法
每个 Docker 镜像都从Dockerfile开始。要创建一个应用程序或脚本的镜像,只需创建一个名为Dockerfile的文件。
注意
它没有扩展名,并且以大写字母 D 开头。
Dockerfile 是一个简单的文本文件,其中写入了构建容器的所有命令。Dockerfile 总是从基础镜像开始。它包含创建应用程序或运行脚本的步骤。
在构建之前,让我们快速浏览一些编写 Dockerfiles 的最佳实践。
一些最佳实践包括但不限于以下内容:
-
关注点分离:确保每个 Dockerfile 尽可能专注于一个目标。这将使其更容易在多个应用中重复使用。
-
避免不必要的安装:这将减少复杂性,并使镜像和容器足够紧凑。
-
重用已经构建的镜像:Docker Hub 上有许多已构建并版本化的镜像;因此,建议通过导入来重用这些镜像,而不是重新实现已有的镜像。
-
限制层数:最少的层数可以让构建更加紧凑或小巧。内存是构建镜像和容器时需要考虑的关键因素,因为这也会影响到镜像的使用者或客户端。
我们将从简单的 Python 和 JavaScript 脚本开始。选择这些语言是因为它们的流行性和易于演示的特点。
为 Python 和 JavaScript 示例编写 Dockerfiles
注意
对所选语言不需要任何先前的经验,因为这些语言旨在动态展示任何语言如何采用容器化。
Python
在我们开始之前,创建一个新的目录或文件夹;让我们将其作为工作区。
打开目录并运行docker search python。我们选择官方镜像:python。官方镜像在OFFICIAL列中标注为[OK]:

访问hub.docker.com或store.docker.com,搜索 python 以获取正确的标签,或者至少了解带有最新标签的 Python 镜像的版本。我们将在主题 D中更多讨论标签。
镜像标签应为类似3.x.x或3.x.x-rc的语法格式。
创建一个名为run.py的文件,并在第一行输入以下内容:
print("Hello Docker - PY")
在同一文件夹级别创建一个新文件,并命名为Dockerfile。
注意
我们没有为 Dockerfile 提供扩展名。
在Dockerfile中添加以下内容:
FROM python
ADD . .
RUN ls
CMD python run.py
如前所述,FROM命令指定基础镜像。
该命令也可以从继承的角度使用。这意味着,如果已有包含所需软件包的镜像,就不必在 Dockerfile 中再包括额外的软件包安装。
ADD 命令将源文件复制到镜像文件系统中的目标位置。这意味着脚本的内容将被复制到指定的目录。
在这种情况下,因为 run.py 和 Dockerfile 在同一层级,所以 run.py 被复制到我们构建的基础镜像文件系统的工作目录中。
RUN 命令是在构建镜像时执行的。这里运行 ls 只是为了让我们查看镜像文件系统的内容。
CMD 命令在基于我们将要使用这个 Dockerfile 创建的镜像运行容器时使用。这意味着在 Dockerfile 执行结束时,我们打算运行一个容器。
JavaScript
退出当前目录并创建一个新目录。这个目录将展示一个 Node 应用程序。
在脚本中添加以下行并保存:
console.log("Hello Docker - JS")
运行 docker search node —— 我们将选择官方镜像:node
记住,官方镜像在 OFFICIAL 列中有 [OK] 的标记:

请注意,Node 是基于 Google 高性能、开源的 JavaScript 引擎 V8 的 JavaScript 运行时。
访问 hub.docker.com 并搜索 Node,以获取正确的标签,或者至少了解带有最新标签的 Node 镜像的版本。
创建一个新的 Dockerfile 并添加以下内容:
这应该与脚本在同一文件级别。
FROM node
ADD . .
RUN ls
CMD node run.js
我们先覆盖这些内容。
活动 3 — 构建 Dockerfile
确保通过在终端中输入 docker 来运行 Docker CLI。
让你熟悉 Dockerfile 语法。本活动的目标是帮助理解和实践使用第三方镜像和容器。这有助于更大程度地理解容器化如何影响协作。通过构建已有的功能或资源,可以加快产品交付速度。
你被要求编写一个简单的 Dockerfile,打印 hello-world。
-
Docker 是否正在运行?在终端或命令行应用程序中输入
docker。 -
创建一个新目录并创建一个新的 Dockerfile。
-
编写一个包含以下步骤的 Dockerfile:
FROM ubuntu:xenial RUN apt-get install -y apt-transport-https curl software-properties-common python-software-properties RUN curl -fsSL https://apt.dockerproject.org/gpg | apt-key add RUN echo 'deb https://apt.dockerproject.org/repo ubuntu-xenial main' > /etc/apt/sources.list.d/docker.list RUN apt-get update RUN apt-get install -y python3-pip RUN apt-get install -y build-essential libssl-dev libffi-dev python-dev
构建镜像
在我们开始构建镜像之前,先理解一下上下文。镜像是一个独立的包,可以运行应用程序或分配的服务。镜像是通过 Dockerfile 构建的,Dockerfile 是定义镜像构建方式的模板。
容器被定义为镜像的运行时实例或版本。注意,这将在你的计算机或主机上作为一个完全隔离的环境运行,这使得它可以被丢弃并用于像测试这样的任务。
Dockerfile 准备好后,让我们进入 Python Dockerfile 目录并构建镜像。
docker build
构建镜像的命令如下:
docker build -t <image-name> <relative location of the Dockerfile>
-t 代表标签。<image-name> 可以包括特定标签,比如 latest。建议你按照这种方式操作:始终为镜像打标签。
Dockerfile 的相对位置 这里会用一个 点 (.) 来表示 Dockerfile 与代码在同一层级;也就是说,它位于项目的根目录。如果不是这种情况,你需要进入 Dockerfile 所在的目录。
例如,如果它在 Docker 文件夹中,你会使用 docker build -t <image-name> docker,或者如果它在比根目录更高的文件夹中,你会使用两个点。如果它在比根目录更高两层的文件夹中,则使用三个点代替一个点。
注意
终端上的输出与你在 Dockerfile 中写的步骤进行对比。你可能想要有两个或更多 Dockerfile 来配置不同的情况,例如,一个 Dockerfile 用于构建生产就绪的应用,另一个用于测试。不管你有什么理由,Docker 都有解决方案。
默认的 Dockerfile 名称是 Dockerfile。按照最佳实践,任何额外的 Dockerfile 会命名为 Dockerfile.<name>,比如,Dockerfile.dev。
要使用非默认的 Dockerfile 构建镜像,请运行以下命令:docker build -f Dockerfile.<name> -t <image-name> <Dockerfile 的相对位置>
注意
如果你在 Dockerfile 中进行了修改后重新构建镜像,但没有指定不同的标签,那么将会构建一个新的镜像,且之前的镜像会被命名为<none>。
docker build 命令有多个选项,你可以通过运行 docker build --help 来查看。使用类似 latest 的标签来标记镜像也用于版本控制。我们将在 主题 F 中详细讨论这个问题。
要构建镜像,请在 Python 工作空间中运行以下命令:
>$ docker build -t python-docker .
注意
这里的尾部点是语法中一个重要的部分:

注意
这里的尾部点是语法中一个重要的部分:

打开 JavaScript 目录并按如下方式构建 JavaScript 镜像:
>$ docker build -t js-docker .
运行这些命令将根据 Dockerfile 中的四行命令概述四个步骤。
运行 docker images 会列出你创建的两个镜像以及你之前拉取的任何其他镜像。
移除 Docker 镜像
docker rmi <image-id> 命令用于删除镜像。提醒你,镜像 ID 可以通过运行 docker images 命令找到。
要删除未标记的镜像(假设它们不相关),掌握 bash 脚本编程会派上用场。请使用以下命令:
docker rmi $(docker images | grep "^<none>" | awk "{print $3}")
这会简单地搜索在 docker images 命令的行中包含 <none> 的镜像,并返回位于第三列的镜像 ID:

活动 4 — 利用 Docker 镜像
确保 Docker CLI 正在运行,可以在终端中输入 docker 来检查。
帮助你熟悉从镜像运行容器。
你被要求从 活动 C 中编写的 Dockerfile 构建一个镜像。停止正在运行的容器,删除镜像,并使用不同的名称重新构建它。
-
Docker 是否正在运行?在终端或命令行应用中输入
docker。 -
打开 JavaScript 示例目录。
-
运行
docker build -t <选择一个名称>(观察步骤并记录结果)。 -
运行
docker run <你选择的名称>。 -
运行
docker stop <容器 ID>。 -
运行
docker rmi <在此添加镜像 ID>。 -
运行
docker build -t <选择新的名称>。 -
运行
docker ps(注意结果;旧镜像应该不存在)。
从镜像运行容器
还记得我们提到容器是从镜像构建的吗?命令 docker run <镜像> 会基于该镜像创建一个容器。可以说,容器是镜像的运行实例。另一个提醒是,这个镜像可以是本地镜像,也可以是从镜像库获取的。
继续运行已经创建的镜像 docker run python-docker 和 docker run js-docker:

你注意到什么了吗?容器运行时会将输出发送到终端的相应行。注意,在 Dockerfile 中以 CMD 开头的命令就是运行的命令:
docker build -t python-docker:test . and docker build -t js-docker:test .
然后,运行以下命令:
python-docker:test and docker run js-docker:test
注意
你在终端上不会看到任何输出。
这不是因为我们没有在容器启动后运行的 CMD 命令。对于从 Python 和 Node 构建的两个镜像,都有一个从基础镜像继承的 CMD 命令。
注意
创建的镜像始终继承自基础镜像。
我们运行的两个容器包含运行一次后退出的脚本。检查 docker ps 的结果时,你不会看到之前运行的两个容器。然而,运行 docker ps -a 会显示这些容器及其状态为已退出。
有一个命令列,显示了容器从哪个镜像继承的 CMD。
运行容器时,你可以按如下方式指定名称:
docker run --name <容器名称> <镜像名称>(例如,docker run --name py-docker-container python-docker):

我们之前提到过,你只需要保持相关的 Docker 镜像,而不是带有 <none> 标签的 Docker 镜像。
至于容器,你需要注意的是,你可以从一个镜像创建多个容器。docker rm <容器 ID> 是删除容器的命令。这个命令适用于已经退出的容器(即不再运行的容器)。
注意
对于仍在运行的容器,你需要执行以下操作之一:
在移除容器之前,先停止容器(docker stop <容器 ID>)。
强制移除容器(docker rm <容器 ID> -f)
如果你运行 docker ps,你不会看到任何容器,但如果我们运行 docker ps -a,你会发现容器被列出,且它们的命令列将显示继承的 CMD 命令:python3 和 node:

Python
Python 镜像的 Dockerfile 中的 CMD 是 python3。这意味着在容器中运行的是 python3 命令,容器执行完后会退出。
注意
记住这一点,你就可以在不安装 Python 的情况下运行 Python。
尝试运行这个命令:docker run -it python-docker:test(使用我们刚创建的镜像)。
我们进入容器中的交互式 bash shell。-it 指示 Docker 容器创建该 shell。该 shell 运行 python3,它是 Python 基础镜像中的 CMD:

在命令 docker run -it python-docker:test python3 run.py 中,python3 run.py 会像在容器内的终端中一样运行。请注意,run.py 在容器内,因此可以运行。如果使用 docker run -it python python3 run.py,则表示没有 run.py 脚本:


同样的概念也适用于 JavaScript,显示了这一概念在各类技术中的普适性。
docker run -it js-docker:test(我们刚创建的镜像)将运行一个节点(节点基础镜像中的 CMD):

docker run -it js-docker:test node run.js 将输出 Hello Docker - JS:

这证明了 Docker 镜像中的继承因素。
现在,恢复 Dockerfile 至其原始状态,并保留 CMD 命令 在最后一行。
镜像版本控制与 Docker Hub
还记得在D 主题中讨论过版本控制镜像吗?我们通过添加 latest 并使用一些数字标记我们的镜像,比如 3.x.x 或 3.x.x-rc,来实现版本控制。
在本主题中,我们将学习如何使用标签进行版本控制,并查看官方镜像过去是如何版本化的,从而学习最佳实践。
这里使用的命令如下:
docker build -t <image-name>:<tag> <relative location of the Dockerfile>
比如说,我们知道 Python 有几个版本:Python 3.6、3.5 等。Node.js 也有更多版本。如果你查看官方的 Node.js 页面,你会看到列表顶部显示以下内容:
9.1.0, 9.1, 9, latest (9.1/Dockerfile)(截至 2017 年 11 月):

这种版本控制系统叫做 semver:语义化版本控制。该版本号的格式为 MAJOR、MINOR、PATCH,按递增方式表示:
重大变更:对于不兼容向后的更改
次要版本:当你进行向后兼容的更改时
补丁版本:当你进行向后兼容的 bug 修复时
你会注意到镜像上附加有诸如 rc 等预发布和构建元数据标签。
在构建镜像时,特别是为了发布给公众或你的团队时,使用 semver 是最佳实践。
也就是说,我提倡你总是这样做,并把它作为个人座右铭:semver 是关键。它会消除在使用镜像时的歧义和混淆。
部署 Docker 镜像到 Docker Hub
每次我们运行 docker build 时,创建的镜像会在本地可用。通常,Dockerfile 和代码库是一起托管的;因此,在新机器上,用户需要使用 docker build 来创建 Docker 镜像。
通过 Docker Hub,任何开发者都有机会将 Docker 镜像托管到平台,并能在任何运行 Docker 的机器上拉取。这完成了两件事:
-
消除了重复运行
docker build的任务 -
提供了一种额外的共享应用程序的方法,与共享应用代码库链接和详细描述设置过程的 README 相比,这种方法更容易设置
docker login 是通过命令行界面连接 Docker Hub 的命令。你需要在 hub.docker.com 上拥有一个账户,并通过终端输入用户名和密码。
docker push <docker-hub-username/image-name[:tag]> 是将镜像推送到 Docker Hub 注册表的命令:

在 hub.docker.com 上简单搜索你的镜像,就能看到 Docker 镜像的输出。
在新机器上,简单的 docker pull <docker-hub-username/your-image-name> 命令即可在本地生成镜像的副本。
总结
在本课中,我们完成了以下任务:
-
回顾了 DevOps 工作流以及一些 Docker 的使用案例
-
讲解了 Dockerfile 语法
-
对应用程序镜像的构建和容器的运行有了一个高层次的理解
-
构建了多个镜像,为它们打上版本标签,并推送到 Docker Hub
第二章:应用容器管理。
在本节中,我们将把我们构建的一个容器扩展为多层次的架构。这将涉及将应用程序拆分为不同的逻辑部分。例如,我们可以让一个应用程序在 Docker 容器上运行,而将应用程序的数据存储在一个独立的数据库容器中;然而,这两个容器应该作为一个整体工作。为此,我们将使用 Docker 的工具来运行多容器应用程序。这个工具叫做 docker-compose。总之,docker-compose 是用来定义和运行多容器 Docker 应用程序的工具。
课程目标。
本节结束时,您将能够:
-
获取多容器应用程序设置的概览。
-
学习
docker-compose文件和命令行界面(CLI)。 -
管理多个容器和分布式应用程序包。
-
使用
docker-compose设置网络。 -
处理和调试不同的应用程序层。
docker-compose 工具
让我们通过了解什么是多容器架构、它为什么重要以及 Docker 如何通过 docker-compose 工具在这种场景中表现得如此出色来开始本节课。
我们最近了解了应用程序是如何工作的,包含其独立的元素:前端、后端和数据库。
要使用 Docker 运行这样的多层应用程序,您需要在不同的终端会话中运行以下命令来启动容器:
- docker run <front-end>
- docker run <back-end>
- docker run <database>
注意
您可以使用(-d)选项将 docker run 运行在分离模式,以防止我们在不同的会话中运行这三个命令,例如:docker run <front-end> -d。
也就是说,链接不同容器(网络设置)确实变得特别具有挑战性。
docker-compose 来拯救一天。我们可以从一个文件 docker-compose.yml 中定义和运行多个容器。在接下来的主题中,我们将进一步讨论这一点。首先,安装它。
安装 docker-compose。
如果您在 第一课,镜像和容器 中经历了 Docker 的安装,docker-compose 很可能已经与 Docker 一起安装。要确认这一点,请在终端中运行 docker-compose。
如果命令被识别,您应该会看到如下输出:

Windows 用户应该安装 Docker 的社区版,以便与 Docker 一起安装 docker-compose。Docker Toolbox 在其安装中包括了 docker-compose。
注意
有关 docker-compose 安装的更多步骤,请参阅文档:docs.docker.com/compose/install/。
说到这个,注意卸载它的各种方式。要卸载该程序:
进入 程序和功能。
找到 Docker,右键点击,然后选择 卸载。
多容器应用程序设置概览。
在上一课中,我们介绍了 Docker 和容器化。我们运行了示例 Python 和 JavaScript 脚本,演示了应用程序如何被容器化以及如何构建镜像。我们现在准备运行一个超越这些的应用程序。
在 Dockerfile 中,每一行描述一个层。Docker 中使用的联合文件系统允许不同目录透明叠加,形成一个统一的文件系统。基础层始终是一个镜像,你在其上构建。每添加一行命令,比如 RUN、CMD 等,都会为其添加一个层。层的优势在于,只要层未被修改,就不必重新构建该部分镜像。其次,镜像从 Docker 镜像仓库拉取时,是按层拉取的,因此可以减少在拉取和推送镜像时出现的连接中断等问题。
许多应用程序都建立在一个常见的结构下:前端,后端和数据库。让我们进一步分解并理解如何设置这些。
前端
当你打开一个 web 应用时,看到的页面属于前端的一部分。有时,前端包含控制器(逻辑端)和视图层(哑端)。布局和内容的样式(即 HTML 和 CSS)属于视图层。这里的内容由控制器管理。
控制器根据用户的操作和/或数据库更改影响视图层中呈现的内容。以 Twitter 应用为例:如果有人关注你,你的数据就发生了变化。控制器将捕捉此更改,并更新视图层显示新的关注者数量。
后端
你可能听说过模型-视图-控制器(MVC)。 模型位于应用程序的后端。以 Twitter 的早期示例为例,模型不涉及 HTML 或其布局。它处理应用程序的状态:关注者数量、正在关注的人数、推文、图片、视频等。
注意
这是后端层包含内容的摘要。后端主要处理应用程序的逻辑。这包括操作数据库的代码;也就是说,所有查询都来自后端。但是,请求来自前端。例如,当用户点击按钮时发生这种情况。
你可能也听说过 API 这个术语。API 是应用程序接口的缩写。它同样位于后端。API 暴露了应用程序的内部工作方式。
这意味着 API 也可以是应用程序的后端或逻辑层。
让我们使用 Twitter 的例子来说明。像发布推文和搜索推文这样的操作可以很容易地作为 API 存在,可以从任何前端应用程序调用,如果 API 被公开的话。
注意
Docker 和docker-compose CLI 实际上是 API 调用,例如在与外部资源或内容(如 Docker Hub)交互时。
数据库
数据库包含有组织的数据(信息),这些数据可以轻松访问、管理和更新。我们有基于文件的数据库和基于服务器的数据库。
基于服务器的数据库涉及一个服务器进程运行,接受请求并读取和写入数据库文件。数据库可以托管在云中,例如。
注意
基于服务器的数据库托管在虚拟主机上,通常是云平台,如 Google Cloud Platform 和 Amazon Web Services。例子包括 Amazon RDS 和 Google Cloud SQL for PostgreSQL。
从以下链接获取基于服务器的数据库:
简而言之,开发一直涉及构建应用程序层,而交付一直是个麻烦,因为云平台的价格和涉及的开发及运维(简称 DevOps)。
Docker 和docker-compose帮助我们将所有应用程序组件管理为一个单一的包,这样既便宜、又快捷、易于管理。docker-compose帮助我们通过一个文件以非常简单的定义来协调所有应用程序层。
在我们总结本概述时,了解开发人员随着时间的推移,已创造出不同的技术栈变体,以总结其应用程序的前端、后端和数据库结构非常重要。以下是它们及其含义的列表(在本课程中我们不会深入讨论):
-
PREN - PostgresDB, React, Express, Node.js
-
MEAN - MongoDB, Express, Angular, Node.js
-
VPEN - VueJS, PostgresDB, Express, Node.js
-
LAMP - Linux, Apache, MySQL, PHP
注意
了解应用程序按照这种方式构建,以便管理关注点的分离非常重要。
通过了解应用程序结构,我们可以进入docker-compose CLI 并将这些知识付诸实践。
使用 docker-compose
使用docker-compose需要三个步骤:
-
使用
Dockerfile将应用程序环境构建为镜像。 -
使用
docker-compose.yml文件定义您的应用程序运行所需的服务。 -
运行
docker-compose up以启动应用程序。
注意
docker-compose是一个命令行接口(CLI),就像 Docker CLI 一样。运行docker-compose会列出命令及其使用方法。
我们在上一节课中已经了解了镜像,因此步骤 1 已经完成。
某些docker-compose版本与某些 Docker 版本不兼容。
我们将花一些时间讨论步骤 2。
这是docker-compose文件:
- 它运行我们在上一节课中创建的两个镜像:

注意
请参考位于Code/Lesson-2/example-docker-compose.yml的完整代码。
访问goo.gl/11rwXV获取代码。
docker-compose 首次运行
-
创建一个新目录并命名为
py-js;如果你更喜欢,可以使用其他目录名称。 -
在目录中创建一个新文件,并命名为
docker-compose.yml。复制上面图片中的内容,或使用在 example-docker-compose.yml中共享的示例。 -
从该目录下运行命令
docker-compose up。
注意运行 js-docker 和 python-docker 的输出。这是因为我们已经从上一课构建了这两个本地镜像。
如果没有相关镜像,运行 docker-compose up 会导致错误,或者尝试从 Docker Hub 拉取镜像(如果它在线存在):

- 一个运行 WordPress 的
docker-compose.yml文件。WordPress 是一个免费的开源 内容管理系统 (CMS),基于 PHP 和 MySQL。
活动 1 — 使用 docker-compose 运行 WordPress
让你熟悉运行 docker-compose 命令。
你被要求使用 docker-compose 构建一个 WordPress 网站。
-
创建一个新目录并命名为
sandbox。 -
创建一个新文件并命名为
docker-compose.yml。添加wordpress-docker-compose.yml中的代码,或复制以下图示:![活动 1 — 使用 docker-compose 运行 WordPress]()
注意
查看 Code/Lesson-2/wordpress-docker-compose.yml 中的完整代码。
访问 goo.gl/t7UGvy 以获取代码。
注意
注意文件中的缩进。建议在缩进时使用相同数量的制表符和空格。
在 sandbox 目录下运行 docker-compose up:

注意
你会注意到,基于一个文件,我们已经启动了一个应用程序。这个例子完美展示了 docker-compose 的强大功能。
运行 docker ps。你将看到正在运行的容器:

打开你的浏览器并访问地址:http://0.0.0.0:8000/。我们将设置好的 WordPress 网站展示出来。
按照设置步骤操作,一瞬间,你就可以拥有一个准备好的 WordPress 网站。
docker-compose 文件:docker-compose.yml
docker-compose.yml 是一个 YAML 文件,它定义了 服务、网络 和 卷。
注意
服务是应用容器定义,包括与应用相关的所有组件,例如 数据库、前端 或 后端。定义服务时真正重要的是组件,它们包括网络、卷和环境变量。
任何 docker-compose.yml 文件的第一行定义了 docker-compose 文件格式的版本。
通过运行 docker -v,你可以知道正在运行的 Docker 版本,从而知道在文件的第一行使用哪个版本。
对于 docker-compose 文件格式 1.0,第一行是可选的。每个 docker-compose 文件都引入了新的配置或弃用了之前的配置。
我们将使用版本 3.3,程序应与版本 3.0 及以上兼容。
确保每个人都在运行版本 3 及以上,并且至少使用 Docker 1.13.0+版本。
接下来是服务。我们使用这个简化的骨架:

注意
注意缩进。
在上述示例中,我们有两个服务,分别是db和web。这两个服务缩进仅一次。
在定义服务后的下一行定义了要从中构建镜像的镜像或 Dockerfile。
第 4 行将指定db服务容器运行的镜像。我们之前提到过一些堆栈;db镜像可以是任何基于服务器的数据库。
注意
要确认您想使用的堆栈是否存在,请运行以下命令:docker search <image or name of your preferred stack>(例如,docker search mongo或docker search postgres)。
第 6 行解释了 Web 服务镜像将从docker-compose.yml相对位置(.)中的 Dockerfile 构建。
我们还可以在第 6 行定义 Dockerfile 的名称。docker-compose将会搜索列出名称的文件,例如,在docker-compose.yml中:
Line 5| web:build: Dockerfilevolumes:
第 7 行到第 10 行进一步定义了 Web 服务。
如我们在构建并运行 WordPress 时所使用的docker-compose.yml所示,有两个服务:db和wordpress。在docker ps的输出中,这些是容器名称:sandbox_wordpress_1和sandbox_db_1。
下划线前的第一个单词表示存放docker-compose.yml的目录名称。容器名称中的第二个单词是服务名称,正如在docker-compose.yml中定义的那样。
我们将在接下来的主题中深入讨论更多内容。
docker-compose CLI
一旦安装了docker-compose,我提到过,当您运行docker-compose时,您应该会看到一系列选项。运行docker-compose -v。
注意
这两个命令,docker-compose和docker-compose -v,是唯一可以在您当前打开的终端命令行或 Git bash 中运行的命令。
否则,docker-compose中的其他选项仅在docker-compose.yml文件存在的情况下运行。
让我们深入了解常用命令:docker-compose build。
该命令构建在模板docker-compose.yml中的docker-compose line: (build: .)所引用的镜像。
构建镜像也可以通过命令docker-compose up来实现。请注意,除非镜像尚未构建,或者有最近的更改影响到需要运行的容器,否则不会发生此操作。
注意
即使这两个服务是从 Docker 注册表中的镜像运行,而不是从目录中的 Dockerfile 构建,针对 WordPress 的示例此命令仍然有效。这将是拉取镜像,而不是构建,因为我们是从 Dockerfile 构建的。
该命令列出了在docker-compose.yml中配置的服务:
docker-compose config --services
该命令列出所创建容器使用的镜像:
docker-compose images
该命令列出服务的日志:
docker-compose logs
docker-compose logs <service>列出特定服务的日志,例如docker-compose logs db。
该命令列出基于docker-compose运行的容器:
docker-compose ps
请注意,在大多数情况下,docker-compose ps和docker ps的结果是不同的。在docker-compose上下文中没有运行的容器将不会被docker-compose ps命令显示出来。
该命令构建、创建、重建并运行服务:
docker-compose up
注意
当运行docker-compose up时,如果某个服务退出,整个命令会退出。
运行docker-compose up -d相当于在分离模式下运行docker-compose up。也就是说,命令会在后台运行。
活动 2 — 分析 docker-compose CLI
让你熟悉docker-compose CLI。
你被要求演示运行两个容器所带来的变化差异。
在 WordPress 的docker-compose.yml所在的目录下——在我这边是 sandbox——运行活动 B-1的命令,然后运行以下命令:
docker-compose up -d
docker-compose stop
docker-compose rm
docker-compose start
docker-compose up -d
docker-compose stop
docker-compose start
管理多个容器和分布式应用程序包
这是运行 Django 应用程序的docker-compose.yml文件。一个类似的应用可以在docker-compose文档的 Django 示例中找到。
从以下地址下载 Django 示例:docs.docker.com/compose/django/:

注意
请参考位于Code/Lesson-2/django-docker-compose.yml的完整代码。
访问goo.gl/H624J1以获取代码。
改进 Docker 工作流程
为了提供更多的上下文,说明docker-compose如何参与以及它如何改进 Docker 工作流程。
-
创建一个新的目录,并命名为
django_docker。 -
在
django-docker目录中,创建一个新的docker-compose.yml文件,并添加上述图中的信息,或使用提供的django-docker-compose.yml脚本。 -
创建一个新的 Dockerfile,并添加提供的 Dockerfile 脚本中的内容。
-
创建一个 requirements 文件;只需复制提供的
django-requirements.txt文件。 -
运行
docker-composeup 并观察日志。
注意,我们可以通过一个简单的命令docker-compose up来启动两个容器。
注意
不需要 Django 的先前经验;这只是为了基本的演示目的。Code/Lesson-2/django-requirements.txt。
Django Compose 文件解析
首先,这个文件包含多少个服务?是的,两个:db和web。服务db基于 Postgres 镜像,服务web是从同一目录下包含此docker-compose.yml文件的 Dockerfile 构建的。
如果没有docker-compose文件,db服务容器本应以以下方式运行:

这个命令翻译成如下内容:

在终端中打开另一个标签页或窗口并运行docker ps。你将看到正在运行的容器。
另一方面,根据示例,web服务容器将按照以下步骤运行:

第二个命令的分解格式如下:
docker run (the command)
-p shows the <workstation-port>:<container-port> (8000:8000)
-v: shows the <present-working-directory> `pwd` <working-directory-in-container> (:/django_docker)
<docker image> (django-web)
<command-to-run-when-the-container-starts> (python3 manage.py runserver 0.0.0.0.8000)
因此,上述命令翻译成以下内容:

使用docker-compose.yml的一个优点是,你不需要一次又一次地在终端中运行命令,而是可以通过一个命令来运行文件中包含的所有容器。
我们在上一课没有讲解卷和端口的内容。现在我会花时间帮助大家理解这个部分。
使用卷来持久化数据
卷用于持久化由 Docker 容器生成和使用的数据。
注意
卷会持久化对本地文件或脚本的任何更新。这会在容器端同步进行相应的更改。
在这种情况下,命令如下:

在 docker run 选项中,紧接着主命令后的是:
-v .:/django_docker
这在docker-compose.yml文件中。

注意
只要在docker-compose文件中定义了卷,当本地发生更改时(如文件更新),这些更改会自动同步到容器中的文件。

端口
Django 与其他 Web 服务器一样,运行在特定的端口上。用于构建 Django 镜像的 Dockerfile 中有一个类似这样的命令:EXPOSE 8000。当容器运行时,这个端口会保持开放,并可以连接。
在 Django 的 Dockerfile 中,我们将端口定义为8000,并在数字前加上了地址(0.0.0.0):

数字0.0.0.0定义了运行容器的主机地址。
注意
这个地址告诉docker-compose在我们的机器上(或者简而言之,本地主机)运行容器。如果我们跳过地址并仅暴露端口,设置可能会导致意外结果,比如空白页面。
请参考docker run选项中的以下行:
-p 8000:8000

在docker-compose.yml文件中的以下几行:

docker-compose端口格式将本地工作站的端口映射到容器端口。格式如下:
-p <workstation-port>:<container-port>
这使我们能够从本地机器访问映射自容器端口的 8000 端口。
在最后有一个选项depends_on,这是docker-compose.yml特有的。depends_on指定了容器启动的顺序,一旦我们运行docker-compose命令,它将按照此顺序启动。
在我们的例子中,depends_on选项位于 Web 服务下。这意味着 Web 服务容器依赖于db服务容器:

活动 3 — 运行 docker-compose 文件
让你熟悉docker-compose语法和命令。
你被要求构建并运行一个简单的 Python 应用程序,该应用程序从镜像josephmuli/flask-app暴露端口 5000。定义一个docker-compose文件,并将 Postgres 镜像作为数据库进行扩展。确保数据库与应用程序相关联。
-
我已经预先构建了一个名为
josephmuli/flask-app的镜像。在你的docker-compose.yml文件中扩展这个镜像。 -
确保编写版本 3 的
docker-compose并定义这两个服务。 -
在端口
5000上运行应用程序。 -
打开浏览器并检查监听端口。
使用 docker-compose 进行网络配置
默认情况下,docker-compose为你的应用程序设置一个单一网络,在该网络中每个容器都可以访问并发现其他容器。
网络根据所在目录的名称命名。因此,如果你的目录名为py_docker,当你运行docker-compose up时,创建的网络名称将是py_docker_default。
我们在上一部分提到过端口,当创建 WordPress 容器时。为了更好地解释网络,我们将使用用于启动 WordPress 应用程序的docker-compose.yml:

在这个文件中,我们有两个服务:db和wordpress。
在 WordPress 服务中,我们有ports选项将端口80映射到端口8000。难怪 WordPress 应用程序在浏览器中运行在0.0.0.0:8000上。
db服务中没有ports选项。然而,如果你访问docker hub mysql 页面,你会注意到端口3306已暴露。这是 MySQL 的标准端口。你可以从以下链接获取更多关于 MySQL 的信息:hub.docker.com/r/library/mysql。
注意
我们没有为数据库映射端口,因为我们不一定需要将端口映射到我们的计算机;相反,我们希望将 WordPress 应用程序映射到数据库,以便进行通信。
我们没有为db映射端口,因为我们不一定需要将端口映射到本地工作站或计算机。我们只需要它在容器环境中暴露出来,这样它就可以像第 23 行中所示那样从 Web 服务进行连接:WORDPRESS_DB_HOST: db:3306。
注意
在docker-compose文件中,这就是你如何将一个容器连接到另一个容器:
-
注意你想要连接的镜像暴露的端口。
-
引用连接到该容器的服务;在我们的例子中,
db服务由 WordPress 服务连接。由于我们将服务命名为
db,我们将这个连接称为db:3306。因此,格式为
<service>:<port由该service暴露>。
运行 WordPress 容器
以便提供更多关于容器如何连接、同步和通信的背景。
在 compose 文件中,你注意到重启选项了吗?该选项的可用值如下:
-
否
-
始终
-
错误重启
-
除非停止

如果没有指定,默认值是no。这意味着无论如何容器都不会重新启动。然而,这里的db服务已指定为 restart: always,所以容器会一直重启。
让我们看看 Django 示例,了解那里网络是如何工作的。这是docker-c ompose.yml:

你可能会发现,在 WordPress 站点中并没有立即显示网络部分。这是一个片段:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres',
'USER': 'postgres',
'HOST': 'db',
'PORT': 5432,
}
}
这里的问题是,我们是如何知道名称和用户是postgres,主机是db,端口是5432的?
这些是我们运行的postgres镜像和容器中设置的默认值。
为了更清晰,你可以查看官方 Postgres Docker 库中的这一行:
你可以从 GitHub 获取 Postgres Docker 示例: github.com/docker-library/postgres/blob/master/10/docker-entrypoint.sh#L101.

如前所述,主机是DB,因为服务名称是通过运行postgres镜像创建的db。
你可以从 GitHub 获取 Postgres Docker 示例: github.com/docker-library/postgres/blob/master/10/Dockerfile#L132:

间接地,它证明了为什么settings.py是按那种方式配置的。
总结
在本节中,我们做了以下事情:
-
讨论并展示了多容器设置
-
讲解了
docker-compose命令,以并行方式构建和运行多个容器 -
获得了关于网络容器的高级理解,以及如何将数据从本地机器持久化到容器中
-
通过 Docker Hub 构建并运行应用程序,甚至不需要设置它们。
第三章:编排与交付
创建 Docker 主机集群的主要动机是为了设计高可用性。大多数,甚至所有的集群和编排工具,如 Docker Swarm 和 Kubernetes,都通过创建主从关系来利用集群的优势。这确保了当某一节点在环境中宕机时,始终有其他节点可以回退使用。在将集群部署到云服务提供商时,有几种技术可以帮助确保环境的高可用性,例如 Consul,并且可以通过将主节点和从节点部署到不同的可用区,利用云平台的原生容错设计。
课程目标
本课程结束后,你将能够:
-
获取 Docker Swarm 模式的概览
-
使用 Docker 引擎创建 Docker 引擎的集群
-
管理服务和应用程序在集群中
-
向上或向下扩展服务以处理应用程序的更多请求
-
负载均衡 Docker Swarm 部署
-
保护 Docker 容器和部署
编排
在我们的本地环境中运行容器是简单的,不需要很多努力;但在云环境中,我们需要一种不同的心态和工具来帮助我们实现这一目标。我们的环境应该是高可用的、容错的,并且易于扩展。协调资源和/或容器的过程,形成一个统一的工作流,这就是编排。
首先,让我们熟悉一些在编排中常用的术语:
-
docker-engine:指我们目前在计算机上安装的 Docker 捆绑包或安装。 -
docker-machine:帮助我们在虚拟主机上安装 Docker 的工具 -
虚拟主机:这些是运行在物理主机下的虚拟服务器 -
docker-swarm:Docker 的集群工具 -
docker 主机:已设置或安装 Docker 的主机或服务器 -
节点:连接到集群的 Docker 主机 -
集群:一组 Docker 主机或节点 -
副本:实例的副本或多个副本 -
任务:在节点上运行的定义操作 -
服务:一组任务
注意
以下是本课程中最常见的术语:
-
docker-engine:在我们的计算机上运行 Docker。 -
docker-machine:帮助我们安装 Docker 的工具或命令行接口(CLI) -
虚拟主机:在物理主机上运行的主机或服务器。 -
docker-swarm:Docker 的集群工具 -
Docker 主机:任何运行 Docker 的服务器或主机 -
节点:指任何绑定到集群的主机。 -
集群:一组受管理和控制的主机。 -
副本:其他运行主机的复制,用于不同的任务 -
任务:如安装、升级或删除等操作。 -
服务:多个任务定义一个服务。
现在,既然我们至少了解了上述术语,我们准备使用docker-machine实现一个 Docker Swarm 编排流程。
Docker Swarm 概览
Docker Swarm 是一个为 Docker 容器设计的集群工具。它允许你将多个 Docker节点作为一个单一的虚拟系统来建立和管理 Docker 集群。这意味着我们可以在多个主机上运行 Docker。
我们通过一个管理器来控制 swarm 集群,管理器主要处理和控制容器。使用 swarm 管理器,你可以创建一个主管理实例和多个副本实例,以防主实例失败。这意味着在一个 swarm 中,你可以拥有多个管理器!
注意
swarm 是从一个管理节点创建的,其他 Docker 机器加入集群,作为工作节点或管理节点。
集群很重要,因为它创建了一个协作系统的组,提供冗余,从而创建一个容错环境。例如,如果一个或多个节点发生故障,Docker Swarm 会切换到另一个正常运行的节点。
Swarm 管理器执行以下角色:
-
接受
docker命令 -
对集群执行命令
-
支持高可用性;部署主实例和备份实例,以便在主实例发生故障时可以接管
Docker Swarm 使用调度来优化资源,并确保环境中的效率。它分配容器到最合适的节点。这意味着 Docker Swarm 会将容器分配到最健康的节点。
注意
请记住,节点是运行 Docker 的主机,而不是容器。
Swarm 可以配置为使用以下任何调度策略:
-
随机:将一个新容器部署到随机节点。
-
扩展:Swarm 将一个新容器部署到容器数量最少的节点。
-
Binpack:binpack 策略涉及将一个新容器部署到容器数量最多的节点。
你可以在以下地址下载 VirtualBox:www.virtualbox.org/wiki/Downloads:

注意
为了模拟 Docker Swarm 集群,我们需要在本地安装一个虚拟化管理程序(虚拟化管理程序类型 2 是一个安装在现有操作系统上的软件应用程序,用于管理虚拟机),在这种情况下是 VirtualBox,它将允许我们通过docker-machine创建多个本地运行 Docker 的主机,并将它们添加到 swarm 集群中。部署到云服务商时,通常通过其计算服务来实现,例如 AWS 上的 EC2。
对于 Windows 操作系统,选择你的操作系统发行版,你应该会立即开始下载。运行可执行文件并安装 VirtualBox。
使用 Docker 引擎创建 Swarm
在我们创建 swarm 之前,先快速了解一下docker-machine cli。在终端输入docker-machine应该会显示以下输出:

在此下方,我们列出了命令列表:

注意
记住当你需要澄清某些问题时,始终使用help选项,即docker-machine stop --help
为了创建我们的第一个 Docker Swarm 集群,我们将首先使用docker-machine来创建我们的管理节点和工作节点。
在创建第一台机器之前,快速回顾一下我们的目标:我们将拥有四台 docker-machine,一台管理节点和三台工作节点;它们都运行在 VirtualBox 上,因此有四台虚拟机。
创建 Docker 机器
此命令用于创建一个新的虚拟 Docker 主机:
docker-machine create --driver <driver> <machine_name>
这意味着我们的 Docker 主机将在VirtualBox上运行,但由docker-machine进行管理和控制。--driver选项指定用于创建机器的驱动程序。在本例中,我们的驱动程序是VirtualBox。
我们的命令将是docker-machine create --driver virtualbox manager1。
注意
我们在命令中需要指定驱动程序,因为它是我们主机的基础,这意味着我们的manager1机器将在 VirtualBox 上作为虚拟主机运行。有多个供应商提供的驱动程序,但这是演示目的下最好的选择。

列出已创建的机器
这个命令将列出当前主机上的所有 Docker 机器,并显示更多信息,比如机器的状态、驱动程序等:docker-machine ls

注意
列出我们的机器非常重要,因为它可以更新我们的机器状态。我们并不会收到错误通知,而错误有时可能积累成灾难性事件。在对机器进行操作之前,这可以提供一个简短的概览。可以通过docker-machine status命令进行更详细的检查。
工作节点创建
我们将遵循相同的过程为我们的 Swarm 集群创建三台工作节点,换句话说,运行docker-machine create --driver virtualbox <machine_name>三次,并在每次运行时将worker1、worker2和worker3作为<machine_name>的值:


最后,最后一个工作节点将显示如下:

完成后,运行docker-machine ls,如果创建成功,您将看到类似以下的输出:

注意
根据机器的用途命名可以帮助我们避免错误地调用到错误的主机。
初始化我们的 Swarm
现在我们的机器已经运行,接下来是创建我们的 Swarm。这将通过管理节点manager1来完成。以下是我们实现完整 Swarm 的步骤:
-
连接到管理节点。
-
将
manager1节点声明为管理节点,并宣传其地址。 -
获取节点加入 Swarm 的邀请地址。
我们将使用ssh进行连接。ssh是一种安全的网络协议,用于访问或连接主机或服务器。
注意
Docker 机器通过docker-machine cli进行控制。Docker Swarm 作为一个服务运行,将所有的 Docker 机器结合起来,并将它们统一在一个管理机器或节点下。这并不意味着 swarm 集群中的所有机器在任何方面都是平等或相似的,它们可能正在运行不同的服务或操作,例如数据库主机和 Web 服务器。Docker Swarm 的作用是帮助编排这些主机。
此命令用于获取一台或多台 Docker 机器的 IP 地址:
docker-machine ip <machine_names>
此命令用于获取一台或多台 Docker 机器的 IP 地址。<machine_name>是我们需要获取 IP 地址的机器的名称。在我们的例子中,我们将用它来获取manager1节点的 IP 地址,因为初始化 swarm 模式时需要它:

连接到一台机器
此命令用于通过SSH:登录到一台机器。
docker-machine ssh <machine_name>
成功连接到我们的manager1后,我们应该获得如下所示的输出:

注意
在云服务商上使用ssh 协议将需要通过用户名和密码或ssh 密钥进行身份验证和/或授权。由于这是一个演示,我们不会深入讨论这一部分。
初始化 Swarm 模式
这是初始化 swarm 模式的命令:
docker swarm init --advertise-addr <MANAGER_IP>
让我们在管理节点内部运行此命令来初始化一个 swarm。advertise-addr选项用于指定将被广播给 swarm 中其他成员的地址,以便进行 API 访问和网络连接。
在这种情况下,它的值是manager IP 地址,该值是我们之前通过运行docker-machine ip manager1获得的:
注意
我们之前提到过,Docker Swarm 是一个通过管理节点将所有机器连接和编排在一起的服务。为了实现这一点,Docker Swarm 允许我们通过管理节点的地址来广播集群,在docker swarm init命令中包含advertise-addr。

运行该命令的输出显示我们的节点现在是一个管理节点!
请注意,我们还拥有两个命令:一个应该允许我们邀请其他节点加入集群,另一个用于将另一个管理节点添加到集群中。
注意
在设计高可用性时,建议拥有多个管理节点,以便在主管理节点发生故障时,其他节点可以接管。
注意
确保保存输出中列出的两个命令,因为它们将在将其他主机添加到 swarm 时非常有用。
将 Worker 节点添加到我们的 Swarm
此命令用于添加 swarm 工作节点:
docker swarm join --token <provided_token> <manager_ip>:<port>
在我们将工作节点添加到 swarm 之前,我们需要通过ssh连接到它们。
我们通过运行docker-machine ssh <node_name>来实现这一点,然后运行我们从manager1 节点获得的邀请命令。
注意
docker-machine 命令可以从任何目录运行,并始终与已创建的机器配合使用。
首先,我们将使用 exit 命令退出管理节点:

然后,我们通过 ssh 连接到工作节点:

最后,我们将节点添加到集群中:

查看集群状态
我们使用此命令查看集群状态:
docker node ls
我们使用此命令查看集群状态。此命令在管理节点上运行,并显示我们集群中所有节点的状态和可用性。在管理节点上运行此命令时,输出类似于以下内容:

活动 1 — 向集群添加节点
确保您有一个管理节点和节点邀请命令。
让您熟悉 ssh 和集群管理。
您需要连接到至少两个节点并将它们添加到集群中。
-
Ssh连接到您的第一个节点:![活动 1 — 向集群添加节点]()
-
在节点上运行邀请命令以加入集群。记住,我们在首次初始化管理节点时获得了此命令:
![活动 1 — 向集群添加节点]()
-
退出节点,
ssh连接到另一个节点并运行命令:![活动 1 — 向集群添加节点]()
-
使用
Ssh连接到管理节点,通过docker node ls检查集群状态:![活动 1 — 向集群添加节点]()
在 Swarm 中管理服务和应用程序
现在我们的集群已准备好,是时候在集群上安排一些服务了。如前所述,管理节点的作用是接受 Docker 命令并将其应用到集群中。因此,我们将在管理节点上创建服务。
注意
此时,工作节点几乎不能做什么,因为它们完全受管理节点控制。
创建服务
此命令用于创建服务:
docker service create --replicas <count> -p <host_port>:<container_port> --name <service_name> <image_name>
我们像之前提到的那样在管理节点上运行此命令。我们将使用前一节中构建的 WordPress 示例。由于我们本地已有该镜像,因此无需从 Hub 拉取。
我们的副本数量将设置为三,因为我们目前有三个工作节点;通过运行 docker node ls 确认您的节点数量。
注意
我们没有创建副本数量;这引入了以下主题。-p <host_port>:<container_port> 将容器映射到我们计算机定义的端口,对应容器端口。我们不需要副本数量与节点数量相等。其他节点可以处理不同的应用层,例如数据库:

我们基于 WordPress 镜像创建了一个 web,并将主机端口 80 映射到容器端口 80。
列出服务
此命令用于查看当前正在运行的服务:
docker service ls
此命令用于查看当前正在运行的服务及更多信息,如副本、镜像、端口等。
从以下输出中,我们可以看到刚启动的服务及其相关信息:

服务状态
此命令用于检查我们的服务是否在运行:
docker service ps <service_name>
查看服务列表无法提供我们所需的所有信息,例如服务部署在哪些节点上。然而,我们可以知道服务是否在运行以及是否遇到任何错误。当我们在管理节点上运行此命令时,我们会看到以下输出:

注意
查看状态非常重要。在我们对节点进行升级或更新的情况下,运行docker ps可以告知我们节点的状态。在理想的 Docker Swarm 设置中,当某个节点宕机时,管理节点会将流量重新分配到可用节点,因此很难察觉到停机,除非有监控系统。在与节点交互之前,务必先运行此命令检查节点的状态。
我们如何知道网站是否正在运行?
我们可以通过在浏览器中打开任何一个工作节点的 IP 地址来验证 WordPress 是否正在运行:

以下是 WordPress 在浏览器中显示的截图:

注意
打开运行 WordPress Web 服务的任何 IP 地址,包括管理节点,将打开相同的地址。
活动 2 — 在 Swarm 上运行服务
确保你有一个正在运行的管理节点。
让你熟悉在 Swarm 中管理服务。
你被要求将一个新的postgres服务添加到 Swarm 中。
-
创建一个新节点并命名为
dbworker:docker-machine create --driver virtualbox dbworker![活动 2 — 在 Swarm 上运行服务]()
-
将新工作节点添加到 Swarm:
![活动 2 — 在 Swarm 上运行服务]()
-
创建一个新的数据库服务并命名为
db,使用 postgres 镜像作为基础:docker service create --replicas 1 --name db postgres以下是输出的截图:
![活动 2 — 在 Swarm 上运行服务]()
-
通过以下步骤验证
postgres是否在运行:-
将运行在
dbworker node中的postgres容器映射到你的计算机:docker run --name db -e POSTGRES_PASSWORD=postgres -d -p 5432:5432 postgres![活动 2 — 在 Swarm 上运行服务]()
-
运行
docker ps列出正在运行的容器;这应该显示我们的postgres容器,并且状态应为UP:![活动 2 — 在 Swarm 上运行服务]()
-
通过以下方式退出并停止容器:
![活动 2 — 在 Swarm 上运行服务]()
-
扩展服务
随着请求量的增加或减少,需要扩展基础设施。我们最近使用节点副本运行了我们所安装的同一个 WordPress。
注意
这是一个非常基础的生产级设置示例。理想情况下,我们需要更多的管理节点和副本,但由于我们正在运行一个演示,这就足够了。
扩展涉及根据应用程序的流量增加和减少资源。
扩展我们的数据库服务
我们将以数据库服务为例,展示如何扩展服务。在实际场景中,像 Google Cloud Platform 和 Amazon Web Services 这样的云服务可能会定义自动扩展服务,创建多个副本并通过名为负载均衡的服务在副本之间分配流量。我们将在下一个活动中深入探讨这一点。首先,我们从基础开始,了解扩展是如何工作的。扩展数据库的命令格式如下:
docker service scale <service_name>=<count>
要扩展服务,传入我们创建服务时提供的服务名称和希望扩展到的副本数。
注意
--detach=false 让我们查看复制进度。命令是 docker service scale <service_name>=<count>:

从上面的输出中,我们可以看到我们的db服务已被复制。现在我们在dbworker节点上运行了两个数据库服务。
Swarm 如何知道在哪里调度服务?
我们之前已经讨论过调度模式,包括以下几种:
-
随机
-
扩展
-
Binpack
Docker Swarm 的默认调度策略是spread,它将新服务分配给资源最少的节点。
注意
如果 Swarm 中没有额外的未分配节点,则您想要扩展的服务将会在当前运行的节点上复制。
Swarm 管理器将使用扩展策略,并根据资源分配。
然后,我们可以使用 docker service ls 命令验证操作是否成功,我们可以看到副本数为二:

缩容与扩容非常相似,唯一的区别是我们传递的副本数比之前少。从以下输出中,我们将副本数缩减为一个,并验证副本数确实为一:

Swarm 如何在副本之间负载均衡请求?
负载均衡器有助于处理和管理应用程序中的请求。在一个应用程序处理大量请求的情况下(例如在 5 分钟内处理 1,000 个请求),我们需要在应用程序上(特别是逻辑部分,即后台)配置多个副本和负载均衡器。负载均衡器有助于分配请求,防止实例过载,从而避免停机。
当部署到像Google Cloud Platform或Amazon Web Services这样的云平台时,您可以使用外部负载均衡器将请求路由到您的 Swarm 主机。
Docker Swarm 包含一个内置的路由服务,使得即使某个节点上没有服务运行,群集中的每个节点也能接受对公开端口的传入连接。默认情况下,postgres 服务使用端口 5432。
活动 3 — 在 Swarm 中扩展服务
确保你的 Swarm 至少包含一个管理节点、两个服务和三个工作节点。
让你熟悉扩展服务和复制节点的操作。
你被要求将 Web 服务扩展到四个副本,并将数据库服务扩展到两个副本。
-
创建了三个新的工作节点,其中两个用于 Web 服务,一个用于数据库服务。
-
连接到管理节点并扩展 Web 和数据库服务。
-
使用 docker service ls 确认服务副本数量;最终结果应该如下:
-
WordPress Web 服务应该有两个副本
-
Postgres 数据库服务应该有四个副本
-
总结
本课中,我们完成了以下内容:
-
讨论了编排,并提到了一些示例工具
-
讨论了集群及其重要性,尤其是在生产环境中
-
通过在 VirtualBox 上运行 Docker Machines 学习了虚拟主机
-
详细了解了 Docker Swarm 以及如何创建和管理一个节点集群
-
介绍了示例服务,包括在我们的集群中运行的 Wordpress
-
获得了关于使用
docker-machine cli的高级理解 -
讨论了负载均衡以及 Docker Swarm 如何管理这一过程
恭喜你顺利完成!以下是我们通过课程所获得的知识回顾。
本书中,我们涵盖了以下内容:
-
讨论了 DevOps 以及 Docker 如何促进工作流
-
理解了如何在 Dockerfile 中创建应用程序模板
-
构建了镜像和容器并推送到 Docker Hub
-
通过
docker-compose管理容器 -
学习了如何通过 Docker Swarm 协调我们的应用程序














浙公网安备 33010602011771号