环境说明:
docker版本:20.10.3
nacos版本:1.3,而nacos镜像是基于此版本自己制作的,并没有拉取官方的镜像。
如果我们的组件集成了nacos,那么服务之间的调用都是采用服务名,来进行服务间的通信。所有的服务启动的时候会向nacos注册自己的地址。我们假设有下面的服务:
服务A---pmadmin
服务B---gateway
服务C---athena
问题描述
容器内的服务A在启动的时候,需要去访问服务gateway进行路由注册,结果出现连接超时,报错日志:
从日志中,看出,服务A-pmadmin 取访问 gateway,实际是先去nacos拿到了gateway的地址:10.0.0.7:65,然后通过此地址访问gateway,但是出现了连接超时,日志如下:
问题排查
去nacos查询gateway的地址,发现确实是10.0.0.7:65,地址是没有问题,那是不是网络的问题?
查看docker网络
docker network ls
NETWORK ID NAME DRIVER SCOPE
52561b34842f bridge bridge local
0f0904683e30 docker_gwbridge bridge local
3273b79adf49 host host local
yuggnzybujkt iam_default overlay swarm
1yl193gp08xc ingress overlay swarm
45c9f5d126c9 none null local
iam_default这个网络是我启动iam 这个stack时,自动创建的网络,我们看下这个网络中有哪些容器
docker network inspect iam_default
[
{
"Name": "iam_default",
"Id": "yuggnzybujktpkkutkf0110pu",
"Created": "2021-03-10T16:52:56.364889741+08:00",
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "10.0.1.0/24",
"Gateway": "10.0.1.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
......
"c4d4ce1fc5330d791b8fbdf2869bfc40bd64535f5ae6d5ad51531227c5c87472": {
"Name": "iam_nacosserver.1.o8ccif1ktcaoh7pj21qnrsyql",
"EndpointID": "076e8d35347afe3fb5980629837d2008d1d9affe138e5dcb07c1646e4f4008c1",
"MacAddress": "02:42:0a:00:01:06",
"IPv4Address": "10.0.1.6/24",
"IPv6Address": ""
},
"fae43956f124d19248d7d00dfc1be4bb615ccde82b5875c15f8ffa4193ece218": {
"Name": "iam_gateway.1.z40ua51l1wlhrsxdol1v35o32",
"EndpointID": "8fc8443b9f7d19d5cb4854b6424c4178dcabf92fd7a4a955fc908dafe820f859",
"MacAddress": "02:42:0a:00:01:0c",
"IPv4Address": "10.0.1.12/24",
"IPv6Address": ""
},
"lb-iam_default": {
"Name": "iam_default-endpoint",
"EndpointID": "f5ecad6250919d7730a3d4bb588eb2d710d673964dd839052e2d89644cbf52a6",
"MacAddress": "02:42:0a:00:01:04",
"IPv4Address": "10.0.1.4/24",
"IPv6Address": ""
}
注意到iam_default这个网络中的所有容器都在10.0.1.0/24这个网段中,并且里面有个gateway的地址为10.0.1.12,省略其他无关的容器详情。
问题来了,那10.0.0.7这个地址也是gateway的地址啊,这个是从nacos的页面确认的
我们知道,swarm集群正常工作需要两个overlay网络,在现在这个环境里,除了iam_default之外,还有一个ingress的网络,这个网络的主要目的是将外部流量导到我们的集群服务,也就是iam-default网络的服务。
那确认一下10.0.0.7这个地址是属于 ingress网络的。查看ingress网络中的容器地址:发现所有的容器 在 10.0.0.0/24 这个网络中
docker network inspect ingress
[
{
"Name": "ingress",
"Id": "1yl193gp08xc48ogm5l2qnf8q",
"Created": "2021-03-10T16:52:56.364392994+08:00",
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "10.0.0.0/24",
"Gateway": "10.0.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": true,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"344903fbfd671c51bbe4ca379a29ddcdd1b970555229caca57fdb89a8b7fe56c": {
"Name": "iam_pmadmin.1.oqtdd8wo0rhff813a2fjjmtoq",
"EndpointID": "f2fc565d0ceb7df1fe80a031523e324a2bef33f909d2faa5ad3f58af6f9f2f36",
"MacAddress": "02:42:0a:00:00:c9",
"IPv4Address": "10.0.0.201/24",
"IPv6Address": ""
},
"c4d4ce1fc5330d791b8fbdf2869bfc40bd64535f5ae6d5ad51531227c5c87472": {
"Name": "iam_nacosserver.1.o8ccif1ktcaoh7pj21qnrsyql",
"EndpointID": "7582d63054303734363d7b9d8dd7d69d5a733ca31cf39b00aaafcc1968a8c5d0",
"MacAddress": "02:42:0a:00:00:04",
"IPv4Address": "10.0.0.4/24",
"IPv6Address": ""
},
"fae43956f124d19248d7d00dfc1be4bb615ccde82b5875c15f8ffa4193ece218": {
"Name": "iam_gateway.1.z40ua51l1wlhrsxdol1v35o32",
"EndpointID": "0e08b11fe06750edb5252b757f464aa07dbb672976066bb69c69917b253468b3",
"MacAddress": "02:42:0a:00:00:07",
"IPv4Address": "10.0.0.7/24",
"IPv6Address": ""
},
"ingress-sbox": {
"Name": "ingress-endpoint",
"EndpointID": "e0845b4d04150dfda8bb30259a2d2c9516e55b0716bdbc7c03c467e4bceb0395",
"MacAddress": "02:42:0a:00:00:02",
"IPv4Address": "10.0.0.2/24",
"IPv6Address": ""
}
我们确实发现了10.0.0.7这个地址是gateway的。所以,10.0.1.12和10.0.0.7是swarm网络中 不同网络的地址,也就是说nacos客户端采集的地址是ingress网络中的地址,也就是10.0.0.7这个地址。
那为什么服务A去访问服务B不通呢?难道是网络不通。
由于服务A没有启动,无法进入容器内部,我们进入此集群中另外一个容器c-athena 内部,看看这个容器的网络是什么样的情况?
cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.0.1.8 athena
发现athena这个服务的地址10.0.1.8 此地址恰好在iam-default这个网络中,也就是说容器里的ip地址,采用的是集群服务网络,而不是ingress网络,这也是说的通的,ingress网络主要是用来流量导向的。
在此容器内部,我们看下和ingress网络的联通性
# ping 10.0.0.7
PING 10.0.0.7 (10.0.0.7) 56(84) bytes of data.
64 bytes from 10.0.0.7: icmp_seq=1 ttl=64 time=0.144 ms
64 bytes from 10.0.0.7: icmp_seq=2 ttl=64 time=0.075 ms
64 bytes from 10.0.0.7: icmp_seq=3 ttl=64 time=0.080 ms
64 bytes from 10.0.0.7: icmp_seq=4 ttl=64 time=0.077 ms
^C
--- 10.0.0.7 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3001ms
rtt min/avg/max/mdev = 0.075/0.094/0.144/0.028 ms
说明iam_default网络中的容器,访问ingress网络中的gateway容器地址可以联通。
那我们再ping 一下gateway,看下到底返回哪个地址
# ping gateway
PING gateway (10.0.1.12) 56(84) bytes of data.
64 bytes from iam_gateway.1.z40ua51l1wlhrsxdol1v35o32.iam_default (10.0.1.12): icmp_seq=1 ttl=64 time=0.301 ms
64 bytes from iam_gateway.1.z40ua51l1wlhrsxdol1v35o32.iam_default (10.0.1.12): icmp_seq=2 ttl=64 time=0.074 ms
64 bytes from iam_gateway.1.z40ua51l1wlhrsxdol1v35o32.iam_default (10.0.1.12): icmp_seq=3 ttl=64 time=0.065 ms
64 bytes from iam_gateway.1.z40ua51l1wlhrsxdol1v35o32.iam_default (10.0.1.12): icmp_seq=4 ttl=64 time=0.075 ms
iam_default网络中的所有地址是互通的,都是在10.0.1.0/24网络中的。gateway的地址是10.0.1.12,这也说明了,容器内部的ipvs服务也是正常工作的。
以上实验说明:
ingress网络和iam-default网络互通,
容器内部的地址采用的是iam-default这个集群服务网络中地址,在10.0.1.0/24网段中。
所以最开始的那个问题,连接超时实际是
10.0.1.0/24中的某个地址去访问10.0.0.7:65这个服务没有连接成功。
实验
那么直接通过iam_default网络访问gateway能否成功呢?
经过实验发现,修改服务A配置,让其直接访问iam-default中gateway的地址10.0.1.12:65,
也就是说将 https://gateway/xxx改为 https://10.0.1.12:65/xxx
这样是可以正常通信的:
同样,将gateway的地址的地址改为 物理机ip也是正常启动的。
问题来了:
- 容器访问ingress(10.0.0.0/24)不行,访问iam_default(10.0.1.0/24)是可以的,但是又是可以ping的,那么为什么网络互通的情况下,到了tcp协议这一层就不通了呢?
- nacos客户端采集的地址是 ingress网络中的,为什么不是iam-default网络中的。
问题解决
查阅nacos的资料,发现可以让客户端自定义注册的地址,因为一般容器中会有多个网卡,nacos采集的地址不一定就是我们想要的那个,所以如何让客户端采集某个网卡的地址向服务器注册呢?可以参见下面的解决方案,确实解决我遇到的问题。
如何让nacos中服务注册的ip是自定义的网段ip ( docker swarm )
好,采用上面方法解决了问题,那遗留的问题就是为啥:同一网络中的容器互相访问的时候,通过ingress网络无法通信(即使可以ping通ingress网络中的地址),现在的猜想如下:
所谓的端口暴露,应该是物理机和集群网络(而不是ingress)之间的映射,容器访问ingress网络中的某个地址时,无法根据端口做转发??
扩展知识
在服务c-athena容器内部,查看网卡信息
# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
37646: eth0@if37647: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether 02:42:0a:00:01:08 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.0.1.8/24 brd 10.0.1.255 scope global eth0
valid_lft forever preferred_lft forever
37658: eth2@if37659: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:13:00:08 brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet 172.19.0.8/16 brd 172.19.255.255 scope global eth2
valid_lft forever preferred_lft forever
37680: eth1@if37681: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether 02:42:0a:00:00:05 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet 10.0.0.5/24 brd 10.0.0.255 scope global eth1
valid_lft forever preferred_lft forev
eth0:10.0.1.8 ——iam_default网络
eth1: 10.0.0.5 —— ingress网络
eth2: 172.19.0.8 ——docker_gwbridge网络
前面两个都已经证实了,证实一下eth2网络,在主机上执行一下命令,发现,docker_gwbridge网络中的容器地址都在172.19.0.0/24网中,
docker network inspect docker_gwbridge |grep Gateway
"Gateway": "172.19.0.1"
所以,以上三个网卡分别对应三个网络,根据不同ip走不同的网卡,流量默认走docker_gwbridge网络
# ip route
default via 172.19.0.1 dev eth2
10.0.0.0/24 dev eth1 proto kernel scope link src 10.0.0.5
10.0.1.0/24 dev eth0 proto kernel scope link src 10.0.1.8
172.19.0.0/16 dev eth2 proto kernel scope link src 172.19.0.8
在主机上查看路由,也有三个网络,默认走ens192网卡,看到最后一个docker_gwbridge,负责172.19.0.0网段。所以容器也可以通过docker_gwbridge和主机通信。
ip route
default via 10.92.4.1 dev ens192 proto static metric 100
10.92.4.0/24 dev ens192 proto kernel scope link src 10.92.4.126 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.19.0.0/16 dev docker_gwbridge proto kernel scope link src 172.19.0.1
更多的docker网络知识:
https://blog.neuvector.com/article/docker-swarm-container-networking