rreay724
rreay724

Reputation: 183

Bash script command to wait until docker-compose process has finished before moving on

We have a bash script that installs a program, which includes running a docker-compose file. One of the services, RabbitMQ, takes some time to load and I need a command to wait until it's loaded before the other services are loaded. We were using a sleep command, but our customers use different laptops, so it takes longer to load on some than others. Is there a way to just hold it until it finishes loading the service before moving on to the next without using the sleep command? I have included the part of the script below. Thanks!

# Execute applications
cd /opt/program
docker-compose up -d
echo "waiting for message queue..."
sleep 15

echo "starting ingest manager"

cd /opt/program/scripts
chmod +x start-manager.sh
./start-manager.sh &

Upvotes: 8

Views: 15321

Answers (3)

acran
acran

Reputation: 9018

As already stated in the other answers you'll have to do an application-specific readiness-check for your container. Personally I prefer to provide these checks/scripts with the container image, e.g. by adding the wait-for-it.sh (see ErikMD's answer) or similar scripts to the image and executing them within the running container e.g. with docker exec (as proposed by Ahmed Arafa's answer).

This has some advantages over running the check on the host:

  • you can provide all required scripts and dependencies with the container image
  • you don't need to make any assumptions about the host (e.g. when testing via an api endpoint: is wget/curl available on the host, or even a bash/shell? Is the docker/docker-compose command executed on the same host as the docker deamon, i.e. could you reach the container via localhost?)
  • you don't have to expose any ports/endpoints to the outside world only for checking container status
  • you can provide different check scripts with different versions of the image without having to modify the start script

So, to apply this method to your example, simply add a script - e.g. is_ready.sh - to the image, execute it within the container with docker-compose exec and act upon its exit status:

# Execute applications
cd /opt/program
docker-compose up -d
echo "waiting for message queue..."

while ! docker-compose exec rabbitmq /is_ready.sh; do sleep 1; done

echo "starting ingest manager"

cd /opt/program/scripts
chmod +x start-manager.sh
./start-manager.sh &

where is_ready.sh may look like this:

#!/bin/bash

rabbitmqctl status

Going even further down this road you may leverage the native healtcheck feature of docker and docker-compose. With these docker will automatically execute the defined healthcheck script/command and indicate the current health in the container status.

Incorporated into your script this could look like:

# Execute applications
cd /opt/program
docker-compose up -d
echo "waiting for message queue..."

is_healthy() {
    service="$1"
    container_id="$(docker-compose ps -q "$service")"
    health_status="$(docker inspect -f "{{.State.Health.Status}}" "$container_id")"

    if [ "$health_status" = "healthy" ]; then
        return 0
    else
        return 1
    fi
}

while ! is_healthy rabbitmq; do sleep 1; done

echo "starting ingest manager"

cd /opt/program/scripts
chmod +x start-manager.sh
./start-manager.sh &

with the healthcheck defined in the docker-compose.yml

...
services:
  rabbitmq:
    ...
    healtcheck:
      test: rabbitmqctl status      

For more complex healthchecks you can also add a longer script to the image and execute that instead.

Upvotes: 8

Ahmed Arafa
Ahmed Arafa

Reputation: 500

First you need to determine specific name for rabbitmq container in your docker-compose.yaml by adding:

version: "3"

services:
        web:
                image: rabbitmq:latest
                container_name: rabbitmq

to service block

And script will be like:

#!/bin/bash
cd /opt/program
docker-compose up -d
echo "waiting for message queue..."

rabbitmq_status() { 
        docker exec -it rabbitmq rabbitmqctl status
}

rabbitmq_status

if [ $? -eq 0 ]
then
        echo "starting ingest manager"
        cd /opt/program/scripts
        chmod +x start-manager.sh
        ./start-manager.sh &
else
        echo "rabbitmq container is not running"
fi

Which will not move to the next step till rabbitmq container is up and running.

Upvotes: 1

ErikMD
ErikMD

Reputation: 14753

The docker-compose up -d command is run in the background in "detached mode", but I guess that your setup exposes at least one web service… If yes, you can rely on a tool such as wait-for-it to precisely wait for this service to be ready.

First, download and inspect this script:

https://github.com/vishnubob/wait-for-it/raw/master/wait-for-it.sh

Next, assuming your docker-compose.yml setup exposes a web service at localhost:9090 with:

version: 3
services:
  frontend:
    image: user/image
    ports:
      - '9090:8080'

You could refactor your Bash script (named e.g. run.sh) as follows:

#!/bin/bash

# retrieve and remember the directory where is stored this script
srcdir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )
# we assume the script "wait-for-it.sh" is also in this directory

# Execute applications
cd /opt/program
docker-compose up -d

# Change the working directory now
cd /opt/program/scripts
chmod +x start-manager.sh

echo "waiting for message queue..."
"$srcdir/wait-for-it.sh" -h localhost -p 9090 -s -t 0 -- ./start-manager.sh

For more details, some online doc. is available at https://github.com/vishnubob/wait-for-it#readme

Upvotes: 1

Related Questions