usersina
usersina

Reputation: 1783

How to wait for mysql docker-entrypoint-initdb before starting a service

I have a dockerized app that uses mysql, express & react.

I need to first initialize mysql with a docker-entrypoint-initdb.d before starting the express app. However, when mysql server starts it doesn't execute the starting .sql script right away and my_database_name starts initially empty.

In that timegap, my express app starts and tries to query en empty database. Later the initialization script works as intended and fills my_database_name which is already too late by then since my express app has already started.

I've tried depends_on/condition as shown below but Version 3 no longer supports the condition form of depends_on as stated in the docs.

version: '3.8'

services:
  api-server:
    build:
      context: ./server
      dockerfile: ./Dockerfile
    image: myapp_server
    env_file: ./server/.env
    stdin_open: true
    tty: true
    depends_on:
      mysqldb:
        condition: service_healthy
    ports:
      - 9000:9000
    networks:
      - mysern-app
    container_name: my_express_app

  mysqldb:
    image: mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: my_database_name
    volumes:
      - mysql-data:/data/db
      - ./db/backup/:/mysql/backup
      - ./db/init:/docker-entrypoint-initdb.d
    healthcheck:
      test: 'not sure what to write in here as well'
      interval: 1s
      retries: 120
    networks:
      - mysern-app
    ports:
      - 3306:3306
    container_name: my_mysql_app
networks:
  mysern-app:
    driver: bridge
volumes:
  mysql-data:
    driver: local

I ultimately want to do this sequence:

Start mysqldb > fill the table from the script > start the express app

Upvotes: 4

Views: 3765

Answers (3)

ItayB
ItayB

Reputation: 11337

There are several things you can do:

  1. Add restart: always to your api-server. If it crashes because MySQL is not yet available - it will restart until MySQL will be available. I'm assuming that your api-server will crash if MySQL is not available.
  2. In case it won't crash, implement health-check for your service, so it won't be available if MySQL isn't available.

docker-compose is your orchestrator and it can help you and take care of failures. In real-world, your MySQL might be not available for a second too, and you must know how to handle that.

Upvotes: 2

marcandreuf
marcandreuf

Reputation: 301

  1. Add a healthcheck to test something meaningful of your app. Instead of "SELECT 1;" it would be better to write a query custom to you app / dev env. i.e check the users table exists so that the login feature will work.
  2. Adjust intervals, timeout and retries to the performance of the query defined in step one.
mysql:   
  build: ...
  ...
  healthcheck:
    test: mysql ${DB_NAME} --user=${DB_USER} --password='${DB_PWD}' --silent --execute "SELECT 1;"
    interval: 30s
    timeout: 10s
    retries: 5
  1. Define the DB variables into a '.env' file next to the docker-compose.yml file.

Sample .env file

DB_HOST=...
DB_NAME=...
DB_USER=...
DB_PWD=...

Upvotes: 0

JRichardsz
JRichardsz

Reputation: 16505

This worked for me:

version: "3.7"
services:
  acme-db:
    image: mysql:5.7
    command: mysqld --sql_mode="" --max_connections=1100 --general-log=1 --general-log-file=/tmp/mysql-general-log.log
    container_name: eventhos-db
    ports:
      - "3306:3306"
    volumes:
      - ./database:/docker-entrypoint-initdb.d
    environment:
      MYSQL_ROOT_PASSWORD: changeme
      MYSQL_PASSWORD: changeme
      MYSQL_USER: "usr_acme"
      MYSQL_DATABASE: "acme"
      TZ: America/Lima
    deploy:
      resources:
        limits:
          memory: 512M
    healthcheck:
      test: 'cat /tmp/mysql-general-log.log | grep "root@localhost on  using Socket"'
      interval: 10s
      retries: 120

Explanation

There is not a native way to ask the mysql if dump has been fully imported.

Researching I found an internal mysql log file called mysql general_log and reviewing its content in dockerized mysql I detected a pattern:

If everything is ok, at the end this line appear: "root@localhost on using Socket" just once

enter image description here

So if we enabled this log using command: in docker-compose:

command: mysqld --sql_mode="" --max_connections=1100 --general-log=1 --general-log-file=/tmp/mysql-general-log.log

The health check is just a search of these line

cat /tmp/mysql-general-log.log | grep "root@localhost on  using Socket"

Note: Health check interval less than 10 seconds, don't work!

Upvotes: 2

Related Questions