Docker Swarm 无法通过 Python Celery Worker 连接到 RabbitMQ Broker 的服务名称解析 DNS,导致连接超时

Docker Swarm Failing to Resolve DNS by Service Name With Python Celery Workers Connecting to RabbitMQ Broker Resulting in Connection Timeout

设置

我已经 Docker 安装并连接了 9 台机器,1 台管理器和 8 个工作节点,使用 Docker 群。这种安排已经在我们的开发服务器中使用了 ~5 年了。

我正在使用它来启动一个使用 Celery 的任务队列 Python。 Celery 使用 RabbitMQ 作为其代理,使用 Redis 作为结果后端。

我在 Docker 中创建了一个覆盖网络,这样我所有由 Docker swarm 启动的 Celery worker 都可以通过名称引用他们的代理和结果后端;即,rabbitmq 或 redis,而不是 IP 地址。该网络由 运行 以下命令创建:

docker network create -d overlay <network_name>

RabbitMQ 服务和 Redis 服务已使用以下命令在此覆盖网络下的管理器节点上启动:

docker service create --network <my_overlay_network> --name redis --constraint "node.hostname==manager" redis

docker service create --network <my_overlay_network> --name rabbitmq --constraint "node.hostname==manager" rabbitmq

这两个都启动后,我使用以下命令在同一个覆盖网络上部署我的 Celery worker,每个 Docker swarm worker 节点一个:

docker service create --network <my_overlay_network> --name celery-worker --constraint "node.hostname!=manager" --replicas 8 --replicas-max-per-node 1 <my_celery_worker_image>

在有人提出建议之前,是的,我知道我应该使用 Docker 撰写文件来启动所有这些。我目前正在测试,等一切正常后我会写一篇。

问题

Celery 工作人员配置为通过容器名称引用其代理和后端:

app = Celery('tasks', backend='redis://redis', broker='pyamqp://guest@rabbitmq//')

所有服务启动并由 Docker 验证后,8 个服务中的 3 个成功启动,连接到代理和后端,让我开始对它们执行 运行 任务。其他5个尝试连接RabbitMQ时连续超时并报告以下消息:

consumer: Cannot connect to amqp://guest:**@rabbitmq:5672//: timed out.

我无计可施,试图找出为什么我的工作节点中只有 3 个允许发生连接,而其他 5 个导致连续超时。所有启动的服务都通过同一个覆盖网络连接。

当我尝试使用 RabbitMQ 以外的代理时,问题仍然存在,这让我认为它不特定于任何一个代理。当在报告超时的机器上时,我可能会在覆盖网络上按名称连接到任何服务时遇到问题。停止服务并再次启动总是产生相同的结果 - 相同的 3 个节点工作而其他 5 个超时。

所有节点 运行 与 Docker (19.03.4,内部版本 9013bf583a) 的版本相同,并且这些机器是从相同的映像创建的。他们几乎是一样的。它们之间唯一的区别是它们的主机名,例如 manager、worker1、worker2 等

在我的个人计算机上开发我的应用程序时,我已经能够在 Docker 群之外(全部在一台机器上)复制此设置,方法是使用桥接网络而不是覆盖。我没有遇到问题,直到我在开发服务器上启动所有内容,使用上面详述的步骤,在将其推送到生产环境之前对其进行测试。

关于为什么会发生这种情况以及我该如何补救有什么想法吗?从 Docker swarm 切换到 Kubernetes 目前对我来说不是一个选择。

这不是我想要的答案,但这似乎是 Docker swarm 中一个持续存在的错误。对于任何感兴趣的人,我将包含问题页面。

https://github.com/docker/swarmkit/issues/1429

那里有一位用户列出的解决方法,可能会唤醒某些人,但您的里程可能会有所不同。它对我不起作用。下面的项目符号中列出了解决方法:

  • 不要尝试将 docker 用于 Windows 以获得多节点网状网络 (swarm) 运行。根本不(还)支持它。如果您 google 周围,您会发现一些 Microsoft 博客讲述它。 docker 文档也在某处提到了它。如果 docker cmd 本身在尝试在 Windows 下设置某些内容时会打印 error/warning 就好了——这根本行不通。它确实在单个节点上工作。
  • 不要尝试在 Windows 下的 Virtualbox 中使用 Linux 并希望使用它来解决问题。当然,它不起作用,因为它具有与基础 Windows.
  • 相同的限制
  • 确保至少为工作节点打开端口 7946 tcp/udp 和 4789 udp。对于 master 也是 2377 tcp。使用例如netcat -vz -u 用于 udp 检查。 tcp 没有-u。
  • 确保在执行 join swarm 命令时在 docker 工作节点 (!) 上传递 --advertise-addr。此处放置打开上述端口的工作节点的外部 IP 地址。仔细检查端口是否真的打开!
  • 使用 ping 检查容器名称的 DNS 解析有效。如果您忘记了 --advertise-addr 或未打开端口 7946 会导致 DNS 解析无法在工作节点上工作!

如果您遇到同样的问题,我建议您先尝试以上所有方法。为了澄清上述要点中的一些事情,在将工作节点加入 swarm 时,应在工作节点上使用 --advertise-addr 标志。如果您的工作程序节点没有静态 IP 地址,您可以使用该接口连接它。 运行 ifconfig 查看您的界面。您需要使用具有面向外部 IP 地址的接口。对于大多数人来说,这可能是 eth0,但您仍应在 运行 命令之前检查。这样做,您将对工作人员发出的命令是:

docker swarm join --advertise-addr eth0:2377 --token <your_token> <manager_ip>:2377

2377 是 Docker 使用的端口。通过进入您的管理器节点和 运行 以下内容来验证您是否使用了正确的 IP 地址加入:

docker node inspect <your_node_name>

如果您不知道您的节点名称,它应该是您作为工作节点加入的机器的主机名。可以通过运行:

查看
docker node ls

如果你加入了正确的界面,你会在 return 的底部看到这个当 运行 检查:

{
    "Status": "ready",
    "Addr": <your_workers_external_ip_addr>
}

如果您确认一切都已正确连接,但问题仍然存在,您可以尝试使用附加标志启动您的服务 --dns-option use-vc when 运行 Docker 群这样创建:

docker swarm create --dns-option use-vc --network <my_overlay> ...

最后,如果以上所有方法都像我一样对你失败,那么你可以在 swarm 中公开你希望连接的 运行 服务的端口。对我来说,我希望将工作节点上的服务连接到管理器节点上的 RabbitMQ 和 Redis。我通过公开服务端口来做到这一点。您可以在 运行:

创建时执行此操作
docker swarm create -p <port>:<port> ...

或者在 运行

启动服务之后
docker service update --publish-add <port>:<port> <service_name>

在此之后,您的工作节点服务可以通过工作节点主机的 IP 地址和您公开的端口连接到管理节点服务。例如,使用 RabbitMQ,这将是:

pyamqp://<user>:<pass>@<worker_host_ip_addr>:<port>/<vhost>

希望这对偶然发现此问题的人有所帮助 post。