Docker 1.12.0 版本中服务俨然是一等公民:新版本的 Docker 允许服务镜像的复制和升级,以及动态负载均衡。有了 Docker 1.12 ,服务可以接入所有 Swarm 节点的端口,并通过 Docker 中基于虚拟 IP ( VIP )的负载均衡法,或者基于轮询调度( RR ) DNS 的均衡负载法(抑或两种方法兼有)来实现负载均衡。
如果你还是初次接触负载均衡的概念,那我就来解释一下吧。负载均衡器会把工作负载布置到一套联网的计算机服务器或组件上,并使计算资源能以一种最理想的方式被利用。负载均衡器能监测服务器或组件故障,正确重新配置系统,来提供很高的可用性。在这篇博文中,我将尝试回答以下问题:
我们开始吧——
负载均衡( LB )功能对于 Docker 而言绝非新事物。它最初出现在 1.10 版的 Docker 上,那时 Docker 引擎为用户自定义网络中的容器内嵌了一个 DNS 服务器。尤其是那些用网络别名( net-alias )运行的容器,在使用别名时都需要这个嵌入式 DNS 来解决容器 IP 地址的问题。
毫无疑问,基于轮询调度( RR ) DNS 的均衡负载法是相当简单易行的,对于特定脚本的增量能力而言,还是一种极为优秀的机制——如果你考虑进那些默认的地址选择误差的话。不过它也有着这样那样的限制和问题,比如一些客户端应用会把 DNS 主机名与实际 IP 的映射关系缓存起来,这就会在映射发生改变时,造成应用的宕机。同时,非零的 DNS TTL 值会使得 DNS 记录延迟反映最新的细节。基于负载均衡的 DNS 不会做出正确的、基于客户端执行的负载均衡。轮询调度( RR ) DNS 经常被叫做“穷人协议”,我们会在以后发布更多关于它的内容。
现在, Docker 1.12.0 有了内置的负载均衡功能。负载均衡被当作容器网络模式(正确叫法是 CNM , Container Network Model )不可分割的一部分,并在网络、端点和沙盒等 CNM 高层架构上运行。 1.12 版 Docker 自带基于 VIP 的负载均衡。基于 VIP 的服务器使用 Linux IPVS 负载均衡来连接后台容器。
负载均衡器再也不会集中使用了,它已经被分散布置,也因此可以实现规模化。负载均衡被植入了独立容器,无论何时,只要容器想与其它服务之间通信,负载均衡就能即时嵌入到那个容器中。负载均衡如今更为强大,能够在各种不确定因素中完美工作。
新的 1.12.0 版 Docker Swarm 模式使用 IPVS (名为 “ ip_vs ” 的核心模块)来进行负载均衡。这是一个被整合进 Linux 内核的负载均衡模块。
Docker 1.12 第一次使用了路由网( Routing Mesh )。有 IPVS 在内核中规划数据包的路线, Swarm 的路由网能实现高性能的、可感知容器的负载均衡。 Docker Swarm 模式包含有一个能使用多主机网络的路由网。它能创建一个基于云网络的虚拟拓展式局域网( VXLAN ),允许两台不同主机上的容器顺畅通信,就像在同一台主机上一样。在本文的最后,我们会详细讨论路由网。
不管你什么时候在 Swarm 集群内创建了新的服务,这个服务都会获得虚拟 IP ( VIP ) 地址。不管你什么时候尝试对特定 VIP 提出需求, Swarm 负载均衡器都会把这一需求分配给特定服务中的某一容器。事实上,内置的服务发现功能就可以决定虚拟 IP 的服务名。最后,从服务 VIP 到容器 IP 负载均衡是通过 IPVS 实现的。这里要特别提一下,VIP 只有在集群内是有用的。在集群外,它一点意义都没有,因为它是一个私有的,无法路由转发的 IP 。
我在谷歌云引擎上的 1.12.0 版 Docker 上运行了一个 6 节点集群。让我们通过以下步骤来检查一下 VIP 地址:
1. 创建一个新的覆盖网络:
$docker network create – driveroverlay \
– subnet10.0.3.0/24 \
– optencrypted \
collabnet
2. 创建一个名叫 collabweb 的新服务,就是如下所示,一个简单的 Nginx 服务端:
$docker service create \
— replicas 3 \
— name collabweb \
— network collabnet \
Nginx
3. 如下所示,在名为 “ collabnet ” 的 Swarm 覆盖网络下,有 3 个容器副本在 3 个节点上运行服务。
4. 如下所示,用 Docker 检查命令来定时查看服务:
它显示了添加到各个服务的 “ VIP ” 地址。在下面的图表中,有一个可以帮助我们获得可见 IP 地址的简单命令:
5. 你可以用 nsenter 功能进入它的沙盒,来检查 iptables 的配置:
在任何 iptable 里,数据包通常先进入 Mangle Table 链,然后再进入 NAT Table 链。前者负责修正 IP 数据包,而后者只负责地址的翻译。如上面的 mangle table 所示, 10.0.3.2 服务器 IP 用 iptables 输出链获得了 0x10c 的标记。 IPVS 使用了这一标记,并如下所示,将其负载均衡到了容器 10.0.3.3 , 10.0.3.5 和 10.0.3.6 。
如上图,你可以在 Linux 内核里用 ipvsadm 来安装、维系或检测 IP 可见的服务端 table 。这一工具可以通过基于 Linux 分布规则的 apt 或 yum 命令,安装到任何 Linux 机器上。
按下图,一个典型的轮询调度 DNS 和 IPVS 负载均衡可以清楚地区分开来。每次我们尝试连接服务器时(不管是用 curl 命令或 dig 命令),轮询调度 DNS 会显示顺序的 IP 地址列表,而 IPVS 会将其负载均衡到容器上(比如 10.0.0.1, 10.0.0.2 和 10.0.0.3 )。
6. 让我们在同一网络下创建一个名叫 collab-box 的新服务。如图所示,一个新的可见 IP ( 10.0.3.4 )会自动像下面这样附加到这个服务:
同时,服务发现也按照预想在工作,
IPVS ( IP 虚拟服务器)在 Linux 内核中执行传输层的负载均衡,因此被叫做 “ Layer-4 转换”。它是一个被整合进 Linux 内核的负载均衡模块,基于 Netfllter (网络过滤器)。它支持 TCP 、 SCTP 和 UDP 、 v4 和 v7 。 IPVS 在真正的服务器集群之前先在主机上工作,作为负载均衡器。它能把对服务(基于 TCP/UDP )的需求引导到真正的服务器上去,让真正服务器的服务以虚拟服务的方式呈现在单个 IP 地址上。
值得特别注意的是, IPVS 并不是一个代理——它是运行在 4 层网络上的转发者。 IPVS 负责转送从客户端到后台的信息通信,这意味着你可以负载均衡任何东西,甚至是 DNS !它能使用的模式包括以下特征:
IPVS 有很多有趣的特性,而且已经在 Linux 内核用了 15 年以上了。下面这张表区分了 IPVS 和其他负载均衡工具:
路由网络( Routing Mesh )不是负载均衡器,它利用了负载均衡的概念。路由网为某一特定服务器提供通用式端口,这些端口都基于服务发现和负载均衡。所以,要想接触集群外的服务器,你需要开放端口,并通过公共端口连接它们。
简而言之,如果你有 3 个 Swarm 节点, A 、 B 和 C ,还有一个运行在节点 A 和 C 上的服务,并且已经指定了节点端口 30000 ,那么只要通过端口 30000 上 3 个 Swarm 节点中的任意一个,就能连接该服务,并自动在两个运行的容器之间完成负载均衡,不管这个服务器是否在那台机器上运行。如果时间允许,我会另写一篇博客来细讲路由网。
请特别注意, 1.12 版 Docker 引擎创建 ingress overlay 网络来达到 Routing Mesh 的目标。一般情况下,前端网络服务和 sandbox 是 “ ingress ” 网络的一部分,会在路由网里被特别关注。所有的节点都通过内置其中的沙盒网络命名空间,默认成为 “入口” 覆盖网络的一部分。
你可以把服务的端口开放给一个外部的负载均衡器。从内部而言, Swarm 允许你特殊规定如何在节点之间分配服务容器。如果你想用 L7 负载均衡器,你需要把它们指定给任何(全部也好,部分也好)节点 IP 和公共端口——这只有在你的 L7 负载均衡器无法变成集群的一部分时才有用。如果你的 L7 负载均衡器可以通过自行运行,变成集群的一部分,那么它们只需指定给自命名的服务即可(会分配到一个 VIP )。一个典型的架构会是这副样子: