mongodb 如何等待 docker 容器启动并运行?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21183088/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 13:33:36  来源:igfitidea点击:

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

mongodbdocker

提问by Gravis

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

在容器内运行服务时,假设 mongodb,命令

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.

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

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

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

采纳答案by VonC

As commented in a similar issuefor docker 1.12

正如在docker 1.12的类似问题中所评论的

HEALTHCHECKsupport 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

HEALTHCHECK根据docker/docker#23218将支持合并到上游- 这可以被认为是在开始顺序中的下一个容器之前确定容器何时健康

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

这从docker 1.12rc3 (2016-07-14) 开始可用

docker-composeis in the processof supporting a functionality to wait for specific conditions.

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

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

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

You can use it in Dockerfile like this:

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

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

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

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

回答by superhero

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;

回答by David Lemphers

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:

如果您不想公开端口,就像您计划链接容器并且可能运行多个实例进行测试一样,那么我发现这是在一行中完成的好方法:) 这个例子是基于等待 ElasticSearch 准备就绪:

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.

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

回答by Mark Henwood

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 ncinstead.

如果您启动的容器化服务不一定能很好地响应 curl 或 wget 请求(这对于许多服务来说很可能),那么您可以nc改用。

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

这是主机脚本的一个片段,它启动 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

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. Howeverthis 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.

编辑- 此示例不要求您公开您正在测试的端口,因为它访问 Docker 为容器分配的“私有”IP 地址。但是,这仅在 docker 主机守护程序正在侦听环回 (127.xxx) 时才有效。如果(例如)您在 Mac 上运行 boot2docker VM,您将无法使用此方法,因为您无法从 Mac shell 路由到容器的“私有”IP 地址。

回答by jpetazzo

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 curlto check if the MongoDB server is running and accepting connections.

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

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"

回答by Gravis

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

回答by Ell Neal

Throwing my own solution out there:

抛出我自己的解决方案:

I'm using docker networks so Mark's netcat trickdidn't work for me (no access from the host network), and Erik's ideadoesn'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:

我正在使用 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...

回答by banyan

test/test_runner

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

$ 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.

我正在像这样测试端口是否正在侦听。在这种情况下,我从容器内部运行测试,但无论 mongodb 是否准备就绪,也可以从外部运行。

$ docker run -p 37017:27017 -d myimage

$ docker run -p 37017:27017 -d myimage

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

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

回答by jannis

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

基于 Docker-compose 的解决方案

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).

如果您正在使用 docker-compose,您可以查看我的 docker 同步 POC。我结合了其他问题中的一些想法(感谢 - 赞成)。

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.

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

In the example docker-compose environment there are two services server1and server2and the clientservice which waits for both servers to start then sends a request to both of them and exits.

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

Excerpt from the POC

摘自 POC

wait_for_server.sh

wait_for_server.sh

#!/bin/bash

server_host=
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):

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

回答by Guido

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".

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

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?

但是,您应该尝试设计您的服务以避免服务之间的这种相互依赖性。您的服务可以尝试重新连接到数据库吗?如果您的容器无法连接到数据库,您是否可以让您的容器死亡并让容器编排器(例如 Docker Swarm)为您完成?