Docker Compose getting error ECONNREFUSED 127.0.0.1:3306 with MySQL and NodeJS

I am trying to setup some containers for my NestJS + TypeORM + MySQL environment by using Docker Compose in a Windows 10 host, but I am getting an ECONNREFUSED error:

connect ECONNREFUSED 127.0.0.1:3306 +2ms
backend_1  | Error: connect ECONNREFUSED 127.0.0.1:3306
backend_1  |     at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1145:16)
backend_1  |     --------------------
backend_1  |     at Protocol._enqueue (/usr/src/app/node_modules/mysql/lib/protocol/Protocol.js:144:48)
backend_1  |     at Protocol.handshake (/usr/src/app/node_modules/mysql/lib/protocol/Protocol.js:51:23)        
backend_1  |     at PoolConnection.connect (/usr/src/app/node_modules/mysql/lib/Connection.js:116:18)
backend_1  |     at Pool.getConnection (/usr/src/app/node_modules/mysql/lib/Pool.js:48:16)
backend_1  |     at /usr/src/app/node_modules/typeorm/driver/mysql/MysqlDriver.js:793:18
backend_1  |     at new Promise (<anonymous>)
backend_1  |     at MysqlDriver.createPool (/usr/src/app/node_modules/typeorm/driver/mysql/MysqlDriver.js:790:16)
backend_1  |     at MysqlDriver.<anonymous> (/usr/src/app/node_modules/typeorm/driver/mysql/MysqlDriver.js:278:51)
backend_1  |     at step (/usr/src/app/node_modules/typeorm/node_modules/tslib/tslib.js:141:27)
backend_1  |     at Object.next (/usr/src/app/node_modules/typeorm/node_modules/tslib/tslib.js:122:57)

I have created the following Dockerfile to configure the NestJS API container:

FROM node:12-alpine
WORKDIR /usr/src/app

COPY package.json .
RUN npm install

EXPOSE 3000

#CMD ["npm", "start"]

CMD /wait-for-it.sh db:3306 -- npm start

COPY . .

And then I reference this from Docker Compose with the following docker-compose.yml:

version: "3.8"

networks:
  app-tier:
    driver: bridge

services:
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    expose:
      - "3306"
    ports:
      - "3306:3306"    
    networks:
      - app-tier      
    environment:
      MYSQL_DATABASE: school
      MYSQL_ALLOW_EMPTY_PASSWORD: ok
      MYSQL_ROOT_PASSWORD: root
      MYSQL_USER: dbuser
      MYSQL_PASSWORD: dbuser
      MYSQL_ROOT_HOST: '%'
  backend:
    depends_on:
      - db
    build: .
    ports:
      - "3000:3000"
    networks:
      - app-tier      

Finally, I set the TypeORM configuration to match with the Docker Compose file:

export const DB_CONFIG: TypeOrmModuleOptions = {
    type: 'mysql',
    host: 'db',
    port: 3306,
    username: 'dbuser',
    password: 'dbuser',
    database: 'school',
    entities: [], // We specify the entities in the App Module.
    synchronize: true,
};

I am kind of new to Docker Compose, but I have tried many things like changing the output port to 3307, setting an explicit network... and the port 3306 is free in my host OS when I run it. Any help?

Edit 1

I have included MYSQL_ROOT_HOST and wait-for-it.sh as suggested, but still no results.

Upvotes: 12

Views: 39290

Answers (2)

Corey
Corey

Reputation: 1133

You can utilize health checks to gracefully check if a service is in a healthy state and fail gracefully if it doesn't reach that state after a max number of tries.

the health check on db service might look something like

healthcheck:
  test: mysqladmin ping -h mysql --user=$$MYSQL_USER --password=$$MYSQL_ROOT_PASSWORD
  interval: 30s
  timeout: 12s
  retries: 10

and then on backend add a depends

depends_on:
  db:
    condition: service_healthy

This will boot your backend after the db is healthy and fail after trying 10 times waiting 30 seconds between each try. This is advantageous to the previous answer because it won't hang forever if mysql never comes up

Upvotes: 2

WMRamadan
WMRamadan

Reputation: 1220

I think your db is taking more time to start and your app is starting before the db, try something like this for your docker-compose.yml file:

version: "3.8"

networks:
  app-tier:
    driver: bridge

services:
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    expose:
      - "3306"
    ports:
      - "3306:3306"    
    networks:
      - app-tier      
    environment:
      MYSQL_DATABASE: school
      MYSQL_ALLOW_EMPTY_PASSWORD: ok
      MYSQL_ROOT_PASSWORD: root
      MYSQL_USER: dbuser
      MYSQL_PASSWORD: dbuser
      MYSQL_ROOT_HOST: '%'
  backend:
    depends_on:
      - db
    build: .
    command: bash -c 'while !</dev/tcp/db/3306; do sleep 1; done; npm start'
    ports:
      - "3000:3000"
    networks:
      - app-tier   

Upvotes: 14

Related Questions