Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js connect to MySQL Docker container ECONNREFUSED

Before you flag this question as a duplicate, please note that I did read other answers, but it didn't solve my problem.

I have a Docker compose file consisting of two services:

version: "3"
services:
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_HOST: localhost
      MYSQL_DATABASE: mydb
      MYSQL_USER: mysql
      MYSQL_PASSWORD: 1234
      MYSQL_ROOT_PASSWORD: root
    ports:
      - "3307:3306"
    expose:
      - 3307
    volumes:
      - /var/lib/mysql
      - ./mysql/migrations:/docker-entrypoint-initdb.d
    restart: unless-stopped
  web:
    build:
      context: .
      dockerfile: web/Dockerfile
    volumes:
      - ./:/web
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: development
      PORT: 3000
    links:
      - mysql:mysql
    depends_on:
      - mysql
    expose:
      - 3000
    command: ["./wait-for-it.sh", "mysql:3307"]

/web/Dockerfile:

FROM node:6.11.1

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

COPY package.json /usr/src/app/
RUN npm install

COPY . /usr/src/app

CMD [ "npm", "start" ]

After docker-compose up --build the services start up, however the "wait-for-it.sh" script times out when waiting for mySQL to start (so temporarily I am not using it when testing for DB connectivity, I just wait until the console shows that MySQL is ready for accepting incoming connections)

When MySQL is running from the host machine I can login using Sequel Pro and query the DB and get the sample records from ./mysql/migrations

enter image description here enter image description here

I can also SSH into the running MySQL container and do the same.

However, my Node.js app yields ECONNREFUSED 127.0.0.1:3307 when connecting

MySQL init:

import * as mysql from 'promise-mysql'

const config = {
    host: 'localhost',
    database: 'mydb',
    port: '3307',
    user: 'mysql',
    password: '1234',
    connectionLimit: 10
}

export let db = mysql.createPool(config);

MySQL query:

import { db } from '../db/client'

export let get = () => {

return db.query('SELECT * FROM users', [])
    .then((results) => {
        return results

    })
    .catch((e) => {
        return Promise.reject(e)
    })
}

Route invoked when hitting url /

import { Router } from 'express';
import * as repository from '../repository'
export let router = Router();

router.get('/', async (req, res) => {

    let users;

    try{
        users = await repository.users.get();
    } catch(e){
        // ECONNREFUSED 127.0.0.1:3307
    }

    res.render('index', {
        users: users
    });
});

It's unlikely to be a race condition because at the same time when Node.js fails I can query using Sequel Pro or SSH into the running Docker container and query. So it's probably a case of Node.js not being able to access to MySQL container?

{
    error: connect ECONNREFUSED 127.0.0.1:3307
    code: 'ECONNREFUSED',
    errno: 'ECONNREFUSED',
    syscall: 'connect',
    address: '127.0.0.1',
    port: 3307,
    fatal: true
}
like image 903
ChrisRich Avatar asked Jul 30 '17 12:07

ChrisRich


1 Answers

This:

mysql:
    image: mysql:5.7
    environment:
    ...
    ports:
      - "3307:3306"

Means that Docker will map the 3307 port of the host to the 3306 port of the container. So you can access from Sequel to localhost:3307.

However, it does not mean that the container is listenting to 3307; the container is in fact still listening to 3306. When others containers tries to access the mysql DNS, it gets translated to the internal container IP, therefore you must connect to 3306.

So your node config should look like:

const config = {
    host: 'mysql',
    database: 'mydb',
    port: '3306',
    user: 'mysql',
    password: '1234',
    connectionLimit: 10
}

And this in your docker-compose.yml:

command: ["./wait-for-it.sh", "mysql:3306"]

Note: wait-for-it.sh script comes from: https://github.com/vishnubob/wait-for-it

like image 105
Robert Avatar answered Oct 23 '22 03:10

Robert