Search code examples
node.jsdockeramazon-ec2docker-composechild-process

Difference in behavior when running docker-compose up command from CLI vs. Node.js script


I have a Docker Compose file (docker-compose.yml) that defines two database containers and an Nginx container:

version: '3.1'
services:
  mysql1:
    image: mysql:8.0.21
    container_name: 'db1'
    restart: always
    environment:
      TZ: 'Asia/Jerusalem'
      MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
    expose:
      - '3306'
    command: ["--server-id=1","--default-authentication-plugin=mysql_native_password"]
    security_opt:
      - seccomp:unconfined
    volumes:
      - ./db1/auto-generate:/auto-generate:rw
      - ./common/conf.d:/etc/mysql/conf.d:ro
      - ./common/initdb.d:/docker-entrypoint-initdb.d:ro
  mysql2:
    image: mysql:8.0.21
    container_name: 'db2'
    restart: always
    environment:
      TZ: 'Asia/Jerusalem'
      MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
    expose:
      - '3306'
    command: ["--server-id=2","--default-authentication-plugin=mysql_native_password"]
    security_opt:
      - seccomp:unconfined
    volumes:
      - ./db2/auto-generate:/auto-generate:rw
      - ./common/conf.d:/etc/mysql/conf.d:ro
      - ./common/initdb.d:/docker-entrypoint-initdb.d:ro
  nginx:
    image: nginx:latest
    restart: always
    container_name: production_nginx
    ports:
      - '${PORT_DB1}:3001'
      - '${PORT_DB2}:3002'
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - "mysql1"
      - "mysql2"

When I run the following command from the CLI, everything works as expected: sudo -E docker-compose -f docker-compose.yml up -d.

CLI output:

[+] Running 3/3
 ⠿ Container db2               Running                                                                                                                                                                                                 0.0s
 ⠿ Container db1               Running                                                                                                                                                                                                 0.0s
 ⠿ Container production_nginx  Started

However, when I run the same command from a Node.js script, it fails:

...
const { execSync } = require('child_process');

const switchCommand = `cd /opt/datateam  && sudo -E docker-compose  -f docker-compose.yml up -d`;

const password = config.db.password || dbDefaults.password;
const port = config.db.port || dbDefaults.port;
const { stagingPort } = config;

const getEnv = env => {
  return {
    PORT_DB1: env === 1 ? stagingPort : port,
    PORT_DB2: env === 2 ? stagingPort : port,
    DB_PASSWORD: password,
    COMPOSE_PROJECT_NAME: 'Mysql_dockers'
  };
};

...
await execSync(switchCommand, { env }); // <-- This fails

Node.js output:

Error: Command failed: cd /opt/datateam && sudo -E docker-compose -f docker-compose.yml up -d
16:02:38   Container db2  Creating
16:02:38   Container db1  Creating
16:02:38   Error response from daemon: Conflict. The container name "/db2" is already in use by container "78040292072bc3e959b563

The script is suppose to restart the nginx container, pointing to different ports (switching between them).

It appears that when running the command from the node.js script, it is if it doesn't see the containers already exist, and try to create them, contrary to docker compose up documentation which states that it should avoid creating containers that their configuration have not changed.

The following code is run on an EC-2 instance. Tried both from the default ec2-user and root.

It works on our on-prem linux server without issues.


Solution

  • The issue was using a different compose project name in a node.js parameter compared to the project name that was generated by docker implicitly.

    When we ran docker-compose up for the first time, it ran from the /opt/datateam dir using CLI. Docker implicitly assigned it with a project name of "datateam" (the directory of the file). This was confirmed using docker inspect production_nginx | grep "compose.project".

    The node.js file we ran executed the command sudo -E docker-compose -f /opt/datateam/docker-compose.yml up -d with an environment variable COMPOSE_PROJECT_NAME=Mysql_dockers. This caused docker to look for existing containers with a project name of Mysql_dockers, and since none exist, it attempted to create new ones, which ultimately caused the error to be thrown (Conflict. The container name "/db2" is already in use..).

    The best solution was to alter the docker-compose.yml file and give it a name:

    version: '3.1'
    name: 'prod_mysql'
    
    services:
    ...
    

    And use the name in the environment variable we run the command with:

    ...
    const getEnv = env => {
      return {
        PORT_DB1: env === 1 ? stagingPort : port,
        PORT_DB2: env === 2 ? stagingPort : port,
        DB_PASSWORD: password,
        COMPOSE_PROJECT_NAME: 'prod_mysql'
      };
    };
    ...