control docker-compose startup

Docker-compose is a powerful tool to run an isolated environment for the application. However, it doesn’t provide one important feature right out of the box which is startup control.

  • What if the containerized application requires a database to be up and ready and fully provisioned?
  • How to check if a certain process is finished in another container?

If you encounter such issues lets dive into the solution.

Database and Service in docker-compose

Assume we have a simple service phone-book-writer, which populates the PhoneBook table with some records. When this service runs it expects that phonebookdatabase and PhoneBook already exist.

Steps inside the mssql database container:

  • Wait while the sqlservr is up and running;
  • Create a new database with the name phonebookdatabase;
  • Inside the phonebookdatabase create table PhoneBook with two fields PersonName, PhoneNumber;

Steps inside the phone-book-writer service container:

  • Wait for mssql service is ready;
  • Insert new records into the PhoneBook table;

Descliamer

It’s possible to create a new database and table inside the phone-book-writer container instead but this is not what we want to achieve. imagine instead of phone-book-writer could be a .net core application or another service, which expects the database is already provisioned.

Database container

mssql container instance is based on mcr.microsoft.com/mssql/server:2019-latest docker image.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
version: '3.4'
services:
  # MSSQL
  mssql:
    image: mcr.microsoft.com/mssql/server:2019-latest
    environment:
      ACCEPT_EULA: "Y"
      SA_PASSWORD: "1234567_A"
      MSSQL_PID: Express
      DATABASE: "servicedatabase"
    ports:
      - "1433:1433"
      - "1444"
    volumes:
      - "./mssql/scripts:/tmp/scripts:ro"
    command: /bin/bash /tmp/scripts/entrypoint.sh
  # PhoneBookWriter
  # ...

Container startup script is overriten with mssql/scripts/entrypoint.sh:

1
2
3
4
5
6
# Start the script to create the DB and user
cd /tmp/scripts
bash ./configure-db.sh &

# Start SQL Server
/opt/mssql/bin/sqlservr

The & in bash ./configure-db.sh & allows to execute script in background. It means that floowing command /opt/mssql/bin/sqlservr won’t wait for configure-db.sh to complete.

The configure-db.sh has following responsibilities:

  • sqlservr is up and running;
  • phonebookdatabase is created;
  • PhoneBook is created;
  • readiness.py is started;

Startup control

readiness.py script plays the glue role between the mssql and phone-book-writer. This is a simple service which listens for specified port 1444 and responds with HTTP 200 / OK. In other words when the port 1444 is available the mssql database is up and fully provisioned.

Not working approaches

Docker-compose provides two features depends_on and healthcheck. But they are not guaranteed that service is ready.

  • depends_on only indicates that the container is running.
  • healthcheck was useful in docker-compose version 2.x but doesn’t provide expected behavior for version 3.x.

Service container

phone-book-writer service container instance is based on mcr.microsoft.com/mssql-tools docker image. However it misses some important tools to lister if port 1444 is available. Let’s add them into the PhoneBookWriter/Dockerfile:

1
2
3
4
5
6
FROM mcr.microsoft.com/mssql-tools as base

# Install wget and netcat
RUN apt-get update -y && apt-get install -y wget netcat

CMD /bin/bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
version: '3.4'
services:
  # MSSQL
  # ...
  # PhoneBookWriter
  service:
    image: phone-book-writer:latest
    build:
      context: .
      dockerfile: PhoneBookWriter/Dockerfile
    environment:
      SA_PASSWORD: "1234567_A"
      DATABASE: "servicedatabase"
    volumes:
      - "./PhoneBookWriter/scripts:/tmp/scripts:ro"
    command: /bin/bash /tmp/scripts/entrypoint.sh
    depends_on:
      - "mssql"

Container startup script PhoneBookWriter/scripts/entrypoint.sh implements following flow:

  • wait-for-it.sh mssql:1444 wait for port 1444 to be avaialable;
  • Access mssql 1433 and executes sql statements from _main.sql script;

Startup control

wait-for-it.sh listens for port 1444 in the container which is served by readiness.py script inside the mssql container.

Results

The complete solution can be found in the following repository: blog-docker-compose-control-startup

To try you need to clone the repository to your local computer:

1
2
git clone https://github.com/Igor-Pchelko/blog-docker-compose-control-startup.git
cd blog-docker-compose-control-startup

To start docker-compose and force build dockerfile:

1
docker-compose up --build

After phone-book-writer completed the PhoneBook table contain 2 records. The phonebookdatabase can be accesable via localhost with port 1433 and password 1234567_A.

To stop in interactive mode press Ctrl+C in either case use the docker-compose command:

1
docker-compose down

With the help of this approach it’s possible to create self-contained integration tests or run applications with required infrastructure such as database, event, etc.