Redis在docker-compose的本地主机上不可用

7

我目前遇到了一个问题,已经尝试解决了一周,却没有进展。希望你能指点我正确的方向。

初始情况

描述

我正在构建一个连接到一些API的 NestJS 应用程序。内部使用bullmq 作为消息队列,它本身使用ioredis 连接到redis数据库。我通过以下配置使用docker-compose up连接自己编写的服务器组件和redis(使用docker):

version: '3'

services:
  server:
    image: myserver:1.4.0
    container_name: myserver
    depends_on:
      - db
    ports:
      - 3001:3000
    environment:
      - REDIS_HOST=redis
  db:
    image: redis:6.0.8
    container_name: redis
    ports:
      - 6379:6379

版本信息

工作站

  • Docker版本号为19.03.13,构建版本号为4484c46d9d
  • docker-compose版本号为1.27.4,构建版本号为40524192

服务器组件

  • bullmq版本号为1.9.0
  • ioredis版本号为4.17.3

Redis Docker

  • 版本号为6.0.8

问题

我的服务器组件的问题在于它尝试使用以下代码连接到给定REDIS_HOST下端口为6379的redis实例:

readonly connection = new Redis(
    +(process.env.REDIS_PORT ?? this.configService.get('redis_port')),
    process.env.REDIS_HOST ?? this.configService.get('redis_host'),
  );

但是会抛出以下错误:
[ioredis] Unhandled error event: Error: connect ECONNREFUSED 127.0.0.1:6379

我原本以为只需在暴露端口处看到redis实例,但不在127.0.0.1看到redis实例,但不应该使用给定的ip吗?

我检查过的事情

服务器代码正确,REDIS_HOST在ioredis调用中正确提交和调用。因此,在ioredis内进一步挖掘时,我发现了这个问题。所以,考虑到所有提示,它应该是在我的工作站上作为本地可用的,我正在使用0.0.0.0:6379进行连接,并且可以正常工作。

Docker compose会自动创建网络桥,在redis docker的IP(以及别名redis和db)上使用netcat检查,服务器docker控制台上的6379端口上,redis实例是可用的。

然后,我使用docker-compose的网络配置显式设置了子网,以及为容器分配了静态IP,但正如我已经描述的那样:IP已正确解析。

我在docker github上找到了以下问题issue 204。我认为这正是我在这里面对的问题,但如何解决?

简而言之,ioredis尝试连接到正确解析的redis实例的IP,但失败了,因为该实例在服务器组件的本地IP上不可用。

我当前的状态是什么

我无法控制地哭泣。

我目前没有任何想法如何使“myserver”容器通过ioredis连接到redis实例。我认为我面临的问题必须与Windows上的Docker解析IP到127.0.0.1的方式有关。

  • 我的观点正确吗?
  • 你能建议其他尝试的方法吗?

此致敬礼并提前致谢。

编辑(2020-11-27):经过一些挖掘和进一步调查Jeffrey Mixon的建议,我很遗憾离解决方案还很远。我的最后几步包括:

  • 更新所有依赖项(包括bullmq到v1.11,ioredis到4.19.2等)。这并没有改变任何东西。
  • 然后我在bullmq issue board上找到了一个相似问题的相对较新的帖子,我从像上面展示的连接对象中重用连接切换到始终创建新连接,因为它也在bullmq文档中解释。但这也没有帮助。
new Queue(name, {
        connection: {
          host: this.redisHost,
          port: this.redisPort,
        },
      })
  • 我从使用IORedis库中的'Redis'对象切换为使用'IORedis',但是我的应用程序docker的习惯仍然没有改变。即使使用正确的redis主机调用Redis连接,它仍然尝试连接到上面显示的127.0.0.1:6379。
  • 最后一个奇怪的行为是,如果我选择一个无法解决的主机URL,应用程序docker会正确地尝试连接到那个不可解析的主机。但是,一旦这个主机在docker-compose的网络中可用,它就会使用127.0.0.1。

编辑(2020-12-01):: 同时,我在一个干净的Linux机器上检查了一下是否仅在Windows Docker上发生问题,但是它也在Linux上发生。

我没有解决问题本身,但是我通过将所有内容放在一个docker中绕过了这个问题。由于我的应用程序更多地是一个概念验证,所以这样做没有太大的痛苦。 如果未来出现解决方案或更多人有相同的问题,我将保持这个问题开放。

对于那些想知道的人,我的dockerfile现在在redis图像之上堆叠另一层。我添加了我之前使用的ng-cli-e2e图像的部分。因此,在我的现有dockerfile的开头,我添加了以下内容:


FROM redis:6.0.9-buster

RUN apt update

RUN apt install nodejs -y

RUN apt install npm -y

最终,我创建了一个小的包装脚本,它可以启动Redis服务器和我的应用程序。现在我暴露了两个端口,以便在我的机器上访问所有内容。

EXPOSE 3000 6379

CMD ./docker-start-wrapper.sh

这不是最美观的解决方案,但目前它确实能够起到作用。


1
无论我尝试什么,最终都会出现这个问题。为什么连接 ioredis 如此困难?错误:connect ECONNREFUSED 127.0.0.1:6379 at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1146:16) { errno: -111, code: 'ECONNREFUSED', syscall: 'connect', address: '127.0.0.1', port: 6379 } - Caruso33
请查看此文档 https://newbedev.com/error-99-connecting-to-localhost-6379-cannot-assign-requested-address ,db = redis.Redis(host='db', port=6379, decode_responses=True) - Hamed
我没有使用Flask,所以我不知道这个信息应该给我什么。无论如何,你的评论应该是一个答案。 - sehe
3个回答

5
问题在于您的应用程序容器将 localhost 作为与redis容器连接的主机名。在这种情况下,应使用主机名 redis
考虑以下演示:
version: '3.7'

services:
  server:
    image: busybox
    container_name: myserver
    entrypoint: /bin/sh
    # if redis is replaced by localhost or 127.0.0.1 below, this container will fail
    command: "-c \"sleep 2 && nc -v redis 6379\""
    depends_on:
      - db
    ports:
      - 3001:3000
    environment:
      - REDIS_HOST=redis
  db:
    image: busybox
    container_name: redis
    entrypoint: /bin/nc
    command: "-l -p 6379 -v"
    # is not necessary to publish these ports
    #ports:
    #  - 6379:6379

$ docker-compose -f scratch_2.yml up
Creating network "scratches_default" with the default driver
Creating redis ... done
Creating myserver ... done
Attaching to redis, myserver
redis     | listening on [::]:6379 ...
myserver  | redis (172.25.0.2:6379) open
redis     | connect to [::ffff:172.25.0.2]:6379 from [::ffff:172.25.0.3]:34821 ([::ffff:172.25.0.3]:34821)
myserver exited with code 0
redis exited with code 0

当您发布端口时,它们是用于主机上容器之外的使用。通过尝试将您的myserver容器连接到127.0.0.1,该容器只是在尝试连接自己。


1
抱歉耽搁了。感谢您的回答,特别是关于“外部”端口的部分。但不幸的是,这个答案并没有让我有所进展。正如我在问题中所述,我知道连接应该是到redis的。我再次检查,主机redis已正确提供给应用程序容器。但仍然如此,正如在这里所述https://github.com/luin/ioredis/issues/365,ioredis想要绑定到本地端口。我怎样才能将redis docker的端口绑定到本地? - sehe
2
@sehe 我的意思是,根据你的错误信息 ECONNREFUSED 127.0.0.1:6379,你的应用程序并没有尝试连接到主机名为 redis,尽管你可能正在尝试设置它。相反,它显然是在尝试连接到 127.0.0.1。我对 ioredis 一无所知,所以这可能只是一个 bug。你尝试过官方的 redis node 客户端 redis 吗? - Jeffrey Mixon
1
我只是使用了ioredis,因为bullmq推荐使用。但也许我会尝试适应这个。感谢保持联系! - sehe
1
@sehe,bullmq需要连接选项来连接任何队列,但也需要实例化的Worker或QueueScheduler。这可能是您的问题吗? - EuberDeveloper
@EuberDeveloper 是这样吗?https://docs.bullmq.io/guide/workers 这可能确实是我的问题。 - sehe

2
docker-compose的问题在于redis不在localhost上,而是在它自己的网络上。默认情况下,docker compose中的所有容器共享同一个默认网络,因此您的redis容器应该通过主机名redis(或在您的情况下是容器名称db)对该docker-compose中的所有其他容器可用。
另一个需要注意的问题是,如果您正在使用bullmq,则不仅需要自定义集合的Queue选项,还需要自定义任何使用的WorkerQueueScheduler,因此您也应将自定义连接选项传递给它们。

嗯,这是一个很好的观点。所有我的工作人员都是使用队列实例化的,就像在bullmq文档中所述。https://docs.bullmq.io/guide/workers没有显示与工作人员共享连接选项的方法。正如我在问题中所述,我尝试了主机名“redis”以及“db”,但没有成功。 - sehe
2
当我遇到这个问题时,将“redis”(在我的情况下)添加到所有队列和工作实例中可以解决问题。如果您已经这样做了,但仍然无法正常工作,我想不出其他解决方法。 - EuberDeveloper

1
希望这可以帮到你,我刚遇到了这个问题,结果发现是代码问题,通过客户端对象设置正确的连接到redis的方式是我的问题的原因,请确保您的代码与您的redis模块版本匹配。
在我的情况下,redis v4.6.6,以下内容有效:
// 创建一个redis客户端并确保连接 const redisClient = redis.createClient({ url: redis://${redisHost}:${redisPort}, });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接