如图所示,我们将在 swarm 集群中部署 “client” 服务 和 “vote” 服务,其中 “vote” 服务部署多个副本。
客户端请求 “vote” 服务时,输出结果中包含服务端的容器 ID,这样就更方便演示网络请求。
docker node ls
使用如下命令,创建 overlay 网络:
docker network create --driver overlay overlay
一、基于 DNS 的负载均衡(DOCKER引擎内嵌了DNS服务器,创建容器时,在各个容器内设置nameserver,指向dns服务)
dns服务内会配置overlay网络内的容器的IP和容器主机之间的映射
下图描述了基于 DNS 的负载均衡是如何工作的:DNS轮询
DNS server 内嵌于 Docker 引擎。
Docker DNS 解析服务名 “vote” 并返回容器 ID 地址列表(随机排序)。
客户端通常会挑第一个 IP 访问,因此负载均衡可能发生在服务器的不同实例之间。
-
docker service create --endpoint-mode dnsrr --replicas 1 --name client --network overlay1 registry.cn-hangzhou.aliyuncs.com/anoy/ubuntu ping anoyi.com
-
-
docker service create --endpoint-mode dnsrr --name vote --network overlay1 --replicas 2 registry.cn-hangzhou.aliyuncs.com/anoy/vote
可以看出 "client" 运行于 node2,在 node2 上进入 client 容器,使用 dig 来解析服务名 "vote",如下所示,"vote" 解析到 10.0.0.6 和 10.0.0.5
docker exec -it 436702b21a1c /bin/bashdig vote;使用 ping 解析 "vote" 服务,如下所示,交替解析到 10.0.0.6 和 10.0.0.5
基于 DNS 负载均衡存在如下问题:
-
某些应用程序将 DNS 主机名缓存到 IP 地址映射,这会导致应用程序在映射更改时超时
-
具有非零 DNS ttl 值会导致 DNS 条目反映最新的详细信息时发生延迟
二、基于 VIP 的负载均衡(IPVS)--三层转发
克服了基于 DNS 负载均衡的一些问题。
在这种方法中,每个服务都有一个 IP 地址,并且该 IP 地址映射到与该服务关联的多个容器的 IP 地址。
在这种情况下,与服务关联的服务 IP 不会改变,即使与该服务关联的容器死亡并重新启动。
下图描述了基于 VIP 的负载均衡是如何工作的:
DNS server 会将服务名 "vote" 解析到 VIP,使用 iptables 和 ipvs,VIP 实现 2 个服务端 "vote" 容器的负载均衡。
查看这 2 个服务和它们的服务 IP:
-
[root@node1 ~]# docker service inspect --format {{.Endpoint.VirtualIPs}} vote[{tetug0isdx1gri62g7cfm889i 10.0.0.9/24}]
-
-
[root@node1 ~]# docker service inspect --format {{.Endpoint.VirtualIPs}} client[{tetug0isdx1gri62g7cfm889i 10.0.0.7/24}]
Service IP "10.0.0.9" 使用 Linux 内核的 iptables 和 IPVS 负载均衡到 2 个容器。
iptables 实现防火墙规则,IPVS 实现负载均衡。
为了证明这一点,我们需要使用 nsenter 进入容器的网络空间 (namespace)。为此,我们需要找到网络的命名空间。
[root@node2 ~]# cd /run/docker/netns/
[root@node2 netns]# ls 1-tetug0isdx 1-vyy22w04t6 be7330b99a27 d67fa9efb59e ingress_sbox
[root@node2 netns]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES43a789312e70 registry.cn-hangzhou.aliyuncs.com/anoy/vote:latest "gunicorn app:app -b…" 3 minutes ago Up 3 minutes 80/tcp vote.1.u46ms31e8zjdxtwrxvaec8zub
f3d1c4ef53f8 registry.cn-hangzhou.aliyuncs.com/anoy/ubuntu:latest "ping anoyi.com" 4 minutes ago Up 4 minutes client.1.ycox088aek5ajejezubwsjqf2
[root@node2 netns]# docker inspect f3d1c4ef53f8 | grep -i sandbox
"SandboxID": "be7330b99a274a03a7f58e9e991346dc6f048836a1682c7244a6068acbfb664c", "SandboxKey": "/var/run/docker/netns/be7330b99a27",
SandboxID 即为 "client" 容器的网络命名空间。
使用如下命令,我们就能够进入到 "client" 容器的网络命令空间:
nsenter --net=f3d1c4ef53f8 sh
下面,我们可以看到 iptables 的转发规则和 IPVS 输出:
-
sh-4.2# iptables -nvL -t mangle Chain OUTPUT (policy ACCEPT 606 packets, 50867 bytes)
-
pkts bytes target prot opt in out source destination
-
0 0 MARK all -- * * 0.0.0.0/0 10.0.0.7 MARK set 0x102
-
0 0 MARK all -- * * 0.0.0.0/0 10.0.0.9 MARK set 0x103
-
-
sh-4.2# ipvsadm IP Virtual Server version 1.2.1 (size=4096)
-
Prot LocalAddress:Port Scheduler Flags
-
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
-
FWM 258 rr
-
-> node2:0 Masq 1 0 0
-
FWM 259 rr
-
-> 10.0.0.10:0 Masq 1 0 0
-
-> 10.0.0.11:0 Masq 1 0 0
查看 vote 服务的 2 个容器的 IP 如下所示,即 VIP "10.0.0.9" 负载均衡到不同的容器实例:
-
[root@node2 netns]# docker inspect vote.1.u46ms31e8zjdxtwrxvaec8zub | grep IPv4
-
"IPv4Address": "10.0.0.10"[root@node1 ~]# docker inspect vote.2.tutj19i4iwu1xn7arsaq815cu | grep IPv4
-
"IPv4Address": "10.0.0.11"