前面介绍了Docker管理工具-Swarm部署记录,下面重点说下Swarm基于多主机容器通信的覆盖网络
在Docker版本1.12之后swarm模式原生支持覆盖网络(overlay networks),可以先创建一个覆盖网络,然后启动容器的时候启用这个覆盖网络,
这样只要是这个覆盖网络内的容器,不管在不在同一个宿主机上都能相互通信,即跨主机通信!不同覆盖网络内的容器组之间是相互隔离的(相互ping不通)。
swarm模式的覆盖网络包括以下功能:
1)可以附加多个服务到同一个网络。
2)默认情况下,service discovery为每个swarm服务分配一个虚拟IP地址(vip)和DNS名称,使得在同一个网络中容器之间可以使用服务名称为互相连接。
3)可以配置使用DNS轮循而不使用VIP
4)为了可以使用swarm的覆盖网络,在启用swarm模式之间你需要在swarm节点之间开放以下端口:
5)TCP/UDP端口7946 – 用于容器网络发现
6)UDP端口4789 – 用于容器覆盖网络
实例如下:
-----------在Swarm集群中创建overlay网络------------
[root@localhost ~]# docker network create --driver overlay --opt encrypted --subnet 10.10.19.0/16 ngx_net
i5czdn39hrkg0q38crxzrk8g5
参数解释:
–opt encrypted 默认情况下swarm中的节点通信是加密的。在不同节点的容器之间,可选的–opt encrypted参数能在它们的vxlan流量启用附加的加密层。
--subnet 命令行参数指定overlay网络使用的子网网段。当不指定一个子网时,swarm管理器自动选择一个子网并分配给网络。
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
f8fcec5c6ab0 bridge bridge local
5d04342a289b docker_gwbridge bridge local
0cae1d696f88 harbor_harbor bridge local
750da6f52800 host host local
nf8hr7thujhv ingress overlay swarm
i5czdn39hrkg ngx_net overlay swarm
383ddb3c7e8d none null local
由上可知,Swarm当中拥有2套覆盖网络。其中"ngx_net"网络正是我们在部署容器时所创建的成果。而"ingress"覆盖网络则为默认提供。
Swarm 管理节点会利用 ingress 负载均衡以将服务公布至集群之外。
在将服务连接到这个创建的网络之前,网络覆盖到manager节点。上面输出的SCOPE为 swarm 表示将服务部署到Swarm时可以使用此网络。
在将服务连接到这个网络后,Swarm只将该网络扩展到特定的worker节点,这个worker节点被swarm调度器分配了运行服务的任务。
在那些没有运行该服务任务的worker节点上,网络并不扩展到该节点。
------------------将服务连接到overlay网络-------------------
[root@localhost ~]# docker service create --replicas 5 --network ngx_net --name my-test -p 80:80 nginx
xt7hbixrb0ozxs7d8cyqqj4lq
overall progress: 5 out of 5 tasks
1/5: running [==================================================>]
2/5: running [==================================================>]
3/5: running [==================================================>]
4/5: running [==================================================>]
5/5: running [==================================================>]
verify: Service converged
上面名为"my-test"的服务启动了5个task,用于运行每个任务的容器都可以彼此通过overlay网络进行通信。Swarm集群将网络扩展到所有任务处于Running状态的节点上。
[root@localhost ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
xt7hbixrb0oz my-test replicated 5/5 nginx:latest *:80->80/tcp
在manager-node节点上,通过下面的命令查看哪些节点有处于running状态的任务:
[root@localhost ~]# docker service ps my-test
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
uvuon5zlsw56 my-test.1 nginx:latest localhost Running Running 7 minutes ago
wy6up5e0vgyc \_ my-test.1 nginx:latest hsp2 Shutdown Shutdown 7 minutes ago
b4rctcg0bwqo my-test.2 nginx:latest hsp1 Running Running 8 minutes ago
2q2mm1u2woey my-test.3 nginx:latest hsp2 Running Running 7 minutes ago
46c0ycahza3o \_ my-test.3 nginx:latest hsp2 Shutdown Shutdown 7 minutes ago
b09z0ac8lxn5 my-test.4 nginx:latest localhost Running Running 8 minutes ago
krq4k8zcnxu9 my-test.5 nginx:latest hsp1 Running Running 8 minutes ago
可见三个节点都有处于running状态的任务,所以my-network网络扩展到三个节点上。
可以查询某个节点上关于my-network的详细信息:
[root@localhost ~]# docker network inspect ngx_net
[
{
"Name": "ngx_net",
"Id": "i5czdn39hrkg0q38crxzrk8g5",
"Created": "2020-05-19T17:34:03.810709768+08:00",
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "10.10.19.0/16",
"Gateway": "10.10.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"abf3b461762907f5526b9909e6ad2d43a679394a66f311ae78a2ed772e440834": {
"Name": "my-test.4.b09z0ac8lxn5fiut07riv9rkn",
"EndpointID": "6c0caf990b389581afed6112d6cc3f597ef3a753509254b0503f252e47798092",
"MacAddress": "02:42:0a:0a:00:09",
"IPv4Address": "10.10.0.9/16",
"IPv6Address": ""
},
"c556d0b4800ba7e3748d3b44db094016ce88edc66eb3044e13ae9685b3379104": {
"Name": "my-test.1.uvuon5zlsw56c36ksrcjia555",
"EndpointID": "cd3134a2a278d68febbf07711986c687f5a1f2bbb88262ed8621734bcbf0481d",
"MacAddress": "02:42:0a:0a:00:0c",
"IPv4Address": "10.10.0.12/16",
"IPv6Address": ""
},
"lb-ngx_net": {
"Name": "ngx_net-endpoint",
"EndpointID": "331805a3d75b2ed3536105be65e3d378f80f01eab7d9f2770e0532c8ab24b64c",
"MacAddress": "02:42:0a:0a:00:03",
"IPv4Address": "10.10.0.3/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "4097",
"encrypted": ""
},
"Labels": {},
"Peers": [
{
"Name": "75e8d4c227ad",
"IP": "192.168.31.10"
},
{
"Name": "d25f52e1f4f2",
"IP": "192.168.31.136"
},
{
"Name": "b1fc9d6bf4a6",
"IP": "192.168.31.137"
}
]
}
]
从上面的信息可以看出在manager-node节点上,名为my-test的服务有一个名为my-test.1.uvuon5zlsw56c36ksrcjia555和
my-test.4.b09z0ac8lxn5fiut07riv9rkn连接到名为ngx_net的网络上(另外两个节点同样可以用上面命令查看)
[root@hsp2 ~]# docker network inspect ngx_net
[
{
"Name": "ngx_net",
"Id": "i5czdn39hrkg0q38crxzrk8g5",
"Created": "2020-05-19T17:34:03.832827626+08:00",
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "10.10.19.0/16",
"Gateway": "10.10.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"27b292006ebb3f18ed83016136c994d42bcd5de2e044d6c087cc9e5f7d802007": {
"Name": "my-test.2.55164ssl3hrx4bfum96947ibh",
"EndpointID": "2a04498496e3448ed0cc39ce1c76777d7f6020b55ee70c5e659b1bce7c3d0df5",
"MacAddress": "02:42:0a:0a:00:0d",
"IPv4Address": "10.10.0.13/16",
"IPv6Address": ""
},
"5319057785b2ac303597812a6ca6d1906505ffa7df8fb4d5db104e5c0127f48e": {
"Name": "my-test.3.2q2mm1u2woeywu1iffamd16xl",
"EndpointID": "5b941d033b899b63ee0294fac64d79348f4fd3049e1b4ad81381230f2bc55d06",
"MacAddress": "02:42:0a:0a:00:0b",
"IPv4Address": "10.10.0.11/16",
"IPv6Address": ""
},
"d3e66cdff911d5e63a20e30be010b379176d3eeea085f1b5b9ff28033c0da975": {
"Name": "my-test.5.i2slw15fa6aysup6kkkxk9ja7",
"EndpointID": "ad9a7d3c49e432b92761ada8a18c92894c583f84e27a4ec2cb8cf01a78647126",
"MacAddress": "02:42:0a:0a:00:0e",
"IPv4Address": "10.10.0.14/16",
"IPv6Address": ""
},
"lb-ngx_net": {
"Name": "ngx_net-endpoint",
"EndpointID": "7d7640449ad265276133aa962288fcb111e6b046bb853197cd8de5234b2c1165",
"MacAddress": "02:42:0a:0a:00:02",
"IPv4Address": "10.10.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "4097",
"encrypted": ""
},
"Labels": {},
"Peers": [
{
"Name": "b1fc9d6bf4a6",
"IP": "192.168.31.137"
},
{
"Name": "d25f52e1f4f2",
"IP": "192.168.31.136"
},
{
"Name": "75e8d4c227ad",
"IP": "192.168.31.10"
}
]
}
]
由上结果可知,10.10.0.5其实就是swarm集群内部的vip.
加入ngx_net网络的容器彼此之间可以通过IP地址通信,也可以通过名称通信。
```bash
[root@hsp2 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5319057785b2 nginx:latest "nginx -g 'daemon of…" 17 minutes ago Up 16 minutes 80/tcp my-test.3.2q2mm1u2woeywu1iffamd16xl
[root@hsp2 ~]# docker exec -it 5319057785b2 /bin/bash
root@5319057785b2:/# ifconfig
bash: ifconfig: command not found
root@5319057785b2:/# apt-get update
root@5319057785b2:/# apt-get install -y net-tools
root@5319057785b2:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.255.0.11 netmask 255.255.0.0 broadcast 10.255.255.255
ether 02:42:0a:ff:00:0b txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1424
inet 10.10.0.11 netmask 255.255.0.0 broadcast 10.10.255.255
ether 02:42:0a:0a:00:0b txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eth2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.0.4 netmask 255.255.0.0 broadcast 172.18.255.255
ether 02:42:ac:12:00:04 txqueuelen 0 (Ethernet)
RX packets 2369 bytes 8767222 (8.3 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2200 bytes 120261 (117.4 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1 (Local Loopback)
RX packets 6 bytes 536 (536.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 6 bytes 536 (536.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
root@5319057785b2:/# ping 10.10.0.9
bash: ping: command not found
root@5319057785b2:/# apt-get install -y iputils-ping
root@5319057785b2:/# ping 10.10.0.9
PING 10.10.0.9 (10.10.0.9) 56(84) bytes of data.
64 bytes from 10.10.0.9: icmp_seq=1 ttl=64 time=105 ms
64 bytes from 10.10.0.9: icmp_seq=2 ttl=64 time=0.496 ms
^C
--- 10.10.0.9 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 0.496/52.642/104.788/52.146 ms
root@5319057785b2:/# ping 10.10.0.12
PING 10.10.0.12 (10.10.0.12) 56(84) bytes of data.
64 bytes from 10.10.0.12: icmp_seq=1 ttl=64 time=26.4 ms
64 bytes from 10.10.0.12: icmp_seq=2 ttl=64 time=0.686 ms
^C
--- 10.10.0.12 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 2ms
rtt min/avg/max/mdev = 0.686/13.534/26.382/12.848 ms
root@5319057785b2:/#
----------------------------使用swarm模式的服务发现--------------------------
默认情况下,当创建了一个服务并连接到某个网络后,swarm会为该服务分配一个VIP。此VIP根据服务名映射到DNS。在网络上的容器共享该服务的DNS映射,
所以网络上的任意容器可以通过服务名访问服务。
在同一overlay网络中,不用通过端口映射来使某个服务可以被其它服务访问。Swarm内部的负载均衡器自动将请求发送到服务的VIP上,然后分发到所有的
active的task上。
如下示例:
在同一个网络中添加了一个nginx服务,此服务可以通过名称my-test访问前面创建的nginx服务:
[root@localhost ~]# docker service create --network ngx_net --name my-test2 nginx
ufwwkmrkzzwt68abpjogblly7
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
[root@localhost ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
xt7hbixrb0oz my-test replicated 5/5 nginx:latest *:80->80/tcp
ufwwkmrkzzwt my-test2 replicated 1/1 nginx:latest
查询nginx运行在哪个节点上(上面创建命令执行后,需要一段时间才能完成这个nginx服务的创建)
[root@localhost ~]# docker service ps my-test2
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
t4z8sfmlst04 my-test2.1 nginx:latest hsp1 Running Running 7 minutes ago
登录nginx运行的节点(由上可知是hsp1节点),打开nginx的交互shell:
[root@hsp1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d746be71a2e3 nginx:latest "nginx -g 'daemon of…" 8 minutes ago Up 8 minutes 80/tcp my-test2.1.t4z8sfmlst040oxm0hv70sr09
[root@node2 ~]# docker exec -ti my-centos.1.9yk5ie28gwk9mw1h1jovb68ki /bin/bash
root@d746be71a2e3:/# apt-get update
root@d746be71a2e3:/# apt-get install dnsutils
root@d746be71a2e3:/# nslookup my-test
Server: 127.0.0.11
Address: 127.0.0.11#53
Non-authoritative answer:
Name: my-test
Address: 10.10.0.5
从nginx容器内部,使用特殊查询 查询DNS,来找到my-test服务的所有容器的IP地址:
root@d746be71a2e3:/# nslookup tasks.my-test
Server: 127.0.0.11
Address: 127.0.0.11#53
Non-authoritative answer:
Name: tasks.my-test
Address: 10.10.0.13
Name: tasks.my-test
Address: 10.10.0.14
Name: tasks.my-test
Address: 10.10.0.12
Name: tasks.my-test
Address: 10.10.0.9
Name: tasks.my-test
Address: 10.10.0.11
从nginx容器内部,通过wget来访问my-test服务中运行的网页服务器
root@d746be71a2e3:/# apt-get install -y wget
root@d746be71a2e3:/# wget -O- my-test
--2020-05-19 10:58:23-- http://my-test/
Resolving my-test (my-test)... 10.10.0.5
Connecting to my-test (my-test)|10.10.0.5|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 612 [text/html]
Saving to: 'STDOUT'
- 0%[ ] 0 --.-KB/s <!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
- 100%[==============================================>] 612 --.-KB/s in 0.1s
2020-05-19 10:58:25 (5.38 KB/s) - written to stdout [612/612]
Swarm的负载均衡器自动将HTTP请求路由到VIP上,然后到一个active的task容器上。它根据round-robin选择算法将后续的请求分发到另一个active的task上。
-----------------------------------为服务使用DNS round-robin-----------------------------
在创建服务时,可以配置服务直接使用DNS round-robin而无需使用VIP。这是通过在创建服务时指定 --endpoint-mode dnsrr 命令行参数实现的。
当你想要使用自己的负载均衡器时可以使用这种方式。
如下示例(注意:使用DNS round-robin方式创建服务,不能直接在命令里使用-p指定端口)
[root@localhost ~]# docker service create --replicas 3 --name my-dnsrr-nginx --network ngx_net --endpoint-mode dnsrr nginx
o5uyghy9vsh3ilx9745jhuoy7
overall progress: 3 out of 3 tasks
1/3: running [==================================================>]
2/3: running [==================================================>]
3/3: running [==================================================>]
verify: Service converged
[root@localhost ~]# docker service ps my-dnsrr-nginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
vtuqgoqri9bh my-dnsrr-nginx.1 nginx:latest hsp1 Running Running about a minute ago
qcdl1bddlzgp my-dnsrr-nginx.2 nginx:latest localhost Running Running 15 seconds ago
w4u0va00d24c my-dnsrr-nginx.3 nginx:latest hsp2 Running Running 39 seconds ago
当通过服务名称查询DNS时,DNS服务返回所有任务容器的IP地址:
root@d746be71a2e3:/# nslookup my-dnsrr-nginx
Server: 127.0.0.11
Address: 127.0.0.11#53
Non-authoritative answer:
Name: my-dnsrr-nginx
Address: 10.10.0.149
Name: my-dnsrr-nginx
Address: 10.10.0.148
Name: my-dnsrr-nginx
Address: 10.10.0.150
需要注意的是:一定要确认VIP的连通性
通常Docker官方推荐使用dig,nslookup或其它DNS查询工具来查询通过DNS对服务名的访问。因为VIP是逻辑IP,ping并不是确认VIP连通性的正确的工具。