Docker Swarm 踩坑

2020 年 04 月 22 日 阅读数: 44
Docker Swarm 踩坑

解决 Swarm 服务副本不能在其他节点运行的问题

最近在读「深入浅出 Docker」,在学习 Docker Swarm 集群实现的时候掉坑里了,差点没爬出来,记录下。

挖坑

Docker Swarm 集群使用原生覆盖网络来实现多个主机之间的连接,Docker 提供了对原生覆盖网络的支持,是基于 Libnetwork 以及相应驱动来构建的,Libnetwork 是 CNM 的典型实现,从而可以通过插拔驱动的方式来实现不同的网络技术和拓扑结构。比如这里 Docker 就提供了 overlay 的原生驱动来实现覆盖网络。

我这里通过两台 CentOS7 虚拟机来实现 Swarm 的搭建。

在 node1 创建 Swarm

使用 init 来创建 Swarm,使其成为管理节点

docker swarm init

将 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 中所有节点

docker node ls

创建新的 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-mlkernel-lt,也就是 5.6.4 4.4.219,结果全都和刚才的错误一样。

无奈只能切换 Docker 版本,将最新版降到了和书中一样的版本,结果,还是不行。

怕了怕了,去睡了。

第二天,我开始重新梳理思绪,从提示信息开始。

既然 ERROR 提示:starting container failed: er...,那就是说明副本(容器)在 node2 节点上已经创建成功,只是启动的时候出问题了。

在 node2 节点中查看容器是否创建

docker container ls -a

可以看到容器已经创建,执行 docker container start xxxx 手动启动容器,xxxx 为容器 ID,启动失败,信息大体意思就是没有发现 overlay 网络,所以导致无法启动容器。

原因似乎在慢慢浮出水面,能够确定是 overlay 网络的问题了。

通过查询 docker overlay 资料和对比两个节点网络差异,发现了重要信息:docker_gwbridge

这个 docker_gwbridge 是什么东西呢?

docker_gwbridge 为使用多主机群覆盖网络的所有容器和任务提供默认网关功能。它是在每个Docker 主机上创建的,当 Docker 主机加入集群时创建。

docker_gwbridge 是一个本地桥接网络,在以下两种情况会自动创建:

  • 初始化或者加入一个 swarm 集群时,用来在不同 hosts 主机的不同节点间进行通信;

  • 在容器中的所有网络都不能访问外部时,Docker 会将 docker_gwbridge 网络加入到容器中,用来访问外部网络或者其他的集群节点。

也就是说,dockergwbridge 在 Swarm 集群中充当网关的角色,每个主机通过 dockergwbridge 进行网络连接。那上面创建的 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

yum install bridge-utils

查看 Linux 网桥:

$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242611d036cno
docker_gwbridge 8000.0242158020bfno veth4e013cb
vethe7c84dd
vethff947fe

可以看到 ifconfig 中列出的 docker0dockergwbridge 是 Linux 网桥,而 veth.... 则是 dockergwbridge 网桥的 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 之间的连接通讯也是通过 dockergwbridge

在 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 时就会创建 dockergwbridge,但不知为何没有创建,不过这是另一个问题了,日后再整。

现在根据 node1 管理节点中的 dockergwbridge 网桥信息,在 node2 工作节点中创建 dockergwbridge:

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 rm test

docker service create --name test --network uber-net --replicas 2 ubuntu sleep infinity

查看 test 服务详情:

docker service ps test

如图:

可以看到服务副本已在两个节点正常运行。

大功告成!

相关博文资料:

docker 修改gwbridge ip address

Docker 1.12 swarm模式下遇到的各种问题

Swarm使用原生的overlay网络

一种生产环境Docker Overlay Network的配置方案

用户头像

ikook

关注

万物之中,希望至美 2017.10.17 加入

非典型 95 后程序员,全平台各种技术折腾中...

评论

发布
暂无评论
Docker Swarm 踩坑