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:28

test/test_runner

#!/usr/bin/env ruby

$stdout.sync = true

def wait_ready(port)
  until (`netstat -ant | grep #{port}`; $?.success?) do
    sleep 1
    print '.'
  end
end

print 'Running supervisord'
system '/usr/bin/supervisord'

wait_ready(3000)

puts "It's ready :)"

$ docker run -v /tmp/mnt:/mnt myimage ruby mnt/test/test_runner

I'm testing like this whether the port is listening or not. In this case I have test running from inside container, but it's also possible from outside whether mongodb is ready or not.

$ docker run -p 37017:27017 -d myimage

And check whether the port 37017 is listening or not from host container.

查看更多
Lonely孤独者°
3楼-- · 2019-03-07 21:29

If the containerized service you started doesn't necessarily respond well to curl or wget requests (which is quite likely for many services) then you could use nc instead.

Here's a snippet from a host script which starts a Postgres container and waits for it to be available before continuing:

POSTGRES_CONTAINER=`docker run -d --name postgres postgres:9.3`
# Wait for the postgres port to be available
until nc -z $(sudo docker inspect --format='{{.NetworkSettings.IPAddress}}' $POSTGRES_CONTAINER) 5432
do
    echo "waiting for postgres container..."
    sleep 0.5
done

Edit - This example does not require that you EXPOSE the port you are testing, since it accesses the Docker-assigned 'private' IP address for the container. However this only works if the docker host daemon is listening on the loopback (127.x.x.x). If (for example) you are on a Mac and running the boot2docker VM, you will be unable to use this method since you cannot route to the 'private' IP addresses of the containers from your Mac shell.

查看更多
相关推荐>>
4楼-- · 2019-03-07 21:29

Assuming that you know the host+port of your MongoDB server (either because you used a -link, or because you injected them with -e), you can just use curl to check if the MongoDB server is running and accepting connections.

The following snippet will try to connect every second, until it succeeeds:

#!/bin/sh
while ! curl http://$DB_PORT_27017_TCP_ADDR:$DB_PORT_27017_TCP_PORT/
do
  echo "$(date) - still trying"
  sleep 1
done
echo "$(date) - connected successfully"
查看更多
戒情不戒烟
5楼-- · 2019-03-07 21:30

As commented in a similar issue for docker 1.12

HEALTHCHECK support is merged upstream as per docker/docker#23218 - this can be considered to determine when a container is healthy prior to starting the next in the order

This is available since docker 1.12rc3 (2016-07-14)

docker-compose is in the process of supporting a functionality to wait for specific conditions.

It uses libcompose (so I don't have to rebuild the docker interaction) and adds a bunch of config commands for this. Check it out here: https://github.com/dansteen/controlled-compose

You can use it in Dockerfile like this:

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

Official docs: https://docs.docker.com/engine/reference/builder/#/healthcheck

查看更多
Deceive 欺骗
6楼-- · 2019-03-07 21:32

Found this simple solution, been looking for something better but no luck...

until [ "`/usr/bin/docker inspect -f {{.State.Running}} CONTAINERNAME`"=="true" ]; do
    sleep 0.1;
done;

or if you want to wait until the container is reporting as healthy (assuming you have a healthcheck)

until [ "`/usr/bin/docker inspect -f {{.State.Health.Status}} CONTAINERNAME`"=="healthy" ]; do
    sleep 0.1;
done;
查看更多
贼婆χ
7楼-- · 2019-03-07 21:37

If you don't want to expose the ports, as is the case if you plan to link the container and might be running multiple instances for testing, then I found this was a good way to do it in one line :) This example is based on waiting for ElasticSearch to be ready:

docker inspect --format '{{ .NetworkSettings.IPAddress }}:9200' elasticsearch | xargs wget --retry-connrefused --tries=5 -q --wait=3 --spider

This requires wget to be available, which is standard on Ubuntu. It will retry 5 times, 3 seconds between tries, even if the connection is refused, and also does not download anything.

查看更多
登录 后发表回答