在容器内运行服务时,假设使用mongodb命令
docker run -d myimage
将立即退出,并返回容器ID。在我的CI脚本中,我在运行mongo容器之后立即运行客户端以测试mongodb连接。问题是:由于服务尚未启动,客户端无法连接。除了sleep 10
在脚本中添加大字体外,我没有看到等待容器启动并运行的任何选项。
Docker的命令wait
在这种情况下不起作用,因为该容器不存在。是docker的限制吗?
Answers:
如在docker 1.12的类似问题中所述
HEALTHCHECK
支持已根据docker / docker#23218在上游合并-可考虑在启动该订单中的下一个容器之前确定容器何时健康
自docker 1.12rc3(2016-07-14)起可用
docker-compose
正在支持等待特定条件的功能。
它使用
libcompose
(因此我不必重建docker交互)并为此添加了一堆配置命令。在这里查看:https : //github.com/dansteen/control-compose
您可以像这样在Dockerfile中使用它:
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
官方文档:https : //docs.docker.com/engine/reference/builder/#/healthcheck
找到了这个简单的解决方案,一直在寻找更好的东西,但是没有运气...
until [ "`/usr/bin/docker inspect -f {{.State.Running}} CONTAINERNAME`"=="true" ]; do
sleep 0.1;
done;
或者如果您要等到容器报告为健康状态(假设您进行了健康检查)
until [ "`/usr/bin/docker inspect -f {{.State.Health.Status}} CONTAINERNAME`"=="healthy" ]; do
sleep 0.1;
done;
while [ "`docker inspect -f {{.State.Health.Status}} $container_id`" != "healthy" ]; do sleep 2; done
/usr/bin/docker inspect -f {{.State.Running}} local_mysql
== true $睡觉0.1;完成 回声“ mysql on”
如果您不想公开端口,就像您计划链接容器并且可能正在运行多个实例进行测试的情况一样,那么我发现这是在一行中完成此操作的好方法:)此示例是基于等待ElasticSearch准备就绪:
docker inspect --format '{{ .NetworkSettings.IPAddress }}:9200' elasticsearch | xargs wget --retry-connrefused --tries=5 -q --wait=3 --spider
这需要wget可用,这在Ubuntu上是标准的。即使连接被拒绝,它也会重试5次,每次尝试之间3秒钟,并且不会下载任何内容。
--waitretry=3
而不是--wait=3
--wait=seconds Wait the specified number of seconds between the retrievals.
以及 --waitretry=seconds If you don't want Wget to wait between every retrieval, but only between retries of failed downloads, you can use this option. Wget will use linear backoff, waiting 1 second after the first failure on a given file, then waiting 2 seconds after the second failure on that file, up to the maximum number of seconds you specify.
如果您启动的容器化服务不一定对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
编辑-此示例不需要暴露正在测试的端口,因为它访问了Docker分配的容器的“私有” IP地址。但是,这仅在docker host守护进程在环回(127.xxx)上侦听时才有效。如果(例如)您在Mac上并且正在运行boot2docker VM,则将无法使用此方法,因为您无法从Mac shell路由到容器的“私有” IP地址。
--format
自此回复以来,选项已更改;现在有效的是docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [NAME|ID...]
(请参阅docs.docker.com/engine/reference/commandline/inspect上的示例)。
假设您知道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"
IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' mysql)
获取mysql容器的IP地址(其中“ mysql”是名称或容器ID),然后将URL替换为:http://$IP:3306
。为我工作!
我最终得到了这样的东西:
#!/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
把我自己的解决方案扔在那里:
我使用的是docker网络,所以Mark的netcat技巧对我不起作用(无法从主机网络访问),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...
postgres
主机无法解析,因此我docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgres
改用了。2.psql
需要密码,pg_isready
比较合适。3.有了pg_isready
,也不需要-U myuser
。
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
我正在像这样测试端口是否在监听。在这种情况下,我可以从容器内部运行测试,但是也可以从外部运行mongodb是否准备就绪。
$ docker run -p 37017:27017 -d myimage
并检查端口37017是否正在从主机容器侦听。
我不得不悄悄解决这个问题,并提出了一个想法。在为此任务做研究时,我来到了这里,所以我想与这篇文章的未来访问者分享我的解决方案。
如果您使用的是docker-compose,则可以查看我的Docker同步POC。我在其他问题中结合了一些想法(对此表示感谢-赞成)。
基本思想是,组合中的每个容器都将公开诊断服务。调用此服务将检查容器中所需的端口组是否已打开,并返回容器的整体状态(根据POC进行“ WARMUP / RUNNING”)。每个容器还具有一个实用程序,用于在启动时检查相关服务是否已启动并正在运行。只有这样,容器才能启动。
在示例docker-compose环境中,有两个服务server1和server2,客户端服务等待两个服务器启动,然后向它们发送请求并退出。
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
您可以使用wait-for-it,这是一个纯bash脚本,它将等待主机和TCP端口的可用性。它对于同步相互依赖的服务(例如链接的docker容器)的启动很有用。纯bash脚本,它没有任何“外部依赖项”。
但是,您应该尝试设计服务,以避免服务之间的这种相互依赖性。您的服务可以尝试重新连接到数据库吗?如果容器无法连接到数据库,是否可以让它死掉,并让容器协调器(例如Docker Swarm)为您完成呢?
为了验证PostgreSQL或MySQL(当前)Docker容器是否已启动并正在运行(特别是对于Flyway之类的迁移工具),可以使用wait-for二进制文件:https : //github.com/ArcanjoQueiroz/wait-for。
Docker-compose解决方案
在docker-compose之后,我不知道docker容器的名称,所以我使用
docker inspect -f {{.State.Running}} $(docker-compose ps -q <CONTAINER_NAME>)
并true
像这里 检查https://stackoverflow.com/a/33520390/7438079
对于mongoDB docker实例,我们做到了这一点,并且像一个符咒一样工作:
#!/usr/bin/env bash
until docker exec -i ${MONGO_IMAGE_NAME} mongo -u ${MONGO_INITDB_ROOT_USERNAME} -p ${MONGO_INITDB_ROOT_PASSWORD}<<EOF
exit
EOF
do
echo "Waiting for Mongo to start..."
sleep 0.5
done