负载均衡是Swarm路由网格(service mesh
)中提供的一个功能,在Docker内部,利用Linux IPVS(一种内核第4层多协议负载均衡器)实现。
在【1.Swarm服务初识】文章中,通过docker swarm init
,我们初始化了一个具有2个节点(172.17.0.15, 172.17.0.16)的Swarm集群:
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
vq9zrem4w61cbp6g2cb8un6cz * host01 Ready Active Leader 18.03.1-ce
rq0mc2w41bbgcv3rqfhfavcbs host02 Ready Active 18.03.1-ce
并创建了一个名为http的服务,其中有两个容器正在运行,该服务通过80端口对外公开。默认情况下,对服务的请求基于公共端口进行负载平衡。
docker network create -d overlay --attachable httpnet
docker service create --name http --network httpnet --replicas 2 -p 80:80 nandy/show-host-info:v1
当在端口80上向集群中的节点发出请求时,它将在两个容器之间分配负载。curl 172.17.0.15:80
通过返回值我们可以看出是哪个容器处理的请求。
$ curl 172.17.0.15:80
{"version":"v1","hostname":"6bf3107d0fcc","address":"10.0.0.6"}
$ curl 172.17.0.15:80
{"version":"v1","hostname":"195f225a8e47","address":"10.0.0.5"}
Docker Swarm内置路由网格来实现分布式网络,使于不同节点上的容器通信就好像在一台主机上,Swarm通过创建专为云服务而设计的虚拟可扩展网络(VXLAN)来实现此。
路由以两种不同的方式工作。任何对于服务的暴露端口的请求都会被转发给服务的虚拟IP(VIP),VIP只会在Docker网络内部进行路由,最终路由到真正的容器IP。
服务在部署时,VIP、服务名已经注册于Docker内置的DNS服务器中,当根据服务名请求时,DNS服务器反向解析并返回VIP。Docker官方推荐用dig, nslookup
进行DNS域名解析查询。
运行docker run --name=dig --network httpnet --rm benhall/dig dig http
:
...
;; QUESTION SECTION:
;http. IN A
;; ANSWER SECTION:
http. 600 IN A 10.0.0.4
...
运行docker run --name=ping --network httpnet --rm alpine ping -c4 http
:
PING http (10.0.0.4): 56 data bytes
64 bytes from 10.0.0.4: seq=0 ttl=64 time=0.082 ms
64 bytes from 10.0.0.4: seq=1 ttl=64 time=0.090 ms
64 bytes from 10.0.0.4: seq=2 ttl=64 time=0.081 ms
64 bytes from 10.0.0.4: seq=3 ttl=64 time=0.082 ms
--- http ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.081/0.083/0.090 ms
运行docker inspect http
查看服务的VIP:
...
"VirtualIPs": [
...
{
"NetworkID": "w6qvnm5nkdvcnsykpttkceqdb",
"Addr": "10.0.0.4/24"
}
]
...
最终我们会发现,服务中容器的IP地址,都是在VIP的基础上依次增加。例如:VIP是10.0.0.4,则容器为10.0.0.5[6,7,8…],多开几个服务进行验证。
当服务进行扩展或者某些容器意外停止时,只要服务不死,对请求方来说,服务就是透明的,永远返回的是VIP
,而真正的容器地址被隐藏,由VIP进行维护和转发,这样就实现了动态服务发现。并且,Docker Swarm的所有节点都参与到ingress
路由网格,它允许集群中的所有节点接受已发布端口的连接,即使节点上没有任何任务运行,详见文章开始图片所示。