Skip to content

Latest commit

 

History

History
1044 lines (719 loc) · 50.3 KB

File metadata and controls

1044 lines (719 loc) · 50.3 KB

十、使用 Docker 创建 Scraper 微服务

在本章中,我们将介绍:

  • 安装 Docker
  • 从 Docker Hub 安装 RabbitMQ 容器
  • 运行 Docker 容器(RabbitMQ)
  • 停止并删除容器和图像
  • 创建 API 容器
  • 使用 Nameko 创建通用微服务
  • 创建爬取微服务
  • 创建刮刀容器
  • 创建后端(ElasticCache)容器
  • 使用 Docker Compose 组合和运行刮板容器

介绍

在本章中,我们将学习如何将我们的刮板装箱,通过开始将其打包以用于真正的、现代的、支持云的操作,为现实世界做好准备。这将涉及将 scraper 的不同元素(API、scraper、后端存储)打包为 Docker 容器,可以在本地或云中运行。我们还将研究如何将 scraper 实现为一个可以独立扩展的微服务。

大部分重点将放在使用 Docker 创建我们的集装箱刮板上。Docker 为我们提供了一种便捷的方式,将 scraper 的各种组件打包为服务(API、scraper 本身以及其他后端,如 Elasticsearch 和 RabbitMQ)。通过使用 Docker 对这些组件进行容器化,我们可以轻松地在本地运行容器,协调组成服务的不同容器,还可以方便地发布到 Docker Hub。然后,我们可以轻松地将它们部署到云提供商,以便在云中创建刮板。

Docker(以及一般的容器)的一个优点是,我们可以轻松地安装预打包的容器,而无需为应用程序获取安装程序,并且可以处理所有配置方面的麻烦。然后,我们还可以将自己编写的软件打包到一个容器中,并运行该容器,而无需处理所有这些细节。此外,我们还可以发布到私有或公共存储库以共享我们的软件。

Docker 真正伟大的地方在于,容器在很大程度上是独立于平台的。任何基于 Linux 的容器都可以在任何操作系统上运行,包括 Windows(它在下面使用 VirtualBox 来虚拟化 Linux,并且对 Windows 用户来说基本上是透明的)。因此,一个好处是,任何基于 Linux 的 Docker 容器都可以在任何 Docker 支持的操作系统上运行。不再需要为应用程序创建多个操作系统版本!

所以,让我们去学习一个小码头工人,把我们的刮板组件放入容器中。

安装 Docker

在本食谱中,我们将介绍如何安装 Docker 并验证它是否正在运行。

准备

Docker 在 Linux、macOS 和 Windows 上受支持,因此它涵盖了主要平台。Docker 的安装过程因您使用的操作系统而异,甚至在不同的 Linux 发行版中也有所不同。

Docker 网站上有关于安装过程的良好文档,因此本教程将快速介绍 macOS 上安装的要点。安装完成后,Docker 的用户体验(至少在 CLI 中)是相同的。

For reference, the main page for installation instructions for Docker is found at: https://docs.docker.com/engine/installation/

怎么做

我们将按照以下步骤进行配方:

  1. 我们将使用称为 Docker Community Edition 的 Docker 变体,并在 macOS 上完成安装过程。在 macOS 的下载页面上,您将看到以下部分。点击稳定频道的下载,除非你感到勇敢并想使用边缘频道。

The docker download page

  1. 这将下载一个Docker.dmg文件。打开 DMG,您将看到以下窗口:

The Docker for Mac installer window

  1. 摩比鲸鱼拖到您的应用程序文件夹中。然后打开Docker.app。系统将要求您对安装进行身份验证,因此请输入密码,安装将完成。完成后,您将在状态栏中看到 Moby:

The Moby toolbar icon

  1. 单击 Moby 可以获得许多配置设置、状态和信息。我们将主要使用命令行工具。要从命令行验证工作是否正常,请打开终端并输入命令 docker info。Docker 将向您提供有关其配置和状态的一些信息。

从 Docker Hub 安装 RabbitMQ 容器

可以从许多容器存储库中获取预构建的容器。Docker 预先配置了与 Docker Hub 的连接,许多软件供应商和爱好者在 Docker Hub 中发布具有一个或多个配置的容器。

在这个配方中,我们将安装 RabbitMQ,它将被我们在另一个配方中使用的另一个工具 Nameko 使用,作为我们的抓取微服务的消息传递总线。

准备

通常,RabbitMQ 的安装是一个相当简单的过程,但它确实需要几个安装程序:一个用于 Erlang,然后一个用于 RabbitMQ 本身。如果需要诸如基于 web 的管理 GUI 之类的管理工具,那么这是又一个步骤(尽管这是一个相当小的步骤)。通过使用 Docker,我们可以简单地获得具有所有这些预配置的容器。我们去做吧。

怎么做

我们按照以下方法进行配方:

  1. 可以使用docker pull命令获取容器,该命令将检查容器是否安装在本地,如果没有,请去帮我们取。尝试从命令行执行命令,包括--help flag. 您将获得以下信息,通知您至少还需要一个参数:容器的名称,可能还有标记:
$ docker pull --help

Usage: docker pull [OPTIONS] NAME[:TAG|@DIGEST]

Pull an image or a repository from a registry

Options:
  -a, --all-tags Download all tagged images in the repository
      --disable-content-trust Skip image verification (default true)
      --help Print usage
  1. 我们将拉动rabbitmq:3-management集装箱。冒号前面的部分是容器名称,第二部分是标记。标记通常表示容器的版本或特定配置。在本例中,我们希望获得标签为 3-management 的 RabbitMQ 容器。此标记表示我们希望容器版本为 RabbitMQ 的版本 3,并且安装了管理工具。

在我们这样做之前,你可能会想这是从哪里来的。它来自 Docker Hub(hub.docker.com),来自 RabbitMQ 存储库。此存储库的页面位于https://hub.docker.com/_/rabbitmq/ ,并将如下所示:

Page for RabbitMQ repository Note the section showing tags, and that it has the 3-management tag. If you scroll down, you will also see a lot more information about the container and tags, and what they comprise.

  1. 现在让我们拉这个容器。从终端发出以下命令:
$docker pull rabbitmq:3-management
  1. Docker 将前往 Docker Hub 并开始下载。您将看到这一点,其输出类似于以下内容,可能会运行几分钟,具体取决于您的下载速度:
3-management: Pulling from library/rabbitmq
e7bb522d92ff: Pull complete 
ad90649c4d84: Pull complete 
5a318b914d6c: Pull complete 
cedd60f70052: Pull complete 
f4ec28761801: Pull complete 
b8fa44aa9074: Pull complete 
e3b16d5314a0: Pull complete 
7d93dd9659c8: Pull complete 
356c2fc6e036: Pull complete 
3f52408394ed: Pull complete 
7c89a0fb0219: Pull complete 
1e37a15bd7aa: Pull complete 
9313c22c63d5: Pull complete 
c21bcdaa555d: Pull complete 
Digest: sha256:c7466443efc28846bb0829d0f212c1c32e2b03409996cee38be4402726c56a26 
Status: Downloaded newer image for rabbitmq:3-management 

祝贺如果这是您第一次使用 Docker,那么您已经下载了第一个容器映像。您可以使用 docker images 命令验证它是否已下载和安装。

$ docker images 
REPOSITORY TAG IMAGE    ID           CREATED     SIZE 
rabbitmq   3-management 6cb6e2f951a8 10 days ago 151MB

运行 Docker 容器(RabbitMQ)

在这个配方中,我们学习如何运行 docker 映像,从而制作容器

准备

我们将启动在上一个配方中下载的 RabbitMQ 容器映像。这个过程代表了运行了多少个容器,因此它是一个很好的示例。

怎么做

我们按照以下方法进行配方:

  1. 到目前为止,我们下载的是一个可以运行以创建实际容器的图像。容器是图像的实际实例化,具有配置容器中软件所需的特定参数。我们通过使用 docker run 运行映像并传递映像名称/标记以及运行映像所需的任何其他参数来运行容器(这些参数特定于映像,通常可以在映像的 docker Hub 页面上找到)。 使用此映像运行 RabbitMQ 所需的具体命令如下:
$ docker run -d -p 15672:15672 -p 5672:5672 rabbitmq:3-management
094a138383764f487e5ad0dab45ff64c08fe8019e5b0da79cfb1c36abec69cc8
  1. docker run告诉 Docker 在容器中运行图像。我们要运行的映像位于语句的末尾:rabbitmq:3-management-d选项告诉 Docker 运行分离的容器,这意味着容器的输出不会路由到终端。这允许我们保留对终端的控制。-p选项将主机端口映射到容器端口。RabbitMQ 使用端口 5672 执行实际命令,使用端口 15672 执行 web UI。这会将实际操作系统上的相同端口映射到容器中运行的软件所使用的端口。

The big hexidecimal value output is an identifier of the container. The first portion, 094a13838376, is the container ID created by Docker (this will be different for every container that is started).

  1. 我们可以使用 docker ps 检查哪些容器正在运行,这将为我们提供每个容器的进程状态:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
094a13838376 rabbitmq:3-management "docker-entrypoint..." 5 minutes ago Up 5 minutes 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp dreamy_easley

We can see the container ID and other information such as which image it is based on, how long it has been up, which ports the container exposes, the port mappings we defined, and a friendly name made up by Docker for us to refer to the container.

  1. 检查是否正在运行的真正方法是打开浏览器并导航到localhost:15672,RabbitMQ 管理 UI URL:

The RabbitMQ Admin UI login page

  1. 此容器的默认用户名和密码为 guest:guest。输入这些值,您将看到管理 UI:

The management UI

还有更多。。。

这实际上是我们将在 RabbitMQ 方面取得的进展。在后面的配方中,我们将使用 Nameko Python 微服务框架,该框架将在我们不知情的情况下透明地使用 RabbitMQ。我们首先需要确保它已安装并运行。

创建和运行 Elasticsearch 容器

当我们查看容器图像和启动容器时,让我们运行 Elasticsearch 容器。

怎么做

和大多数 Docker 一样,Elasticsearch 容器有很多不同的版本。我们将使用 Elastic 自己的 Docker 存储库中提供的官方 Elasticsearch 图像:

  1. 要安装映像,请输入以下内容:
$docker pull docker.elastic.co/elasticsearch/elasticsearch:6.1.1

Note that we are using another way of specifying the image to pull. Since this is on Elastic's Docker repository, we include the qualified name that includes the URL to the container image instead of just the image name. The :6.1.1 is the tag and specifies a specific version of that image.

  1. 在处理过程中,您将看到一些输出,显示下载过程。当它完成时,您将有几行代码让您知道它已完成:
Digest: sha256:9e6c7d3c370a17736c67b2ac503751702e35a1336724741d00ed9b3d00434fcb 
Status: Downloaded newer image for docker.elastic.co/elasticsearch/elasticsearch:6.1.1
  1. 现在,让我们检查图像是否可供 Docker 使用:
$ docker images 
REPOSITORY TAG IMAGE ID CREATED SIZE 
rabbitmq 3-management 6cb6e2f951a8 12 days ago 151MB docker.elastic.co/elasticsearch/elasticsearch 6.1.1 06f0d8328d66 2 weeks ago 539MB
  1. 现在,我们可以使用以下 Docker 命令运行 Elasticsearch:
docker run -e ELASTIC_PASSWORD=MagicWord -p 9200:9200 -p 9300:9300 docker.elastic.co/elasticsearch/elasticsearch:6.1.1
  1. 环境变量ELASTIC_PASSWORD传入密码,两个端口将主机端口映射到容器中公开的 Elasticsearch 端口。
  2. 接下来,检查容器是否在 Docker 中运行:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
308a02f0e1a5 docker.elastic.co/elasticsearch/elasticsearch:6.1.1 "/usr/local/bin/do..." 7 seconds ago Up 6 seconds 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp romantic_kowalevski
094a13838376 rabbitmq:3-management "docker-entrypoint..." 47 hours ago Up 47 hours 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp dreamy_easley
  1. 最后,执行下面的卷曲。如果 Elasticsearch 正在运行,您将收到You Know, for Search消息:
$ curl localhost:9200
{
 "name" : "8LaZfMY",
 "cluster_name" : "docker-cluster",
 "cluster_uuid" : "CFgPERC8TMm5KaBAvuumvg",
 "version" : {
 "number" : "6.1.1",
 "build_hash" : "bd92e7f",
 "build_date" : "2017-12-17T20:23:25.338Z",
 "build_snapshot" : false,
 "lucene_version" : "7.1.0",
 "minimum_wire_compatibility_version" : "5.6.0",
 "minimum_index_compatibility_version" : "5.0.0"
 },
 "tagline" : "You Know, for Search"
}

停止/重新启动容器并删除映像

让我们看一下如何停止并移除容器,以及它的图像。

怎么做

我们按照以下方法进行配方:

  1. 运行容器的第一个查询 Docker:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
308a02f0e1a5 docker.elastic.co/elasticsearch/elasticsearch:6.1.1 "/usr/local/bin/do..." 7 seconds ago Up 6 seconds 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp romantic_kowalevski
094a13838376 rabbitmq:3-management "docker-entrypoint..." 47 hours ago Up 47 hours 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp dreamy_easley
  1. 让我们停止 Elasticsearch 容器。要停止容器,我们使用docker stop <container-id>。Elasticsearch 的容器 ID 为 308a02f0e1a5。以下命令停止容器
$ docker stop 30
30

要确认容器已停止,Docker 将回显您告诉它停止的容器 ID

Note that I didn't have to enter the full container ID and only entered 30. You only have to enter the first digits of the container ID until what you have entered is unique among all containers. It's a nice shortcut!.

  1. 检查正在运行的容器状态,Docker 仅报告其他容器:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
094a13838376 rabbitmq:3-management "docker-entrypoint..." 2 days ago Up 2 days 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp dreamy_easley
  1. 容器未运行,但也未被删除。让我们看看使用docker ps -a命令:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
308a02f0e1a5 docker.elastic.co/elasticsearch/elasticsearch:6.1.1 "/usr/local/bin/do..." 11 minutes ago Exited (143) 5 minutes ago romantic_kowalevski
548fc19e8b8d docker.elastic.co/elasticsearch/elasticsearch:6.1.1 "/usr/local/bin/do..." 12 minutes ago Exited (130) 12 minutes ago competent_keller
15c83ca72108 docker.elastic.co/elasticsearch/elasticsearch:6.1.1 "/usr/local/bin/do..." 15 minutes ago Exited (130) 14 minutes ago peaceful_jennings
3191f204c661 docker.elastic.co/elasticsearch/elasticsearch:6.1.1 "/usr/local/bin/do..." 18 minutes ago Exited (130) 16 minutes ago thirsty_hermann
b44f1da7613f docker.elastic.co/elasticsearch/elasticsearch:6.1.1 "/usr/local/bin/do..." 25 minutes ago Exited (130) 19 minutes ago

This lists all containers currently on the system. I actually truncated my listing by quite a bit as I have a lot of these!

  1. 我们可以使用docker restart重新启动 Elasticsearch 容器:
$ docker restart 30
30
  1. 如果您检查docker ps,您将看到集装箱再次运行。

This is important as this container is storing the Elasticsearch data within the file system of the container. By stopping and restarting, this data is not lost. So, you can stop to reclaim the resources (CPU and memory) used by the container, and then restart without loss at a later time.

  1. 容器在运行或停止时会占用磁盘空间。可以移除容器以回收磁盘空间。这可以使用docker container rm <container-id>完成,但是容器只有在未运行时才能移除。让我们尝试移除正在运行的容器:
$ docker container rm 30
Error response from daemon: You cannot remove a running container 308a02f0e1a52fe8051d1d98fa19f8ac01ff52ec66737029caa07a8358740bce. Stop the container before attempting removal or force remove
  1. 我们收到了关于集装箱运行的警告。我们可以用旗子强迫它,但最好先阻止它。停止可确保内部的应用程序干净地关闭:
$ docker stop 30
30
$ docker rm 30
30
  1. 现在,如果返回 dockerps -a,Elasticsearch 容器不再在列表中,容器的磁盘空间将被回收

Note that we have now lost any data that was stored in that container! It's beyond the scope of this book, but most containers can be told to store data on the host's file system, and therefore we don't lost data.

  1. 容器的磁盘空间已删除,但容器的映像仍在磁盘上。如果我们想再做一个容器,那就好了。但是如果你还想释放空间,你可以使用docker images rm <image-id>。回到 Docker images 结果,我们可以看到图像的 ID 为06f0d8328d66。以下内容将删除该图像,我们将获得该空间(在本例中为 539MB):
$ docker image rm 06
Untagged: docker.elastic.co/elasticsearch/elasticsearch:6.1.1
Untagged: docker.elastic.co/elasticsearch/elasticsearch@sha256:9e6c7d3c370a17736c67b2ac503751702e35a1336724741d00ed9b3d00434fcb
Deleted: sha256:06f0d8328d66a0f620075ee689ddb2f7535c31fb643de6c785deac8ba6db6a4c
Deleted: sha256:133d33f65d5a512c5fa8dc9eb8d34693a69bdb1a696006628395b07d5af08109
Deleted: sha256:ae2e02ab7e50b5275428840fd68fced2f63c70ca998a493d200416026c684a69
Deleted: sha256:7b6abb7badf2f74f1ee787fe0545025abcffe0bf2020a4e9f30e437a715c6d6a

现在图像消失了,我们也收回了空间。

Note that if there are any containers that have been run off that image that still exist, then this will fail and those containers can be either running or stopped. Just doing a docker ps -a may not show the offending container, so you may have to use docker ps -a to find the stopped containers and delete them first.

还有更多。。。

在这一点上,你对 Docker 的了解足以让你变得非常危险!因此,让我们继续研究如何在安装了自己的应用程序的情况下创建自己的容器。首先,让我们看看如何将爬虫程序变成一个可以在容器中运行的微服务。

使用 Nameko 创建通用微服务

在接下来的几个食谱中,我们将创建一个可以在 Docker 容器中作为微服务运行的刮板。但在直接投入讨论之前,让我们先看看如何使用名为 Nameko 的 Python 框架创建一个基本的微服务。

准备

我们将使用一个名为 Nameko 的 Python 框架(发音为[nahmeh-koh]实现微服务。与 Flask RESTful 一样,使用 Nameko 实现的微服务只是一个类。我们将指导 Nameko 如何将该类作为服务运行,Nameko 将连接消息总线实现,以允许客户端与实际的微服务通信。

默认情况下,Nameko 使用 RabbitMQ 作为消息传递总线。RabbitMQ 是一种高性能消息传递总线,非常适合执行微服务之间使用的消息传递服务类型。这是一个类似于我们之前在 SQS 中看到的模型,但更多的是为位于同一数据中心的服务而设计,而不是跨云计算。这实际上是 RabbitMQ 的一个很好的用途,因为我们现在倾向于在相同的环境中集群/扩展微服务,特别是在容器化集群(如 Docker 或 Kubernetes)中。

因此,我们需要有一个运行 RabbitMQ 的本地实例。确保有一个 RabbitMQ 容器按照前面的配方运行。

还要确保已安装 Nameko:

pip install Nameko

怎么做

我们按照以下方法进行配方:

  1. 示例微服务在10/01/hello_microservice.py中实现。这是一个非常简单的服务,可以传递一个名称,微服务回复Hello, <name>!
  2. 要运行 microservice,我们只需从终端(在脚本目录中)执行以下命令:
$nameko run hello_microservice
  1. Nameko 打开与指定的微服务名称匹配的 Python 文件并启动微服务。开始时,我们将看到几行输出:
starting services: hello_microservice
Connected to amqp://guest:**@127.0.0.1:5672//
  1. 这表明 Nameko 已经找到了我们的微服务,并且它已经连接到端口 5672(RabbitMQ 的默认端口)上的 AMQP 服务器(RabbitMQ)。微服务现在已启动并正在运行,等待请求。

If you go into the RabbitMQ API and go into the queues tab, you will see that Nameko has automatically created a queue for the microservice.

  1. 现在我们必须做一些事情来请求微服务。我们将研究两种方法。首先,Nameko 附带了一个交互式 shell,它允许我们以交互方式向 Nameko 微服务发出请求。您可以在运行 microservice 的另一个终端窗口中使用以下命令启动 shell:
nameko shell
  1. 您将看到一个交互式 Python 会话启动,其输出类似于以下内容:
Nameko Python 3.6.1 |Anaconda custom (x86_64)| (default, Mar 22 2017, 19:25:17)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] shell on darwin
Broker: pyamqp://guest:guest@localhost
In [1]:
  1. 在这个 shell 中,我们可以简单地将 Nameko 称为“n”。为了与我们的服务对话,我们发布以下语句:
n.rpc.hello_microservice.hello(name='Mike')
  1. 这告诉 Nameko 我们想要对hello_microservicehello方法进行 rpc 调用。按回车返回以下结果:
Out[1]: 'Hello, Mike!'
  1. 如果您在运行服务的终端窗口中进行检查,您应该会看到一行额外的输出:
Received a request from: Mike
  1. 也可以从 Python 代码中调用微服务。10/01/say_hi.py中提供了此功能的实现。使用 Python 执行此脚本具有以下输出:
$python say_hi.py
Hello, Micro-service Client!

让我们来看看这些是如何实现的。

工作原理

我们先来看看hello_microservice.py中微服务的实现,其实没有太多的代码,所以这里都是:

from nameko.rpc import rpc

class HelloMicroService:
    name = "hello_microservice"

    @rpc
    def hello(self, name):
        print('Received a request from: ' + name)
        return "Hello, {}!".format(name)

关于这门课,有两点需要指出。第一个是name = "hello_microservice"的声明,这是对微服务实际名称的声明。使用此成员变量代替类名。

第二个是在hello方法上使用@rpc属性。这是一个 Nameko 属性,指定此方法应由微服务作为rpc样式的方法公开。因此,调用方被挂起,直到收到来自微服务的回复。还有其他实现,但出于我们的目的,这是我们将使用的唯一实现。

当使用 nameko run 命令运行时,该模块将查询文件中具有 nameko 属性的方法,并将它们连接到底层总线。

say_hi.py中的实现构造了一个可以调用服务的动态代理。其代码如下所示:

from nameko.standalone.rpc import ClusterRpcProxy

CONFIG = {'AMQP_URI': "amqp://guest:guest@localhost"}

with ClusterRpcProxy(CONFIG) as rpc:
    result = rpc.hello_microservice.hello("Micro-service Client")
    print(result)

动态代理由ClusterRpcProxy类实现。创建类时,我们向其传递一个配置对象,在本例中,该对象指定服务所在的 AMQP 服务器的地址,我们将该实例称为变量rpc。然后,Nameko 动态地将下一部分.hello_microservice标识为微服务的名称(如前面使用微服务类上的 name 字段指定的)。

接下来的部分,.hello表示要调用的方法。结合在一起,Nameko 调用hello_microservicehello方法,将指定的字符串传递给它,由于这是一个 RPC 代理,因此等待直到收到回复。

Remote procedure calls, RPC for short, block until the result comes back from the other system. In contrast with a publish model, where the message is sent off and the sending app continues along.

还有更多。。。

在 Nameko 有很多好东西,我们甚至没有见过。一个非常有用的因素是 Nameko 为多个微服务实例运行侦听器。编写本文时的默认值为 10。在幕后,Nameko 将来自微服务客户端的请求发送到 RabbitMQ 队列,其中将有 10 个请求处理器同时监听该队列。如果一次处理的请求太多,RabbitMQ 将保留消息,直到 Nameko 回收现有的 microservice 实例来处理排队的消息。为了增加微服务的可伸缩性,我们可以通过配置微服务来增加工作人员的数量,或者在另一个 Docker 容器或另一个计算机系统上运行单独的 Nameko 微服务容器。

创建爬取微服务

现在,让我们把我们的刮刀,并使其成为一个名为微服务。该服务将能够独立于 API 的实现运行。这将允许刮板独立于 API 的实现进行操作、维护和缩放

怎么做

我们按照以下方法进行配方:

  1. 微服务的代码很简单。其代码在10/02/call_scraper_microservice.py中,如下所示:
from nameko.rpc import rpc
import sojobs.scraping 

class ScrapeStackOverflowJobListingsMicroService:
    name = "stack_overflow_job_listings_scraping_microservice"

    @rpc
    def get_job_listing_info(self, job_listing_id):
        listing = sojobs.scraping.get_job_listing_info(job_listing_id)
        print(listing)
        return listing

if __name__ == "__main__":
    print(ScrapeStackOverflowJobListingsMicroService("122517"))
  1. 我们创建了一个类来实现微服务,并为其提供了一个方法get_job_listing_info。该方法简单地将实现包装在sojobs.scraping模块中,但为其提供了一个@rpc属性,以便 Nameko 在微服务总线上公开该方法。这可以通过打开终端并使用 Nameko 运行服务来运行:
$ nameko run scraper_microservice
 starting services: stack_overflow_job_listings_scraping_microservice
 Connected to amqp://guest:**@127.0.0.1:5672//
  1. 现在我们可以使用10/02/call_scraper_microservice.py脚本中的代码运行 scraper。文件中的代码如下所示:
from nameko.standalone.rpc import ClusterRpcProxy

CONFIG = {'AMQP_URI': "amqp://guest:guest@localhost"}

with ClusterRpcProxy(CONFIG) as rpc:
    result = rpc.stack_overflow_job_listings_scraping_microservice.get_job_listing_info("122517")
    print(result)
  1. 它基本上与上一个配方中的客户端代码相同,但是更改了微服务和方法名称,当然还传递了特定的作业列表 ID。运行时,您将看到以下输出(截断):
{"ID": "122517", "JSON": {"@context": "http://schema.org", "@type": "JobPosting", "title": "SpaceX Enterprise Software Engineer, Full Stack", "skills": ["c#", "sql", "javascript", "asp.net", "angularjs"], 

...
  1. 就这样,我们创建了一个微服务来从 StackOverflow 获取工作列表!

还有更多。。。

这个微服务只能通过ClusterRpcProxy类调用,不允许任何人在互联网上甚至本地使用 REST 调用。我们将在下一个菜谱中解决这个问题,我们将在一个容器中创建一个 RESTAPI,该容器将与这个微服务进行通信,微服务将在另一个容器中运行

创建刮刀容器

现在我们为 scraper 微服务创建一个容器。我们将学习 DockerFile 以及如何指导 Docker 如何构建容器。我们还将研究提供 Docker 容器主机名,以便它们可以通过 Docker 的集成 DNS 系统找到彼此。最后但并非最不重要的一点是,我们将学习如何配置 Nameko 微服务,以便在另一个容器中而不是仅在本地主机上与 RabbitMQ 通信。

准备

我们要做的第一件事是确保 RabbitMQ 在一个容器中运行,并分配给一个自定义 Docker 网络,其中连接到该网络的各种容器将相互通信。在许多其他功能中,它还提供软件定义网络(SDN)功能,以提供容器、主机和其他系统之间的各种类型的集成。

Docker 附带了几个预定义的网络。您可以使用docker network ls命令查看当前安装的网络:

$ docker network ls
NETWORK ID   NAME                                     DRIVER  SCOPE
bc3bed092eff bridge                                   bridge  local
26022f784cc1 docker_gwbridge                          bridge  local
448d8ce7f441 dockercompose2942991694582470787_default bridge  local
4e549ce87572 dockerelkxpack_elk                       bridge  local
ad399a431801 host                                     host    local
rbultxlnlhfb ingress                                  overlay swarm
389586bebcf2 none                                     null    local
806ff3ec2421 stackdockermaster_stack                  bridge  local

为了让我们的容器彼此通信,让我们创建一个名为scraper-net的新网桥网络。

$ docker network create --driver bridge scraper-net
e4ea1c48395a60f44ec580c2bde7959641c4e1942cea5db7065189a1249cd4f1

现在,当我们启动一个容器时,我们使用--network参数将其连接到scraper-net

$docker run -d --name rabbitmq --network scrape-rnet -p 15672:15672 -p 5672:5672 rabbitmq:3-management

该容器现在连接到刮板网络和主机网络。因为它也连接到主机,所以仍然可以从主机系统连接到它。

还请注意,我们使用--name rabbitmq作为选项。这将为该容器命名为rabbitmq,但 Docker 还将解析附加到scraper-net的其他容器的 DNS 查询,以便他们能够找到该容器!

现在我们去把刮刀放在一个容器里。

怎么做

我们按照以下方法进行配方:

  1. 我们创建容器的方法是创建一个dockerfile,然后用它告诉 Docker 创建一个容器。我在10/03文件夹中包含了一个 Dockerfile。内容如下(我们将在如何工作一节中探讨这意味着什么):
FROM python:3
WORKDIR /usr/src/app

RUN pip install nameko BeautifulSoup4 nltk lxml
RUN python -m nltk.downloader punkt -d /usr/share/nltk_data all

COPY 10/02/scraper_microservice.py .
COPY modules/sojobs sojobs

CMD ["nameko", "run", "--broker", "amqp://guest:guest@rabbitmq", "scraper_microservice"]
  1. 要从此 Dockerfile、终端和10/03文件夹中创建图像/容器,请运行以下命令:
$docker build ../.. -f Dockerfile  -t scraping-microservice
  1. 这告诉 Docker 我们想根据给定 Dockerfile(用-f 指定)中的指令构建一个容器。创建的图像由 -t scraping-microservice指定。构建后的../..指定了构建的上下文。构建时,我们会将文件复制到容器中。此上下文指定副本相对于的主目录。运行此命令时,您将看到类似于以下内容的输出:
Sending build context to Docker daemon 2.128MB
Step 1/8 : FROM python:3
 ---> c1e459c00dc3
Step 2/8 : WORKDIR /usr/src/app
 ---> Using cache
 ---> bf047017017b
Step 3/8 : RUN pip install nameko BeautifulSoup4 nltk lxml
 ---> Using cache
 ---> a30ce09e2f66
Step 4/8 : RUN python -m nltk.downloader punkt -d /usr/share/nltk_data all
 ---> Using cache
 ---> 108b063908f5
Step 5/8 : COPY 10/07/. .
 ---> Using cache
 ---> 800a205d5283
Step 6/8 : COPY modules/sojobs sojobs
 ---> Using cache
 ---> 241add5458a5
Step 7/8 : EXPOSE 5672
 ---> Using cache
 ---> a9be801d87af
Step 8/8 : CMD nameko run --broker amqp://guest:guest@rabbitmq scraper_microservice
 ---> Using cache
 ---> 0e1409911ac9
Successfully built 0e1409911ac9
Successfully tagged scraping-microservice:latest
  1. 这可能需要一段时间,因为构建过程需要将所有 NLTK 文件下载到容器中。要检查图像是否已创建,可以运行以下命令:
$ docker images | head -n 2
REPOSITORY            TAG    IMAGE ID     CREATED     SIZE
scraping-microservice latest 0e1409911ac9 3 hours ago 4.16GB
  1. 请注意,此容器的大小为 4.16GB。此图基于Python:3容器,可以看到其尺寸为692MB
$ docker images | grep python
 python 3 c1e459c00dc3 2 weeks ago 692MB

Most of the size of this container is because of the inclusion of the NTLK data files.

  1. 现在,我们可以使用以下命令将此图像作为容器运行:
03 $ docker run --network scraper-net scraping-microservice
starting services: stack_overflow_job_listings_scraping_microservice
Connected to amqp://guest:**@rabbitmq:5672//

我们组装的 scraper 现在正在这个容器中运行,这个输出显示它已连接到位于名为rabbitmq的系统上的 AMQP 服务器

  1. 现在让我们测试一下这是否有效。在另一个终端窗口中运行 Nameko 外壳:
03 $ nameko shell
Nameko Python 3.6.1 |Anaconda custom (x86_64)| (default, Mar 22 2017, 19:25:17)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] shell on darwin
Broker: pyamqp://guest:guest@localhost
In [1]:
  1. 现在,在提示中输入以下内容以调用 microservice:
n.rpc.stack_overflow_job_listings_scraping_microservice.get_job_listing_info("122517")
  1. 由于刮取,您将看到相当多的输出(以下内容被截断):
Out[1]: '{"ID": "122517", "JSON": {"@context": "http://schema.org", "@type": "JobPosting", "title": "SpaceX Enterprise Software Engineer, Full Stack", "skills": ["c#", "sql", "javascript", "asp.net"

祝贺我们现在已经成功地调用了我们的 scraper 微服务。现在,让我们讨论一下这是如何工作的,以及 Dockerfile 是如何为微服务构建 Docker 映像的。

工作原理

让我们首先通过浏览 Docker 在构建过程中告诉 Docker 的操作来讨论 Docker 文件。第一行:

FROM python:3

这会通知 Docker,我们希望基于 Docker Hub 上找到的Python:3映像构建我们的容器映像。这是一个安装了 Python 3 的预构建 Linux 映像。下一行通知 Docker,我们希望所有文件操作都与/usr/src/app文件夹相关。

WORKDIR /usr/src/app

在构建映像的这一点上,我们已经安装了一个基本的 Python3。然后我们需要安装 scraper 使用的各种库,因此下面告诉 Docker 运行 pip 来安装它们:

RUN pip install nameko BeautifulSoup4 nltk lxml

我们还需要安装 NLTK 数据文件:

RUN python -m nltk.downloader punkt -d /usr/share/nltk_data all

接下来,我们将复制我们的 scraper 的实现。下面将scraper_microservice.py文件从上一个配方的文件夹复制到容器映像中。

COPY 10/02/scraper_microservice.py .

这也取决于sojobs模块,因此我们也复制了:

COPY modules/sojobs sojobs

最后一行通知 Docker 在容器启动时要运行的命令:

CMD ["nameko", "run", "--broker", "amqp://guest:guest@rabbitmq", "scraper_microservice"]

这告诉 Nameko 在scraper_microservice.py中运行微服务,并与位于名为rabbitmq的系统上的 RabbitMQ 消息代理进行对话。由于我们将 scraper 容器连接到 scraper net 网络,并且对 RabbitMQ 容器也做了同样的操作,Docker 为我们连接了这两个容器!

最后,我们从 Docker 主机系统运行 Nameko shell。启动时,它报告将在pyamqp://guest:guest@localhost与 AMQP 服务器(RabbitMQ)通信。当我们在 shell 中执行该命令时,Nameko shell 将该消息发送到 localhost。

那么它如何与容器中的 RabbitMQ 实例通信呢?当我们启动 RabbitMQ 容器时,我们告诉它连接到scraper-net网络。它仍然连接到主机网络,因此我们仍然可以与 RabbitMQ 代理对话,只要我们在启动时映射了5672端口

我们在另一个容器中的微服务正在侦听 RabbitMQ 容器中的消息,然后响应该容器,然后由 Nameko 外壳接收。这不是很酷吗?

创建 API 容器

此时,我们只能使用 AMQP 或使用 Nameko shell 或 NamekoClusterRPCProxy类与我们的微服务对话。因此,让我们将 Flask RESTful API 放入另一个容器中,与其他容器一起运行,并进行 REST 调用。这还需要我们运行一个 Elasticsearch 容器,因为该 API 代码也与 Elasticsearch 通信。

准备

首先,让我们在连接到scraper-net网络的容器中启动 Elasticsearch。我们可以通过以下命令启动该操作:

$ docker run -e ELASTIC_PASSWORD=MagicWord --name=elastic --network scraper-net  -p 9200:9200 -p 9300:9300 docker.elastic.co/elasticsearch/elasticsearch:6.1.1

Elasticsearch 现已在我们的scarper-net网络上启动并运行。其他容器中的应用程序可以使用名称 elastic 访问它。现在让我们继续为 API 创建容器。

怎么做

我们按照以下方法进行配方:

  1. 10/04文件夹中有一个api.py文件,该文件实现了先前修改过的 Flask RESTful API,但进行了一些修改。让我们检查一下 API 的代码:
from flask import Flask
from flask_restful import Resource, Api
from elasticsearch import Elasticsearch
from nameko.standalone.rpc import ClusterRpcProxy

app = Flask(__name__)
api = Api(app)

CONFIG = {'AMQP_URI': "amqp://guest:guest@rabbitmq"}

class JobListing(Resource):
    def get(self, job_listing_id):
        print("Request for job listing with id: " + job_listing_id)

        es = Elasticsearch(hosts=["elastic"])
        if (es.exists(index='joblistings', doc_type='job-listing', id=job_listing_id)):
            print('Found the document in Elasticsearch')
            doc =  es.get(index='joblistings', doc_type='job-listing', id=job_listing_id)
            return doc['_source']

        print('Not found in Elasticsearch, trying a scrape')
        with ClusterRpcProxy(CONFIG) as rpc:
            listing = rpc.stack_overflow_job_listings_scraping_microservice.get_job_listing_info(job_listing_id)
            print("Microservice returned with a result - storing in Elasticsearch")
            es.index(index='joblistings', doc_type='job-listing', id=job_listing_id, body=listing)
            return listing

api.add_resource(JobListing, '/', '/joblisting/<string:job_listing_id>')

if __name__ == '__main__':
    print("Starting the job listing API ...")
    app.run(host='0.0.0.0', port=8080, debug=True)
  1. 第一个变化是 API 上只有一个方法。现在我们将重点关注JobListing方法。在该方法中,我们现在进行以下调用以创建 Elasticsearch 对象:
es = Elasticsearch(hosts=["elastic"])
  1. 默认构造函数假定 Elasticsearch 服务器位于本地主机上。此更改现在指向我们的刮板网络上名为 elastic 的主机。
  2. 第二个变化是删除了对 sojobs 模块中函数的调用。相反,我们使用一个Nameko ClusterRpcProxy对象来调用在我们的 scraper 容器中运行的 scraper 微服务。此对象被传递一个将 RPC 代理指向 rabbitmq 容器的配置。
  3. 最后的更改是启动烧瓶应用程序:
    app.run(host='0.0.0.0', port=8080, debug=True)
  1. 默认连接到 localhost 或 127.0.0.1。在一个容器中,这不会绑定到我们的scraper-net网络,甚至不会绑定到主机网络。使用0.0.0.0将服务绑定到所有网络接口,因此我们可以通过容器上的端口映射与之通信。该端口也被移动到了8080,这是 REST API 比 5000 更常见的端口。
  2. 通过修改 API 以在容器中运行,并与 scraper 微服务对话,我们现在可以构建容器。10/04文件夹中有一个 Dockerfile,用于配置容器。其内容如下:
FROM python:3
WORKDIR /usr/src/app

RUN pip install Flask-RESTful Elasticsearch Nameko

COPY 10/04/api.py .

CMD ["python", "api.py"]

This is simpler than the Dockerfile for the previous container. This container doesn't have all the weight of NTLK. Finally, the startup simply executes the api.py files.

  1. 容器是使用以下内容构建的:
$docker build ../.. -f Dockerfile -t scraper-rest-api
  1. 然后,我们可以使用以下命令运行容器:
$docker run -d -p 8080:8080 --network scraper-net scraper-rest-api
  1. 现在让我们检查所有容器是否都在运行:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55e438b4afcd scraper-rest-api "python -u api.py" 46 seconds ago Up 45 seconds 0.0.0.0:8080->8080/tcp vibrant_sammet
bb8aac5b7518 docker.elastic.co/elasticsearch/elasticsearch:6.1.1 "/usr/local/bin/do..." 3 hours ago Up 3 hours 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp elastic
ac4f51c1abdc scraping-microservice "nameko run --brok..." 3 hours ago Up 3 hours thirsty_ritchie
18c2f01f58c7 rabbitmq:3-management "docker-entrypoint..." 3 hours ago Up 3 hours 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp rabbitmq
  1. 现在,我们可以从主机上的终端向 REST 端点发出一个 curl(输出被截断):
$ curl localhost:8080/joblisting/122517
"{\"ID\": \"122517\", \"JSON\": {\"@context\": \"http://schema.org\", \"@type\": \"JobPosting\", \"title\": \"SpaceX Enterprise Software Engineer, Full Stack\", \"skills\": [\"c#\", \"sql\", \"javas

就在这里。我们已经将 API 和功能容器化,并且在容器中运行 RabbitMQ 和 Elasticsearch。

还有更多。。。

这种类型的集装箱化对作战行动的设计和部署是一大福音,但我们仍然需要创建大量 DockerFile、容器和网络来连接它们,并独立运行它们。幸运的是,我们可以使用 docker compose 简化这一过程。我们将在下一个食谱中看到这一点。

使用 docker compose 在本地合成和运行刮板

Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。使用 Compose,您可以使用 YAML 文件来配置应用程序的服务。然后,只需一个命令和一个简单的配置文件,就可以从配置中创建并启动所有服务。

准备

使用 Compose 需要做的第一件事是确保它已安装。Compose 随 Docker for macOS 自动安装。在其他平台上,可以安装也可以不安装。您可以在以下 URL 中找到说明:https://docs.docker.com/compose/install/#prerequisites

另外,确保我们之前创建的所有现有容器都没有运行,因为我们将创建新的容器。

怎么做

我们按照以下方法进行配方:

  1. Docker Compose 使用一个docker-compose.yml文件,告诉 Docker 如何将容器组合为services10/05文件夹中有一个docker-compose.yml文件,用于启动我们的刮板作为服务的所有部分。该文件的内容如下:
version: '3'
services:
  api:
    image: scraper-rest-api
    ports:
      - "8080:8080"
    networks:
      - scraper-compose-net

  scraper:
    image: scraping-microservice
    depends_on:
      - rabbitmq
    networks:
      - scraper-compose-net

  elastic:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.1.1
    ports:
      - "9200:9200"
      - "9300:9300"
    networks:
      - scraper-compose-net

  rabbitmq:
    image: rabbitmq:3-management
    ports:
      - "15672:15672"
    networks:
      - scraper-compose-net

networks:
  scraper-compose-net:
    driver: bridge

有了 Docker Compose,我们从容器的角度出发,转而使用服务。在这个文件中,我们描述了四个服务(api、scraper、elastic 和 rabbitmq)及其创建方式。每个组件的图像标记告诉 Compose 该服务使用哪个 Docker 图像。如果我们需要映射端口,那么我们可以使用ports标记。network标记指定服务连接到的网络,在这种情况下,该网络也在文件中声明为bridged网络。最后要指出的一点是使用depends_on标签进行刮板服务。该服务要求事先运行rabbitmq服务,这会告诉 docker compose 确保按照指定的顺序执行。

  1. 现在,要打开所有内容,请打开一个终端,然后从该文件夹运行以下命令:
    $ docker-compose up
  1. 当 Compose 读取配置并确定要做什么时,会有暂停,然后会有相当多的输出,因为每个容器的输出都会流式传输到这个控制台。在输出的开头,您将看到类似以下内容:
Starting 10_api_1 ...
 Recreating elastic ...
 Starting rabbitmq ...
 Starting rabbitmq
 Recreating elastic
 Starting rabbitmq ... done
 Starting 10_scraper_1 ...
 Recreating elastic ... done
 Attaching to rabbitmq, 10_api_1, 10_scraper_1, 10_elastic_1
  1. 在另一个终端,您可以发出docker ps以查看已启动的集装箱:
$ docker ps
 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
 2ed0d456ffa0 docker.elastic.co/elasticsearch/elasticsearch:6.1.1 "/usr/local/bin/do..." 3 minutes ago Up 2 minutes 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp 10_elastic_1
 8395989fac8d scraping-microservice "nameko run --brok..." 26 minutes ago Up 3 minutes 10_scraper_1
 4e9fe8479db5 rabbitmq:3-management "docker-entrypoint..." 26 minutes ago Up 3 minutes 4369/tcp, 5671-5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp rabbitmq
 0b0df48a7201 scraper-rest-api "python -u api.py" 26 minutes ago Up 3 minutes 0.0.0.0:8080->8080/tcp 10_api_1

Note the names of the service containers. They are wrapped with two different identifiers. The prefix is simply the folder that the composition is run from, in this case 10 (for a '10_' prefix).  You can change this using the -p option to docker-compose up to specify something different. The trailing number is the instance number of the container for that service. In this scenario, we only started one container per service, so these are all _1 at this point. In a little while, we will see this change when we do scaling.

您可能会问:如果我的服务名为rabbitmq,Docker 创建了一个名为10_rabbitmq_1的容器,那么使用rabbitmq作为主机名的微服务如何仍然连接到 RabbitMQ 实例?Docker Compose 已经介绍过这种情况,因为它知道rabbitmq需要翻译成10_rabbitmq_1。美好的

  1. 作为启动此环境的一部分,Compose 还创建了指定的网络:
$ docker network ls | head -n 2
 NETWORK ID NAME DRIVER SCOPE
 0e27be3e30f2 10_scraper-compose-net bridge local

If we didn't specify a network, then Compose would have made a default network and wired everything to that. In this case that would work fine. But in more complicated scenarios this default may not be correct.  

  1. 现在,一切都开始运转了。让我们通过调用 REST scraping API 来检查工作是否正常:
$ curl localhost:8080/joblisting/122517
 "{\"ID\": \"122517\", \"JSON\": {\"@context\": \"http://schema.org\", \"@type\": \"JobPosting\", \"title\": \"SpaceX Enterprise Software Engineer, Full Stack\", \"
...
  1. 我们还可以检查 Elasticsearch 是否正在运行,检查工作列表的索引,现在我们已经请求了一个:
$ curl localhost:9200/joblisting
{"error":{"root_cause":[{"type":"index_not_found_exception","reason":"no such index","resource.type":"index_or_alias","resource.id":"joblisting","index_uuid":"_na_","index":"j
...
  1. 我们还可以使用 docker compose 来扩展服务。如果我们想添加更多的微服务容器来增加可以处理的请求量,我们可以告诉 Compose 增加 scraper 服务容器的数量。以下内容将刮板容器的数量增加到 3 个:
docker-compose up --scale scraper=3
  1. Compose 将考虑一下这个请求,然后发出以下消息,说明它正在启动另外两个 scraper 服务容器(随后将有来自这些容器的大量输出):
10_api_1 is up-to-date
10_elastic_1 is up-to-date
10_rabbitmq_1 is up-to-date
Starting 10_scraper_1 ... done
Creating 10_scraper_2 ...
Creating 10_scraper_3 ...
Creating 10_scraper_2 ... done
Creating 10_scraper_3 ... done
Attaching to 10_api_1, 10_elastic_1, 10_rabbitmq_1, 10_scraper_1, 10_scraper_3, 10_scraper_2
  1. docker ps现在将显示三个刮板容器正在运行:
Michaels-iMac-2:09 michaelheydt$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b9c2da0c9008 scraping-microservice "nameko run --brok..." About a minute ago Up About a minute 10_scraper_2
643221f85364 scraping-microservice "nameko run --brok..." About a minute ago Up About a minute 10_scraper_3
73dc31fb3d92 scraping-microservice "nameko run --brok..." 6 minutes ago Up 6 minutes 10_scraper_1
5dd0db072483 scraper-rest-api "python api.py" 7 minutes ago Up 7 minutes 0.0.0.0:8080->8080/tcp 10_api_1
d8e25b6ce69a rabbitmq:3-management "docker-entrypoint..." 7 minutes ago Up 7 minutes 4369/tcp, 5671-5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp 10_rabbitmq_1
f305f81ae2a3 docker.elastic.co/elasticsearch/elasticsearch:6.1.1 "/usr/local/bin/do..." 7 minutes ago Up 7 minutes 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp 10_elastic_1
  1. 现在我们可以看到,我们有三个名为10_scraper_110_scraper_210_scraper_3的容器。凉的如果进入 RabbitMQ 管理 UI,可以看到有三个连接:

The Nameko queues in RabbitMQ Note that each has a different IP address. On a bridged network like the one we have created, Compose allocates the IP addresses on the 172.23.0 network, starting at .2.  

在操作上,来自 API 的所有传入抓取请求都将路由到 rabbitmq 容器,然后实际的 rabbitmq 服务将消息分布在所有活动连接上,从而分布在所有三个容器上,帮助我们扩展处理。

服务实例也可以通过使用较少数量的容器发出缩放值来进行缩放,Compose 将通过移除容器来响应,直到达到该值为止。

当我们全部完成后,我们可以告诉 Docker Compose 把所有东西都放下:

$ docker-compose down
Stopping 10_scraper_1 ... done
Stopping 10_rabbitmq_1 ... done
Stopping 10_api_1 ... done
Stopping 10_elastic_1 ... done
Removing 10_scraper_1 ... done
Removing 10_rabbitmq_1 ... done
Removing 10_api_1 ... done
Removing 10_elastic_1 ... done
Removing network 10_scraper-compose-net

执行 docker ps 将显示所有容器都已移除。

还有更多。。。

我们几乎没有接触过 Docker 和 Docker Compose 的许多功能,甚至还没有开始研究使用 Docker swarm 等服务。虽然 docker Compose 很方便,但它只在单个主机上运行容器,这最终会限制可伸缩性。Docker swarm 将执行与 Docker Compose 类似的操作,但在集群内的多个系统中发挥神奇的作用,从而实现更大的可扩展性。但希望这能让您感受到 Docker 和 Docker Compose 的价值,以及它们在创建灵活的刮片服务时的价值。