解决 Swarm 服务副本不能在其他节点运行的问题
最近在读「深入浅出 Docker」,在学习 Docker Swarm 集群实现的时候掉坑里了,差点没爬出来,记录下。
挖坑
Docker Swarm 集群使用原生覆盖网络来实现多个主机之间的连接,Docker 提供了对原生覆盖网络的支持,是基于 Libnetwork 以及相应驱动来构建的,Libnetwork 是 CNM 的典型实现,从而可以通过插拔驱动的方式来实现不同的网络技术和拓扑结构。比如这里 Docker 就提供了 overlay 的原生驱动来实现覆盖网络。
我这里通过两台 CentOS7 虚拟机来实现 Swarm 的搭建。
在 node1 创建 Swarm
使用 init
来创建 Swarm,使其成为管理节点
将 node2 加入 Swarm
将 node2 节点加入 Swarm 使其成为工作节点
注意,这里的 token 和 ip 需要修改成自己的
docker swarm join --token SWMTKN-1-3c69uwnwp6ijruxacv6ibjd37ml85k4sd3h14jcsus64ptjw1o-eqm34euiubbwlzme5poq70ib8 172.16.155.131:2377
可以在 node1 节点通过下面两条命令分别查询加入工作节点和管理节点的指令
工作节点:
docker swarm join-token worker
管理节点:
docker swarm join-token manager
查看节点列表
可以在管理节点执行如下命令查询 Swarm 中所有节点
创建新的 overlay 网络
在管理节点创建一个名为 uber-net
的 overlay 网络
docker network create -d overlay uber-net
可以通过 docker network ls
列出管理节点上所有的网络
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
eb2ea6efa97c bridge bridge local
d7bce6256c50 docker_gwbridge bridge local
5c78a9e4259c host host local
91qhb0sad14d ingress overlay swarm
d9f858f03165 none null local
om9znulbmdei prod-overlay overlay swarm
xe42rvru0nxn uber-net overlay swarm
可以看到列表中有一个名为 uber-net
的 overlay 网络被创建出来了。
注意,这时在 node2 节点上执行 docker network ls
列出的网络中,并不包含 uber-net
。这时因为 Swarm service 在 node2 上的副本容器连接到该网络时,该网络才会变为可用状态。
将服务连接到 overlay 网络
在管理节点中创建新的服务,并将服务连接到 overlay 网络。该服务会包含两个服务副本(容器)。理论上,应该是一个运行于 node1 节点,一个运行于 node2 节点。同时,会自动将 node2 节点接入 uber-net
网络。
创建服务:
docker service create --name test --network uber-net --replicas 2 ubuntu sleep infinity
这里使用 ubuntu 镜像创建了一个名为 test 的服务。
如果一切正常,两个副本(容器)应当运行于两个主机节点,也就是 node1 一个 node2 一个。
使用 docker service ps test
查看服务详情:
命运就是如此,总不是一帆风顺。
可见,服务副本在 node2 上启动失败,也就是图中的 docker2.localdomain。由于在 node2 中启动失败,则两个服务副本就最终全部创建运行在 node1 管理节点中了。
到此,挖坑结束。
填坑
排查了很久,把书翻了一遍又一遍,没看出什么问题来。
通过 Google 查询,很多人都说是 overlay vxlan 的网络驱动问题,由于主机内核版本太低造成。OK,那就升级内核,将内核分别升级到了 kernel-ml
和 kernel-lt
,也就是 5.6.4
和 4.4.219
,结果全都和刚才的错误一样。
无奈只能切换 Docker 版本,将最新版降到了和书中一样的版本,结果,还是不行。
怕了怕了,去睡了。
第二天,我开始重新梳理思绪,从提示信息开始。
既然 ERROR 提示:starting container failed: er...
,那就是说明副本(容器)在 node2 节点上已经创建成功,只是启动的时候出问题了。
在 node2 节点中查看容器是否创建
可以看到容器已经创建,执行 docker container start xxxx
手动启动容器,xxxx
为容器 ID,启动失败,信息大体意思就是没有发现 overlay 网络,所以导致无法启动容器。
原因似乎在慢慢浮出水面,能够确定是 overlay 网络的问题了。
通过查询 docker overlay 资料和对比两个节点网络差异,发现了重要信息:docker_gwbridge
这个 docker_gwbridge
是什么东西呢?
docker_gwbridge
为使用多主机群覆盖网络的所有容器和任务提供默认网关功能。它是在每个Docker 主机上创建的,当 Docker 主机加入集群时创建。
docker_gwbridge
是一个本地桥接网络,在以下两种情况会自动创建:
也就是说,dockergwbridge
在 Swarm 集群中充当网关的角色,每个主机通过 docker
gwbridge
进行网络连接。那上面创建的 ubet-net
网络是怎么回事呢?
在 node1 管理节点中:
通过 docker network ls
查看网络
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
eb2ea6efa97c bridge bridge local
d7bce6256c50 docker_gwbridge bridge local
5c78a9e4259c host host local
91qhb0sad14d ingress overlay swarm
d9f858f03165 none null local
om9znulbmdei prod-overlay overlay swarm
xe42rvru0nxn uber-net overlay swarm
通过 ifconfig
查看系统网卡信息
$ ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:61:1d:03:6c 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
docker_gwbridge: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.0.1 netmask 255.255.0.0 broadcast 172.18.255.255
inet6 fe80::42:15ff:fe80:20bf prefixlen 64 scopeid 0x20<link>
ether 02:42:15:80:20:bf txqueuelen 0 (Ethernet)
......
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.155.131 netmask 255.255.255.0 broadcast 172.16.155.255
inet6 fe80::a278:13ed:103a:28c8 prefixlen 64 scopeid 0x20<link>
......
.....
vethe7c84dd: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::9cdb:61ff:fe36:5793 prefixlen 64 scopeid 0x20<link>
ether 9e:db:61:36:57:93 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
......
vethff947fe: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::d430:f8ff:fe7b:6f52 prefixlen 64 scopeid 0x20<link>
ether d6:30:f8:7b:6f:52 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
......
其中 veth....
则是在创建 docker 网络的时候系统上对应创建的网卡。
这些 veth....
网卡其实是 docker 在主机内核中创建的 Linux 网桥。它和 docker 网络是一对一的。
可以通过使用 Linux brctl 工具来查看系统中的 Linux 网桥。
安装 brctl
:
查看 Linux 网桥:
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242611d036cno
docker_gwbridge 8000.0242158020bfno veth4e013cb
vethe7c84dd
vethff947fe
可以看到 ifconfig
中列出的 docker0
和 dockergwbridge
是 Linux 网桥,而 veth....
则是 docker
gwbridge
网桥的 interface。这三个 veth...
对正好对应 docker 网络中的三个 overlay 网络
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
eb2ea6efa97c bridge bridge local
d7bce6256c50 docker_gwbridge bridge local
5c78a9e4259c host host local
91qhb0sad14d ingress overlay swarm
d9f858f03165 none null local
om9znulbmdei prod-overlay overlay swarm
xe42rvru0nxn uber-net overlay swarm
也就是说 overlay 网络是作为 dockergwbridge
网桥的接口的,overlay 之间的连接通讯也是通过 docker
gwbridge
。
在 node2 节点中,查看网卡信息,发现并没有 docker_gwbridge
的存在:
$ ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:97:65:2c:a5 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
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.155.132 netmask 255.255.255.0 broadcast 172.16.155.255
inet6 fe80::2420:d07c:f270:3e77 prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:f5:ce:df txqueuelen 1000 (Ethernet)
RX packets 38582 bytes 4063671 (3.8 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 46242 bytes 5289150 (5.0 MiB)
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
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 69 bytes 6228 (6.0 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 69 bytes 6228 (6.0 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
到这儿,原因就明确了,是 dockergwbridge
没有创建的问题。按说 Docker Swarm 在初始化之后工作节点在加入 Swarm 时就会创建 docker
gwbridge
,但不知为何没有创建,不过这是另一个问题了,日后再整。
现在根据 node1 管理节点中的 dockergwbridge
网桥信息,在 node2 工作节点中创建 docker
gwbridge
:
docker network create --subnet 172.18.0.0/16 --gateway 172.18.0.1 -o com.docker.network.bridge.enableicc=false -o com.docker.network.bridge.name=dockergwbridge docker_gwbridge
之后删除服务,再重新创建
docker service create --name test --network uber-net --replicas 2 ubuntu sleep infinity
查看 test
服务详情:
如图:
可以看到服务副本已在两个节点正常运行。
大功告成!
相关博文资料:
docker 修改gwbridge ip address
Docker 1.12 swarm模式下遇到的各种问题
Swarm使用原生的overlay网络
一种生产环境Docker Overlay Network的配置方案
评论