十二、负载均衡
Docker 群模式服务提供了一种可以跨节点集群扩展的分布式应用。群模式基于服务的 DNS 名称在群中的不同服务之间提供内部负载均衡。如果服务在主机端口上发布,群模式还在服务的不同任务之间提供入口负载均衡。此外,可以使用放置约束在特定节点上调度服务任务。
服务发现
一个群有一个嵌入其中的 DNS 服务器。服务发现基于 DNS 名称。群管理器为群中的每个服务分配一个唯一的 DNS 名称条目。Swarm manager 使用内部负载均衡,根据服务的 DNS 名称为群中的不同服务分配请求。
自定义计划
默认情况下,服务副本使用分散调度策略在集群中的节点上进行调度。用户可以为服务配置放置约束,以便在特定节点上调度副本。第 6 章讨论了使用约束的调度。
入口负载均衡
默认情况下,在发布的端口上为外部访问公开的每个服务都被添加到 ingress 覆盖网络中。用户可以使用 --publish 或 -p 选项指定任何可用的端口来公开服务。 --publish ( -p ) 选项的语法是 --publish <PublishedPort>:<TargetPort> ,其中 <PublishedPort> 变量用于主机上发布的端口, <TargetPort> 变量用于容器端口。如果 --publish , -p 选项没有指定 <PublishedPort> 端口来在群上发布服务,管理器自动在从范围 30000-32767 中选择的发布端口上公开服务。
问题
入口负载均衡用于在服务任务之间分配负载,即使群由单个节点组成也要使用。图 12-1 显示了多节点集群的入口负载均衡。客户端可以访问群中的任何节点,无论该节点是否调度了服务任务,并且使用入口负载均衡将客户端请求转发到服务任务之一。

图 12-1。
Ingress load balancing
单个客户端访问单个节点,结果,在跨群节点分布外部客户端负载方面,群未被充分利用。客户端负载在群节点之间并不均衡。单个节点不提供任何容错。如果该节点出现故障,访问该节点服务的外部客户端将无法使用该服务。
解决方案
AWS 弹性负载均衡器(ELB) 用于在多个 EC2 实例之间分配客户端负载。当用于 Docker 群模式时,AWS 弹性负载均衡器将客户端负载分布在托管群节点的不同 EC2 实例上。外部负载均衡器使用 LB 监听器访问(监听) 群中运行的服务的发布端口处的每个 EC2 实例上的群。每个 LB 侦听器都有一个 LB 端口映射到每个 EC2 实例上的一个实例端口(服务的发布端口)。蜂群中的 ELB 如图 12-2 所示。

图 12-2。
External load balancer
由于即使单个节点停机或变得不可用,客户端也不访问单个主机上的服务,所以群不会变得不可用,因为外部负载均衡器将客户端请求定向到群中的不同节点。即使所有节点都可用,客户端流量也会分布在不同的节点上。例如,客户端可能在特定时间从一个节点接受服务,此后不久从另一个节点接受服务。因此,外部负载均衡器提供两种功能:负载均衡和容错。此外,托管群的云提供商可以提供额外的功能,如安全和灵活的外部负载均衡。AWS 弹性负载均衡器提供的弹性负载均衡根据客户端流量来调整请求处理能力。
本章讨论 CoreOS 上用户创建的 Swarm 的负载均衡。它还讨论了 Docker 上为 AWS 托管服务自动提供的弹性负载均衡器。
设置环境
启动三个 CoreOS 实例 - 一个用于管理节点,两个用于工作节点 - 如图 12-3 所示。从 EC2 仪表板中获取 manager 实例的公共 IP 地址,如图 12-3 所示。

图 12-3。
CoreOS instances on EC2 for a manager and two worker nodes
SSH 登录到管理器节点以启动群组模式。在第 2 章中讨论了在 CoreOS 上初始化一个群组模式以及将工人节点加入群组。复制 docker swarm join 命令输出,将工作者节点加入到群中。用 docker node ls 命令列出群节点。
core@ip-10-0-0-226 ∼ $ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS 9iqh5tg7hxy8u43tlifd1ri0q ip-10-0-0-203.ec2.internal Ready Active aoe1b2623qj03852mrc5cax97 ip-10-0-0-198.ec2.internal Ready Active dsyo3b6553ueishozhfb1apad * ip-10-0-0-226.ec2.internal Ready Active Leader
创建 Hello World 服务
接下来,用 docker service create 命令创建一个 hello world 服务。使用 --publish 选项在端口 8080 公开服务。使用 --publish 或 -p 发布服务的语法如下。
docker service create \ --name <SERVICE-NAME> \ --publish <PUBLISHED-PORT>:<TARGET-PORT> \ <IMAGE>
<PUBLISHED-PORT> 是主机上公开的端口,而 <TARGET-PORT> 是 Docker 容器公开服务的端口。使用 tutum/hello-world Docker 映像、 <PUBLISHED-PORT> 作为 8080、 <TARGET-PORT> 作为 80、 <SERVICE-NAME> 作为 hello-world ,运行下面的命令来创建服务。
core@ip-10-0-0-226 ∼ $ docker service create \ > --name hello-world \ > --publish 8080:80 \ > --replicas 3 \ > tutum/hello-world 0gk3wom7z91fpm5o9e6optmb5
服务被添加到入口覆盖网络,并且服务被展示在群上的每个节点处,无论服务任务是否正在该节点上运行。 hello-world 服务列出了 3/3 的副本。
core@ip-10-0-0-226 ∼ $ docker service ls ID NAME REPLICAS IMAGE COMMAND 0gk3wom7z91f hello-world 3/3 tutum/hello-world
使用 docker service ps hello-world 命令列出服务任务,三个任务按计划列出,每个节点一个。
core@ip-10-0-0-226 ∼ $ docker service ps hello-world ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR di5oilh96jmr6fd5haevkkkt2 hello-world.1 tutum/hello-world ip-10-0-0-198.ec2.internal Running Running 24 seconds ago 5g5d075yib2td8466mh7c01cz hello-world.2 tutum/hello-world ip-10-0-0-226.ec2.internal Running Running 24 seconds ago 5saarf4ngju3xr7uh7ninho0o hello-world.3 tutum/hello-world ip-10-0-0-203.ec2.internal Running Running 23 seconds ago
一个 Docker 容器正在 manager 节点上运行。
core@ip-10-0-0-226 ∼ $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b73cbcd0c37e tutum/hello-world:latest "/bin/sh -c 'php-fpm " 34 seconds ago Up 32 seconds 80/tcp hello-world.2.5g5d075yib2td8466mh7c01cz
一个 Docker 容器正在一个 worker 节点上运行。
core@ip-10-0-0-198 ∼ $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8bf11f2df213 tutum/hello-world:latest "/bin/sh -c 'php-fpm " 38 seconds ago Up 36 seconds 80/tcp hello-world.1.di5oilh96jmr6fd5haevkkkt2
第三个 Docker 容器运行在另一个工作节点上。
core@ip-10-0-0-203 ∼ $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a461bfc8d4f9 tutum/hello-world:latest "/bin/sh -c 'php-fpm " 40 seconds ago Up 38 seconds 80/tcp hello-world.3.5saarf4ngju3xr7uh7ninho0o
调用 Hello World 服务
在没有外部负载均衡器的情况下,可以在发布端口的每个节点处建立入口连接。要调用管理器节点的服务,从 EC2 控制台获取 Swarm manager 实例的公共 DNS,如图 12-3 所示。
在 web 浏览器中调用位于 <PublicDNS>:<PublishedPort> URL 的服务,如图 12-4 所示。

图 12-4。
Invoking the service in a browser
类似地,要在 worker 节点调用服务,从 EC2 控制台获取 worker 实例的公共 DNS,并在 web 浏览器的 <PublicDNS>:<PublishedPort> URL 调用服务,如图 12-5 所示。

图 12-5。
Invoking the service at a worker node
类似地,要在另一个 worker 节点调用服务,从 EC2 控制台获取 worker 实例的公共 DNS,并在 web 浏览器中的 <PublicDNS>:<PublishedPort> URL 调用服务,如图 12-6 所示。

图 12-6。
Invoking the service at the other worker node
外部 AWS 弹性负载均衡器在 EC2 实例之间分配负载,而入口负载均衡器在服务任务之间分配负载。在前面的例子中,当在群管理器实例和群工作器实例调用服务时,调用相同的服务任务,如相同的主机名所示(图 12-4 和 12-6 )。这演示了入口负载均衡。
如果在同一主机上调用服务,则可能会调用不同的服务任务。例如,再次调用 Swarm manager 实例上的服务。服务于不同的服务任务,如图 12-7 中不同的主机名所示。这与之前在图 12-4 中提供的主机名进行了比较,再次证明了入口负载均衡。

图 12-7。
Different hostname served when invoking the service at the manager node again
创建外部弹性负载均衡器
在本节中,我们将在 AWS 云上创建一个外部弹性负载均衡器。单击 EC2 仪表板中的负载均衡器。然后点击 Create Load Balancer 创建一个新的负载均衡器,如图 12-8 所示。

图 12-8。
Creating a new load balancer
AWS 弹性负载均衡提供两种类型的负载均衡器 - 经典负载均衡器和应用负载均衡器。传统负载均衡器基于应用或网络级别的信息路由流量,而应用负载均衡器基于高级应用级别的信息路由流量。经典的负载均衡器应该能够满足对多个 EC2 实例的流量进行最简单的负载均衡,并且是我们用于 Docker Swarm 实例的负载均衡器。选择经典负载均衡器,然后点击继续,如图 12-9 所示。

图 12-9。
Selecting the classic load balancer option
在定义负载均衡器对话框中,指定一个负载均衡器名称( HelloWorldLoadBalancer ),并选择一个 VPC 在其中创建负载均衡器,如图 12-10 所示。VPC 必须在创建负载均衡器之前存在,并且必须是创建要进行负载均衡的 EC2 实例的位置。默认情况下,负载均衡器协议是 HTTP,实例协议也是。保留 HTTP 协议的默认设置,将负载均衡器端口和实例端口指定为 8080,因为 Hello World 服务在端口 8080 公开。

图 12-10。
Selecting the load balancer protocol
在选择子网选项卡中,单击可用子网表中列出的一个或多个子网。子网被添加到选中的子网中,如图 12-11 所示。点击下一步。要提供高可用性,请在不同的可用性区域中至少选择两个子网。

图 12-11。
Selecting subnets
在分配安全组选项卡中,选择创建新的安全组,如图 12-12 所示。在类型中,选择自定义 TCP 规则。选择 TCP 协议和端口范围 8080。选择任意位置作为源,其值为 0.0.0.0/0。点击下一步。

图 12-12。
Assigning security groups
单击 配置安全设置 中的 下一步 ,因为我们没有使用 HTTPS 或 SSL 协议。在配置运行状况检查选项卡中,为 ping 协议选择 HTTP,为 ping 端口选择 8080。指定 ping 路径为 / ,如图 12-13 所示。在 高级详细信息 区域保留默认值,然后单击 下一步 。

图 12-13。
Configuring a health check
选择列出的三个 Swarm 实例,如图 12-14 所示。另外,选择启用跨区域负载均衡,这将在所有可用性区域中的所有后端实例之间平均分配流量。点击下一步。

图 12-14。
Adding EC2 instances
在 添加标签 选项卡中,不需要添加任何标签。在审核选项卡中,点击创建,如图 12-15 所示。如上所述,负载均衡器是面向互联网的类型。

图 12-15。
Review your settings then create the load balancer
负载均衡器创建完成,如图 12-16 所示。

图 12-16。
The load balancer has been created
从 EC2 控制台获取负载均衡器的 DNS 名称,如图 12-17 所示。最初,状态将是 3 个实例中的 0 个在服务中 ,因为注册仍在进行中。

图 12-17。
Obtaining the DNS name of the load balancer
过一会儿,状态应该变成 3 个实例中的 3 个在服务中 ,所有实例都应该在服务中,如图 12-18 所示。

图 12-18。
Status indicates three of three instances InService
Hello World 服务可以从 web 浏览器中的 <DNSname>:<LoadBalancerPort> URL 调用,如图 12-19 所示。

图 12-19。
Invoking the Hello World service
外部弹性负载均衡器平衡集群中 EC2 实例之间的负载。因为入口负载均衡器在不同的服务任务之间平衡负载,如果在 ELB DNS 名称上再次调用服务,则可能会调用不同的服务任务,如图 12-20 所示。

图 12-20。
Different service task served
AWS Docker 中的负载均衡
虽然在使用命令行创建 Docker 群组时必须创建外部弹性负载均衡器(首先启动群组模式,然后将工作节点加入群组),但在第 3 章中介绍的 AWS 托管服务 Docker 会自动创建弹性负载均衡器。
使用 Docker for AWS 创建一个有三个管理器节点和五个工作者节点的 Swarm(之前创建的 Swarm 可能会被更新),如图 12-21 所示。如图 12-21 中的资源选项卡所示,创建一个外部弹性负载均衡器作为群资源之一。

图 12-21。
CloudFormation stack for a Docker Swarm
创建一个面向互联网的弹性负载均衡器,如图 12-22 所示。负载均衡器的公共 DNS 可以用于访问群,如后面所讨论的。

图 12-22。
Load balancer for the Swarm created with Docker for AWS
选择 实例 选项卡。列出了群中的所有实例,经理或工人。所有的实例都应处于使用状态,如图 12-23 所示。

图 12-23。
Instances status is InService
更新 listeners 选项卡中的负载均衡器侦听器,添加/修改一个侦听器,其负载均衡器端口设置为 8080,实例端口设置为 8080,这是我们创建的 Hello World 服务的发布端口,如图 12-24 所示。

图 12-24。
The Listeners tab
从 EC2 控制台获取其中一个管理器节点的公共 IP 地址。
SSH 登录到管理器节点。
[root@localhost ∼]# ssh -i "docker.pem" docker@34.205.43.53 Welcome to Docker!
列出群体节点。
∼ $ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS 8d0qv1epqu8xop4o2f94i8j40 ip-172-31-8-4.ec2.internal Ready Active 8eckb0twpbuoslfr58lbibplh ip-172-31-32-133.ec2.internal Ready Active b6f18h4f3o44gkf5dhkzavoy3 ip-172-31-2-148.ec2.internal Ready Active k9nl2zcmjzobbqu5c5bkd829g ip-172-31-21-41.ec2.internal Ready Active p0d70jwh5vpjwximc1cpjfjkp * ip-172-31-1-130.ec2.internal Ready Active Leader r02ftwtp3n4m0cl7v2llw4gi8 ip-172-31-44-8.ec2.internal Ready Active rd8d0kksuts3aa07orhgkri3i ip-172-31-41-86.ec2.internal Ready Active Reachable xks3sw6qgwbcuacyypemfbxyj ip-172-31-31-117.ec2.internal Ready Active Reachable
创建一个 Hello World 服务,并在端口 8080(发布端口) 公开该服务。
∼ $ docker service create \ > --name hello-world \ > --publish 8080:80 \ > --replicas 10 \ > tutum/hello-world n4hmfognhjrasf5nhukr55krb
服务任务在整个群体中进行调度。
∼ $ docker service ps hello-world ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS y1fetn3kpwwn hello-world.1 tutum/hello-world:latest ip-172-31-2-148.ec2.internalRunning Running 15 seconds ago 5i15zl9dickd hello-world.2 tutum/hello-world:latest ip-172-31-44-8.ec2.internalRunning Running 17 seconds ago k9glaavn0gzg hello-world.3 tutum/hello-world:latest ip-172-31-8-4.ec2.internalRunning Running 17 seconds ago n83f89ijlokn hello-world.4 tutum/hello-world:latest ip-172-31-41-86.ec2.internalRunning Running 17 seconds ago nelf275h9tp1 hello-world.5 tutum/hello-world:latest ip-172-31-8-4.ec2.internalRunning Running 16 seconds ago w4c8zcvlq5v7 hello-world.6 tutum/hello-world:latest ip-172-31-32-133.ec2.internalRunning Running 17 seconds ago b5qvbbgkrpd5 hello-world.7 tutum/hello-world:latest ip-172-31-21-41.ec2.internalRunning Running 16 seconds ago qlm8dt9fuv92 hello-world.8 tutum/hello-world:latest ip-172-31-31-117.ec2.internalRunning Running 17 seconds ago t3tenhpahh7g hello-world.9 tutum/hello-world:latest ip-172-31-44-8.ec2.internalRunning Running 17 seconds ago up64ekxqeftk hello-world.10 tutum/hello-world:latest ip-172-31-1-130.ec2.internalRunning Running 17 seconds ago
可以在不明确指定发布端口的情况下创建 hello-world 服务。
∼ $ docker service create \ > --name hello-world \ > --publish 80 \ > --replicas 3 \ > tutum/hello-world
群组管理器自动分配 30000-32767 范围内的公布端口;默认端口是 30000(如果可用的话)。AWS Swarm Docker 的负载均衡器中的监听器可能需要修改,以便为 LoadBalancerPort:ServiceInstancePort 添加映射,比如 30000:30000。
获取自动创建的弹性负载均衡器的公共 DNS,如图 12-25 所示。

图 12-25。
Obtaining the public DNS of the ELB
在 web 浏览器中访问 <PublicDNS>:<PublishedPort> 处的服务,如图 12-26 所示。该请求被转发到群中的一个实例上的入口负载均衡器。外部请求转发到的实例不一定要承载服务任务。寻找服务任务是入口负载均衡器的工作。

图 12-26。
Accessing a Docker service at the elastic load balancer DNS
摘要
本章讨论了群模式下的负载均衡。入口负载均衡器用于在服务的任务之间分配负载。群中的每个服务被分配一个 DNS 名称,内部负载均衡器基于 DNS 名称在服务之间平衡服务请求。我们还为 AWS EC2 实例创建了一个外部负载均衡器,以便在 EC2 实例之间分配负载。Docker for AWS 会在 AWS 上自动创建一个外部负载均衡器。在下一章,我们将讨论开发一个基于 Docker Swarm 的高可用性网站。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论