Search code examples
mysqlnode.jsdockersequelize.js

System able to connect to mysql docker, but other docker container cannot


This is my docker-compose file:

version: '3.9'
services:
  mysql:
    image: mysql:8.0
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_DATABASE: 'admindb'
      MYSQL_USER: 'admin'
      MYSQL_PASSWORD: 'secretpassword'
      MYSQL_ROOT_PASSWORD: 'someverysecretpassword'
    ports:
      - '3306:3306'
      - '33060:33060'
    volumes:
      - ./db:/var/lib/mysql
    networks:
      - adminnet
  app:
    image: "node:latest"
    depends_on:
      - mysql
    user: "node"
    restart: unless-stopped
    working_dir: /home/app
    environment:
      NODE_ENV: production
    volumes:
      - ${PWD}/../:/home/app
    ports:
      - '8081:8081'
    command: "npm start"
    networks:
      - adminnet
volumes:
  db:
  data:

networks:
  adminnet:

And this is my app.js:

const { Sequelize } = require('sequelize');

const sequelize = new Sequelize('admindb', 'admin', 'secretpassword', {
    host: 'mysql',
    port: 3306,
    dialect: 'mysql',
});

sequelize
  .authenticate()
  .then(() => {
    console.log('Connection has been established successfully.');
  })
  .catch(err => {
    console.error('Unable to connect to the database:', err);
  });

When I just run the MySQL container (so, if I remove the "app" instance from the docker-compose file) and replace host:'mysql' with host:'127.0.0.1' and run npm start from my host machine, it is able to successfully connect to the database, which runs from the container.

But if I run app.js from a container, which should connect to the database inside the other container, it doesn't work. I use host:'mysql', but I keep getting the following error:

containers-app-1    | Unable to connect to the database: ConnectionError [SequelizeConnectionError]: connect ETIMEDOUT
containers-app-1    |     at ConnectionManager.connect (/home/app/node_modules/sequelize/lib/dialects/mysql/connection-manager.js:102:17)
containers-app-1    |     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
containers-app-1    |     at async ConnectionManager._connect (/home/app/node_modules/sequelize/lib/dialects/abstract/connection-manager.js:220:24)
containers-app-1    |     at async /home/app/node_modules/sequelize/lib/dialects/abstract/connection-manager.js:174:32
containers-app-1    |     at async ConnectionManager.getConnection (/home/app/node_modules/sequelize/lib/dialects/abstract/connection-manager.js:197:7)
containers-app-1    |     at async /home/app/node_modules/sequelize/lib/sequelize.js:301:26
containers-app-1    |     at async Sequelize.authenticate (/home/app/node_modules/sequelize/lib/sequelize.js:453:5) {
containers-app-1    |   parent: Error: connect ETIMEDOUT
containers-app-1    |       at Connection._handleTimeoutError (/home/app/node_modules/mysql2/lib/connection.js:189:17)
containers-app-1    |       at listOnTimeout (node:internal/timers:564:17)
containers-app-1    |       at process.processTimers (node:internal/timers:507:7) {
containers-app-1    |     errorno: 'ETIMEDOUT',
containers-app-1    |     code: 'ETIMEDOUT',
containers-app-1    |     syscall: 'connect',
containers-app-1    |     fatal: true
containers-app-1    |   },
containers-app-1    |   original: Error: connect ETIMEDOUT
containers-app-1    |       at Connection._handleTimeoutError (/home/app/node_modules/mysql2/lib/connection.js:189:17)
containers-app-1    |       at listOnTimeout (node:internal/timers:564:17)
containers-app-1    |       at process.processTimers (node:internal/timers:507:7) {
containers-app-1    |     errorno: 'ETIMEDOUT',
containers-app-1    |     code: 'ETIMEDOUT',
containers-app-1    |     syscall: 'connect',
containers-app-1    |     fatal: true
containers-app-1    |   }
containers-app-1    | }

Something makes it impossible for the node-container to connect to the database inside the mysql-container. I tried to use ping from the node-container, but that package isn't available.

I read a ton of webpages and I'm debugging for the past few hours, without luck. Anyone who might know the issue? I already tried all similar StackOverflow questions and solutions, without any luck.


Solution

  • I tested your stack on my machine and I got it working successfully, after a second run. I ran it twice because of your depends_on option (see my notes below).

    Here's my machine specs:

    Machine: MacBook Air (M1, 2020)
    OS: macOS Monterey v12.4
    Docker Version: 20.10.12 (installed with their .dmg)
    Docker Compose Version: not used -- the latest version of docker includes it as a subcommand

    I think this problem has multiple causes:

    1. Your docker-compose.yml file's depends_on option. If you read the documentation on depends_on, you'll see that your app service DOES NOT wait for mysql to be healthy before attempting a connection. You have to use the healthcheck option if you want to make sure mysql is healthy before connecting.
    2. There is a problem with your docker's internal host networking on your machine. This is hard to diagnose without knowing what OS you're using or what version of Docker you're running.
    3. If you're using swarm mode with Docker, local bind mounts for volumes can be problematic/buggy. Not recommended.