我如何等待docker集装箱启动并运行?

当在一个容器中运行一个服务时,让我们说mongodb,这个命令

docker run -d myimage 

将立即退出,并返回容器ID。 在我的CI脚本中,我运行一个客户端来testingmongodb连接,在运行mongo容器之后。 问题是:客户端无法连接,因为服务尚未启动。 除了在我的脚本中添加一个大的sleep 10 ,我没有看到任何等待容器启动和运行的选项。

在这种情况下,Docker有一个不能工作的命令,因为容器不存在。 是docker的限制吗? 谢谢

正如Docker 1.12的一个类似问题所评论的那样

HEALTHCHECK支持根据docker/docker#23218在上游合并 – 可以考虑在订单开始下一个之前确定集装箱何时健康

这是可用的docker1.12rc3(2016-07-14)

docker-compose正在支持等待特定条件的function。

它使用libcompose (所以我不必重builddocker交互),并为此添加了一堆configuration命令。 看看这里: https : //github.com/dansteen/controlled-compose

你可以像这样在Dockerfile中使用它:

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

官方文档: https : //docs.docker.com/engine/reference/builder/#/healthcheck

如果你不想公开端口,如果你打算链接容器,并且可能运行多个实例进行testing,那么我发现这是一个很好的方法来做到这一点:)这个例子是基于等待ElasticSearch准备就绪:

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

这需要wget可用,这是Ubuntu的标准。 即使连接被拒绝,它也会在尝试之间重试5次,3秒,也不会下载任何东西。

如果您开始的容器化服务不一定对curl或wget请求做出响应(这对很多服务来说很可能),那么您可以使用nc来代替。

下面是一个主机脚本的代码片段,它启动一个Postgres容器并等待它继续使用之前:

 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 

编辑 – 这个例子并不要求你EXPOSE你正在testing的端口,因为它访问了Docker分配给容器的“专用”IP地址。 但是,这只有在docker主机守护进程正在监听环回(127.xxx)时才有效。 如果(例如)您在Mac上并运行boot2docker VM,则无法使用此方法,因为您无法从Mac shell路由到“私有”IP地址。

假设你知道你的MongoDB服务器的主机+端口(或者是因为你使用了-link ,或者是因为你使用-e注入的),你可以使用curl来检查MongoDB服务器是否在运行并接受连接。

以下片段将尝试每秒连接,直到它成功:

 #!/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" 

find这个简单的解决scheme,仍然在寻找更好的东西,猜猜现在没有:

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

把我自己的解决scheme扔在那里:

我使用dockernetworking,所以马克的netcat技巧不适合我(主机networking无法访问), Erik的想法不适用于postgres容器(容器被标记为运行即使postgres尚未可用于连接)。 所以我只是试图通过一个循环中的短暂容器连接到postgres:

 #!/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... 

我已经结束了类似的东西:

 #!/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 

我不得不接受这个想法。 在做这个任务的研究时,我到了这里,所以我想我会分享我的解决scheme与这个职位的未来访客。

基于Docker的组合解决scheme

如果你使用docker-compose,你可以看看我的docker同步POC 。 我结合了其他问题的一些想法(谢谢你 – upvoted)。

其基本思想是组合中的每个容器都暴露一个诊断服务。 调用此服务将检查容器中是否打开了所需的一组端口,并返回容器的整体状态(根据POC进行WARMUP / RUNNING)。 每个容器还有一个实用程序,用于在启动时检查相关服务是否已启动并正在运行。 只有当容器启动。

在示例docker-compose环境中,有两个服务server1server2以及等待两个服务器启动的客户端服务,然后向它们发送请求并退出。

摘录自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 

等待多个容器:

 trap 'kill $(jobs -p)' EXIT for server in $DEPENDS_ON do /assets/wait_for_server.sh $server & wait $! done 

诊断服务基本实现( 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"; 

将诊断服务连接到端口:

 nc -v -lk -p 7070 -e /assets/checkports.sh 

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

我正在testing这个端口是否在监听。 在这种情况下,我已经从容器内运行testing,但是从外部也可以从外部是否准备好mongodb。

$ docker run -p 37017:27017 -d myimage

并检查端口37017是否正在监听主机容器。

你可以使用“ wait-for-it ” 这个纯粹的bash脚本来等待主机和TCP端口的可用性,这对于同步相互依赖的服务(例如链接的docker容器)是非常有用的,因为它是一个纯粹的bash脚本,它没有任何外部依赖 “。

但是,您应该尝试devise您的服务以避免这些服务之间的这种相互依赖性。 你的服务可以尝试重新连接到数据库吗? 如果无法连接到数据库,并让容器编排器(例如Docker Swarm)为您执行此操作,您可以让容器停止工作吗?