继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Docker Swarm 服务发现和负载均衡原理

www说
关注TA
已关注
手记 439
粉丝 83
获赞 493

 本文将介绍基于 DNS 的负载均衡、基于 VIP 的负载均衡和路由网格(Routing Mesh)。

使用的技术

Docker 使用了 Linux 内核 iptables 和 IPVS 的功能来实现服务发现和负载均衡。

  • iptables 是 Linux 内核中可用的包过滤技术,它可用于根据数据包的内容进行分类、修改和转发决策。

  • IPVS 是 Linux 内核中可用的传输级负载均衡器。

准备工作

  • swarm 集群:【Manager】node1、【Worker】node2

  • 客户端镜像: registry.cn-hangzhou.aliyuncs.com/anoy/ubuntu

  • 服务端镜像: registry.cn-hangzhou.aliyuncs.com/anoy/vote

https://img.mukewang.com/5d5e79d60001338306520380.png

如图所示,我们将在 swarm 集群中部署 “client” 服务 和 “vote” 服务,其中 “vote” 服务部署多个副本。客户端请求 “vote” 服务时,输出结果中包含服务端的容器 ID,这样就更方便演示网络请求。

集群状态

[root@node1 ~]# docker node lsID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSIONrnr2i1y2of3n5vy2vzh2vkzq0 *   node1               Ready               Active              Leader              18.03.1-ceqvik057dvphx5s06evmswahaf     node2               Ready               Active                                  18.03.1-ce

使用如下命令,创建 overlay 网络:

docker network create --driver overlay overlay1

基于 DNS 的负载均衡

下图描述了基于 DNS 的负载均衡是如何工作的:

https://img.mukewang.com/5d5e79db000145be08600363.png

DNS server 内嵌于 Docker 引擎。Docker DNS 解析服务名 “vote” 并返回容器 ID 地址列表(随机排序)。客户端通常会挑第一个 IP 访问,因此负载均衡可能发生在服务器的不同实例之间。

使用如下命令创建 2 个基于 DNS 负载均衡的服务 “client” 、 “vote”:

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

查看服务信息:

[root@node1 ~]# docker service lsID                  NAME                MODE                REPLICAS            IMAGE                                                  PORTS2mrj3pqyioc3        client              replicated          1/1                 registry.cn-hangzhou.aliyuncs.com/anoy/ubuntu:latest826s79tsixuh        vote                replicated          2/2                 registry.cn-hangzhou.aliyuncs.com/anoy/vote:latest

[root@node1 ~]# docker service ps clientID                  NAME                IMAGE                                                  NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
f74i688vbh12        client.1            registry.cn-hangzhou.aliyuncs.com/anoy/ubuntu:latest   node2               Running             Running 2 minutes ago

[root@node1 ~]# docker service ps voteID                  NAME                IMAGE                                                NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS7iiuzl2a63hy        vote.1              registry.cn-hangzhou.aliyuncs.com/anoy/vote:latest   node1               Running             Running 47 seconds ago
uyhxxqfdima7        vote.2              registry.cn-hangzhou.aliyuncs.com/anoy/vote:latest   node2               Running             Running about a minute ago

可以看出 "client" 运行于 node2,在 node2 上进入 client 容器,使用 dig 来解析服务名 "vote",如下所示,"vote" 解析到 10.0.0.6 和 10.0.0.5

[root@node2 ~]# docker psCONTAINER ID        IMAGE                                                  COMMAND                  CREATED              STATUS              PORTS               NAMES1eed67d37cbb        registry.cn-hangzhou.aliyuncs.com/anoy/vote:latest     "gunicorn app:app -b…"   About a minute ago   Up About a minute   80/tcp              vote.2.uyhxxqfdima7smos5pki84wul436702b21a1c        registry.cn-hangzhou.aliyuncs.com/anoy/ubuntu:latest   "ping anoyi.com"         3 minutes ago        Up 3 minutes                            client.1.f74i688vbh12on8oniufht633

[root@node2 ~]# docker exec -it 436702b21a1c /bin/bashroot@436702b21a1c:/# dig vote;; ANSWER SECTION:vote.           600 IN  A   10.0.0.5vote.           600 IN  A   10.0.0.6

使用 ping 解析 "vote" 服务,如下所示,交替解析到 10.0.0.6 和 10.0.0.5

root@436702b21a1c:/# ping -c1 votePING vote (10.0.0.6) 56(84) bytes of data.64 bytes from vote.2.uyhxxqfdima7smos5pki84wul.overlay1 (10.0.0.6): icmp_seq=1 ttl=64 time=0.087 ms

root@436702b21a1c:/# ping -c1 votePING vote (10.0.0.5) 56(84) bytes of data.64 bytes from vote.1.7iiuzl2a63hyj084qgufc175v.overlay1 (10.0.0.5): icmp_seq=1 ttl=64 time=0.767 ms

如果使用 curl,如下所示,请求也能解析到不同的容器

root@436702b21a1c:/# curl vote  | grep -i "container id"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed100  3162  100  3162    0     0   7542      0 --:--:-- --:--:-- --:--:--  7546
          Processed by container ID 9b42319d4f13

root@436702b21a1c:/# curl vote  | grep -i "container id"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed100  3162  100  3162    0     0   452k      0 --:--:-- --:--:-- --:--:--  514k
          Processed by container ID 1eed67d37cbb

基于 DNS 负载均衡存在如下问题:

  • 某些应用程序将 DNS 主机名缓存到 IP 地址映射,这会导致应用程序在映射更改时超时

  • 具有非零 DNS ttl 值会导致 DNS 条目反映最新的详细信息时发生延迟

基于 VIP 的负载均衡

基于 VIP 的负载均衡克服了基于 DNS 负载均衡的一些问题。在这种方法中,每个服务都有一个 IP 地址,并且该 IP 地址映射到与该服务关联的多个容器的 IP 地址。在这种情况下,与服务关联的服务 IP 不会改变,即使与该服务关联的容器死亡并重新启动。

下图描述了基于 VIP 的负载均衡是如何工作的:

https://img3.mukewang.com/5d5e79e00001f67308740229.png

DNS server 会将服务名 "vote" 解析到 VIP,使用 iptables 和 ipvs,VIP 实现 2 个服务端 "vote" 容器的负载均衡。

使用如下命令创建 2 个 VIP 模式的服务 “client” 、 “vote”:

docker service create --replicas 1 --name client --network overlay1 registry.cn-hangzhou.aliyuncs.com/anoy/ubuntu ping anoyi.com

docker service create --name vote --network overlay1 --replicas 2 registry.cn-hangzhou.aliyuncs.com/anoy/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}]

在 "client" 的容器中使用如下命令,可以看到服务名 "vote" 映射到 VIP "10.0.0.9"

[root@node2 ~]# docker exec -it f3d1c4ef53f8 /bin/bashroot@f3d1c4ef53f8:/# dig vote;; ANSWER SECTION:vote.           600 IN  A   10.0.0.9

Service IP "10.0.0.9" 使用 Linux 内核的 iptables 和 IPVS 负载均衡到 2 个容器。iptables 实现防火墙规则,IPVS 实现负载均衡。为了证明这一点,我们需要使用 nsenter 进入容器的网络空间 (namespace)。为此,我们需要找到网络的命名空间。

如下是 node2 上的网络命名空间:

[root@node2 ~]# cd /run/docker/netns/[root@node2 netns]# ls1-tetug0isdx  1-vyy22w04t6  be7330b99a27  d67fa9efb59e  ingress_sbox

前 2 个命名空间是用于 overlay 网络,后面的用于容器。下面的命令用于找到 "client" 容器的网络命名空间:

[root@node2 netns]# docker psCONTAINER 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 mangleChain 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# ipvsadmIP 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

Service IP "10.0.0.9" 使用 iptables OUTPUT 链获得标记 0x103 (十六进制 -> 十进制:259),然后 IPVS 使用此标记并将它负载均衡到 "10.0.0.10" 和 "10.0.0.11" 。

查看 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"

进入 client 服务的容器,使用 curl 请求 vote 服务,输出结果如下,即请求分发到不同的容器:

root@f3d1c4ef53f8:/# curl vote | grep -i "container id"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed100  3162  100  3162    0     0  14409      0 --:--:-- --:--:-- --:--:-- 14438
          Processed by container ID c2af209c4e90root@f3d1c4ef53f8:/# curl vote | grep -i "container id"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed100  3162  100  3162    0     0   165k      0 --:--:-- --:--:-- --:--:--  171k
          Processed by container ID 43a789312e70

路由网格 (Routing mesh)

使用路由网格,服务暴露的端口会暴露在 Swarm 集群中的所有工作节点。Docker 是通过创建 "ingress" overlay 网络来实现这一点的,所有节点默认使用内在的 sandbox 网络命名空间成为 "ingress" overlay 网络的一部分。

下图描述了 Routing mesh 如何实现负载均衡的:

https://img.mukewang.com/5d5e79e6000115c308480256.png

首先,会将 Hostname 或 IP 映射到 Sandbox IP,Sandbox 中的 iptables 和 IPVS 负责将请求负载均衡到 2 个 vote 容器。Ingress sandbox 网络命名空间驻留在 swarm 集群中的所有工作节点,它通过将主机映射的端口负载均衡到后端容器来协助路由网格功能。

使用如下命令创建 vote 服务,使用路由网格暴露端口到所有节点:

docker service create --name vote --network overlay1 --replicas 2 -p 8080:80 registry.cn-hangzhou.aliyuncs.com/anoy/vote

下图显示了 Sandbox、容器和每个节点的网络之间的映射关系:

https://img.mukewang.com/5d5e79ea00015f8008450414.png

如图所示,Sandbox 和 vote 容器是 "ingress" 网络的一部分,它有助于路由网格。client 容器和 vote 容器是 "overlay1" 网络的一部分,它有助于内部负载均衡。所有容器都是默认 "docker_gwbridge" 网络的一部分。

遵循 iptables 中的 NAT 规则显示,端口 8080 上的主机流量发送到 node1 里的 Sandbox:

[root@node1 ~]# iptables -nvL -t natChain DOCKER-INGRESS (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:8080 to:172.18.0.2:8080
  315 18876 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0

进入 node1 上的 Sandbox 网络命名空间  (ingress_sbox),查看 iptables 的转发规则和 IPVS 输出:

[root@node1 netns]# nsenter --net=ingress_sbox shsh-4.2# iptables -nvL -t mangleChain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 MARK       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:8080 MARK set 0x105

sh-4.2# ipvsadmIP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
FWM  261 rr
  -> 10.255.0.5:0                 Masq    1      0          0
  -> 10.255.0.6:0                 Masq    1      0          0

端口 8080 标记为 0x105 (十六进制 -> 十进制:261),IPVS 使用此标记将它负载均衡到 "10.255.0.5" 和 "10.255.0.6" 。

查看 vote 服务的 2 个容器的 IP 如下所示,即主机端口 8080 的流量会负载均衡到不同的容器实例:

[root@node1 netns]# docker inspect 6173afd5fab8 | grep IPv4
                        "IPv4Address": "10.255.0.6"
                        "IPv4Address": "10.0.0.14"[root@node2 ~]# docker inspect b07e95c5c681 | grep IPv4
                        "IPv4Address": "10.255.0.5"
                        "IPv4Address": "10.0.0.13"

验证负载均衡,在 node1 上通过 node2 的 IP 和 8080 端口请求 vote 服务:

[root@node1 netns]# curl node2:8080 | grep -i "container id"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed100  3162  100  3162    0     0   199k      0 --:--:-- --:--:-- --:--:--  192k
          Processed by container ID 6173afd5fab8

[root@node1 netns]# curl node2:8080 | grep -i "container id"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed100  3162  100  3162    0     0   7551      0 --:--:-- --:--:-- --:--:--  7546
          Processed by container ID b07e95c5c681

在 node2 上通过 node1 的 IP 和 8080 端口请求 vote 服务:

[root@node2 ~]# curl node1:8080 | grep -i "container id"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed100  3162  100  3162    0     0   7531      0 --:--:-- --:--:-- --:--:--  7546
          Processed by container ID 6173afd5fab8

[root@node2 ~]# curl node1:8080 | grep -i "container id"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed100  3162  100  3162    0     0   169k      0 --:--:-- --:--:-- --:--:--  171k
          Processed by container ID b07e95c5c681

         

             


作者:Anoyi
链接:https://www.jianshu.com/p/dba9342071d8

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP