comonadd
comonadd

Reputation: 1988

ioredis Cannot connect to Redis cluster running in Docker

I have a Nest.js application running that connects to Redis cluster from the provided host/port like this:

        const port = this.configService.get('REDIS_PORT')
        const host = this.configService.get('REDIS_HOST').replace('rediss://', '')
        this.logger.log(`Initializing Redis client at ${host}:${port}`)
        const dev = true
        const clusterConfig: ClusterOptions = {
            scaleReads: 'all',
            enableAutoPipelining: true,
            redisOptions: {
                password: this.configService.get('REDIS_PASSWORD'),
                db: 0,
                showFriendlyErrorStack: true,
                tls: dev
                    ? undefined
                    : {
                          checkServerIdentity: (_host, _cert) => {
                              // skip certificate hostname validation
                              return undefined
                          },
                      },
            },
        }
        this.redis = new Cluster(
            [
                { port: 6379, host: 'localhost' },
            ],
            clusterConfig,
        )

On stage environment, this works fine. If I run app inside Docker locally (where redis cluster is set up using docker compose), it works fine.

BUT, if I run redis cluster in Docker-compose and application on host machine, and try to connect to redis cluster using ports exposed to host machine from docker (localhost:6379), it cannot connect and writes the following logs:

ioredis:cluster:subscriber selected a subscriber 173.17.0.4:7002 +0ms
  ioredis:redis status[173.17.0.4:7002 (ioredis-cluster(subscriber))]: wait -> wait +0ms
  ioredis:cluster cluster slots result count: 3 +0ms
  ioredis:cluster cluster slots result [0]: slots 5461~10922 served by [ '173.17.0.3:7001' ] +0ms
  ioredis:cluster cluster slots result [1]: slots 0~5460 served by [ '173.17.0.2:7000' ] +0ms
  ioredis:cluster cluster slots result [2]: slots 10923~16383 served by [ '173.17.0.4:7002' ] +0ms
  ioredis:cluster:connectionPool Reset with [
  ioredis:cluster:connectionPool   { host: '173.17.0.3', port: 7001, readOnly: false },
  ioredis:cluster:connectionPool   { host: '173.17.0.2', port: 7000, readOnly: false },
  ioredis:cluster:connectionPool   { host: '173.17.0.4', port: 7002, readOnly: false }
  ioredis:cluster:connectionPool ] +2ms
  ioredis:cluster:connectionPool Disconnect ::1:7000 because the node does not hold any slot +0ms
  ioredis:redis status[::1:7000]: wait -> close +2ms
  ioredis:connection skip reconnecting since the connection is manually closed. +2ms
  ioredis:redis status[::1:7000]: close -> end +0ms
  ioredis:cluster:connectionPool Remove ::1:7000 from the pool +0ms
  ioredis:cluster:connectionPool Connecting to 173.17.0.3:7001 as master +0ms
  ioredis:redis status[173.17.0.3:7001]: wait -> wait +0ms
  ioredis:cluster:connectionPool Connecting to 173.17.0.2:7000 as master +0ms
  ioredis:redis status[173.17.0.2:7000]: wait -> wait +0ms
  ioredis:cluster:connectionPool Connecting to 173.17.0.4:7002 as master +0ms
  ioredis:redis status[173.17.0.4:7002]: wait -> wait +0ms
  ioredis:cluster status: connecting -> connect +2ms
  ioredis:redis status[173.17.0.2:7000]: wait -> connecting +0ms
  ioredis:redis queue command[173.17.0.2:7000]: 0 -> cluster([ 'INFO' ]) +0ms
  ioredis:cluster:subscriber subscriber has left, selecting a new one... +2ms
  ioredis:redis status[::1:7000 (ioredis-cluster(subscriber))]: wait -> close +0ms
  ioredis:connection skip reconnecting since the connection is manually closed. +0ms
  ioredis:redis status[::1:7000 (ioredis-cluster(subscriber))]: close -> end +0ms
  ioredis:cluster:subscriber selected a subscriber 173.17.0.2:7000 +0ms
  ioredis:redis status[173.17.0.2:7000 (ioredis-cluster(subscriber))]: wait -> wait +0ms
  ioredis:redis status[::1:7000 (ioredis-cluster(refresher))]: ready -> close +1ms
  ioredis:connection skip reconnecting since the connection is manually closed. +1ms
  ioredis:redis status[::1:7000 (ioredis-cluster(refresher))]: close -> end +0ms
  ioredis:redis status[::1:7000 (ioredis-cluster(refresher))]: ready -> close +1ms
  ioredis:connection skip reconnecting since the connection is manually closed. +1ms
  ioredis:redis status[::1:7000 (ioredis-cluster(refresher))]: close -> end +0ms
  ioredis:redis status[::1:7000 (ioredis-cluster(refresher))]: ready -> close +0ms
  ioredis:connection skip reconnecting since the connection is manually closed. +0ms
  ioredis:redis status[::1:7000 (ioredis-cluster(refresher))]: close -> end +0ms

I don't understand this part. It first says that

ioredis:cluster cluster slots result [0]: slots 5461~10922 served by [ '173.17.0.3:7001' ] +0ms
  ioredis:cluster cluster slots result [1]: slots 0~5460 served by [ '173.17.0.2:7000' ] +0ms
  ioredis:cluster cluster slots result [2]: slots 10923~16383 served by [ '173.17.0.4:7002' ] +1ms

and then:

Disconnect ::1:7000 because the node does not hold any slot +0ms

so does it hold any slot or not?

I can easily connect to this redis cluster from a RedisInsight client

I need app running on host machine for debugging. How do I fix this and why does this happen?

This is docker-compose config:

version: '2'
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NPM_TOKEN: ${NPM_TOKEN}
    volumes:
      - ./src:/usr/src/app/src
    command: ['yarn', 'start:dev']
    restart: always
    ports:
      - '3399:3399'
    depends_on:
      - redis-cluster
    networks:
      - inner_net
    env_file:
      - .env
    env:
      - REDIS_HOST=redis1
      - REDIS_PORT=7000

  redis1:
    image: redis:3
    ports:
      - "6379:7000"
    volumes:
      - ./redis/redis-cluster1.tmpl:/usr/local/etc/redis/redis.conf
    command: redis-server /usr/local/etc/redis/redis.conf
    networks:
      inner_net:
        ipv4_address: 173.17.0.2
  redis2:
    image: redis:3
    ports:
      - "7001:7001"
    volumes:
      - ./redis/redis-cluster2.tmpl:/usr/local/etc/redis/redis.conf
    command: redis-server /usr/local/etc/redis/redis.conf
    networks:
      inner_net:
        ipv4_address: 173.17.0.3
  redis3:
    image: redis:3
    ports:
      - "7002:7002"
    volumes:
      - ./redis/redis-cluster3.tmpl:/usr/local/etc/redis/redis.conf
    command: redis-server /usr/local/etc/redis/redis.conf
    networks:
      inner_net:
        ipv4_address: 173.17.0.4
  redis-cluster:
    tty: true
    build:
      context: redis
      args:
        redis_version: '3.2.9'
    hostname: server
    depends_on:
      - redis1
      - redis2
      - redis3
    networks:
      inner_net:
        ipv4_address: 173.17.0.5

networks:
    inner_net:
        name: inner-service-net
        driver: bridge
        ipam:
          driver: default
          config:
          - subnet: 173.17.0.0/16

Upvotes: 0

Views: 2075

Answers (2)

LongNotDouble
LongNotDouble

Reputation: 1

have the same problem, and with the natMap it works by me

let natMap = {
  "172.20.0.71:6381": {host: '127.0.0.1', port: 6381},
  "172.20.0.72:6382": {host: '127.0.0.1', port: 6382},
  "172.20.0.73:6383": {host: '127.0.0.1', port: 6383}
}

let theConnection = new redis.Cluster([
  { host: '127.0.0.1', port: 6381 },
  { host: '127.0.0.1', port: 6382 },
  { host: '127.0.0.1', port: 6383 }
  // { host: 'localhost', port: 6381 },
  // { host: 'localhost', port: 6382 },
  // { host: 'localhost', port: 6383 }
], {
  redisOptions: {
    username: 'default',
    password: 'yourPassword',
    tls: undefined
  },
  natMap:natMap,
  dnsLookup: (address, callback) => callback(null, address),
  scaleReads: 'slave',
  enableAutoPipelining: true,
  enableOfflineQueue: true,
  enableReadyCheck: true,
  slotsRefreshTimeout: 500000,
});

Upvotes: 0

comonadd
comonadd

Reputation: 1988

I have found an answer to this. For this to work, you have to provide ioredis with natMap parameter. This is what worked for me:

        const devNatMap = {
            '173.17.0.2:7000': {
                host: '127.0.0.1',
                port: 7000,
            },
            '173.17.0.3:7001': {
                host: '127.0.0.1',
                port: 7001,
            },
            '173.17.0.4:7002': {
                host: '127.0.0.1',
                port: 7002,
            },
        }
        const clusterConfig: ClusterOptions = {
            scaleReads: 'all',
            enableAutoPipelining: true,
            natMap: dev ? devNatMap : undefined,
            redisOptions: {
                password: this.configService.get('REDIS_PASSWORD'),
                db: 0,
                showFriendlyErrorStack: true,
                tls: dev
                    ? undefined
                    : {
                          checkServerIdentity: (_host, _cert) => {
                              // skip certificate hostname validation
                              return undefined
                          },
                      },
            },
        }
        this.redis = new Cluster([{ port, host }], clusterConfig)

Upvotes: 0

Related Questions