Run Script After MySQL Docker Initialization
May 31, 2022
The Problem
I wanted to setup automated tests for OpenSupports.
I made them run after each commit with Github Actions, but they failed. Sometimes.
Inside the Ubuntu given by Github Actions, I ran all steps inside Docker containers.
The steps looked like this:
steps:
- uses: actions/checkout@v3
- run: make build
- run: make run
- run: make install
- run: make test
And all those make
commands interact with Docker containers.
- make build: builds the images from
Dockerfile
files, or pulls them from repos with official images - make run: does a
docker run
under the hood, which starts the containers - make install: does initialization steps, like installing dependencies, and setting up a base database (this last step was causing problems)
- make test: finally runs all tests
When done this way, one step after another, I sometimes saw this error in the logs from Docker:
2022-05-31T14:45:34.922935Z 0 [ERROR] InnoDB: Unable to lock ./ibdata1 error: 11
2022-05-31T14:45:34.922969Z 0 [Note] InnoDB: Check that you do not already have another mysqld process using the same InnoDB data or log files.
But sadly none of the resources for these errors helped me.
The Solution
The MySQL Docker official image will run all .sh
scripts in the folder /docker-entrypoint-initdb.d
from the docker container.
First I placed my initialization script in a folder, like ./scripts-db/init-db.sh
, with content:
mysql -u root -e "CREATE DATABASE IF NOT EXISTS development;"
Then I mounted that folder to the special location inside the docker container: I added -v $(pwd)/.scripts-db:/docker-entrypoint-initdb.d
to my docker run
command.
Then I just removed the extra step that tried to create the database in make install
, since it now gets done from the make run
command alone.
The Explanation
The problem was caused by a race condition. While the machine was initializing the MySQL instance, I was attempting to create a database. This sometimes worked, and sometimes didn’t (depending on if the initialization steps were already completed or not).
In order not to block your terminal, you usually initialize Docker instances with docker run -d
. This -d
means “detached”, and effectively runs your container as a background process.
As a result, first running make run
started the MySQL image, but that requires some initialization steps, and at the same time those initialization steps were being done, the script under make install
runs, which tries to create a database. As a consequence, I received the error mentioned above.
The solution was creating the database directly after all those initialization steps ran. The MySQL Docker official image has a way to do this (explained in The Solution section).