How can I wait for a docker container to be up and

2019-03-07 21:18发布

When running a service inside a container, let's say mongodb, the command

docker run -d myimage

will exit instantly, and return the container id. In my CI script, I run a client to test mongodb connection, right after running the mongo container. The problem is: the client can't connect because the service is not up yet. Apart from adding a big sleep 10in my script, I don't see any option to wait for a container to be up and running.

Docker has a command wait which doesn't work in that case, because the container doesn't exist. Is it a limitation of docker? Thanks

标签: docker
11条回答
对你真心纯属浪费
2楼-- · 2019-03-07 21:37

I had to tackle this recetly and came up with an idea. When doing research for this task I got here, so I thought I'd share my solution with future visitors of this post.

Docker-compose-based solution

If you are using docker-compose you can check out my docker synchronization POC. I combined some of the ideas in other questions (thanks for that - upvoted).

The basic idea is that every container in the composite exposes a diagnostic service. Calling this service checks if the required set of ports is open in the container and returns the overall status of the container (WARMUP/RUNNING as per the POC). Each container also has an utility to check upon startup if the dependant services are up and running. Only then the container starts up.

In the example docker-compose environment there are two services server1 and server2 and the client service which waits for both servers to start then sends a request to both of them and exits.

Excerpt from the POC

wait_for_server.sh

#!/bin/bash

server_host=$1
sleep_seconds=5

while true; do
    echo -n "Checking $server_host status... "

    output=$(echo "" | nc $server_host 7070)

    if [ "$output" == "RUNNING" ]
    then
        echo "$server_host is running and ready to process requests."
        break
    fi

    echo "$server_host is warming up. Trying again in $sleep_seconds seconds..."
    sleep $sleep_seconds
done

Waiting for multiple containers:

trap 'kill $(jobs -p)' EXIT

for server in $DEPENDS_ON
do
    /assets/wait_for_server.sh $server &
    wait $!
done

Diagnostic srervice basic implementation (checkports.sh):

#!/bin/bash

for port in $SERVER_PORT; do
    nc -z localhost $port;

    rc=$?

    if [[ $rc != 0 ]]; then
        echo "WARMUP";
        exit;
    fi
done

echo "RUNNING";

Wiring up the diagnostic service to a port:

nc -v -lk -p 7070 -e /assets/checkports.sh
查看更多
何必那么认真
3楼-- · 2019-03-07 21:42

Docker-compose solution

After docker-compose I dont know name of docker container, so I use

docker inspect -f {{.State.Running}} $(docker-compose ps -q <CONTAINER_NAME>)

and checking true like here https://stackoverflow.com/a/33520390/7438079

查看更多
贼婆χ
4楼-- · 2019-03-07 21:47

I've ended up with something like:

#!/bin/bash

attempt=0
while [ $attempt -le 59 ]; do
    attempt=$(( $attempt + 1 ))
    echo "Waiting for server to be up (attempt: $attempt)..."
    result=$(docker logs mongo)
    if grep -q 'waiting for connections on port 27017' <<< $result ; then
      echo "Mongodb is up!"
      break
    fi
    sleep 2
done
查看更多
我欲成王,谁敢阻挡
5楼-- · 2019-03-07 21:47

Throwing my own solution out there:

I'm using docker networks so Mark's netcat trick didn't work for me (no access from the host network), and Erik's idea doesn't work for a postgres container (the container is marked as running even though postgres isn't yet available to connect to). So I'm just attempting to connect to postgres via an ephemeral container in a loop:

#!/bin/bash

docker network create my-network
docker run -d \
    --name postgres \
    --net my-network \
    -e POSTGRES_USER=myuser \
    postgres

# wait for the database to come up
until docker run --rm --net my-network postgres psql -h postgres -U myuser; do
    echo "Waiting for postgres container..."
    sleep 0.5
done

# do stuff with the database...
查看更多
We Are One
6楼-- · 2019-03-07 21:50

You can use wait-for-it, "a pure bash script that will wait on the availability of a host and TCP port. It is useful for synchronizing the spin-up of interdependent services, such as linked docker containers. Since it is a pure bash script, it does not have any external dependencies".

However, you should try to design your services to avoid these kind of interdependencies between services. Can your service try to reconnect to the database? Can you let your container just die if it can't connect to the database and let a container orchestrator (e.g. Docker Swarm) do it for you?

查看更多
登录 后发表回答