Docker 컨테이너가 실행될 때까지 어떻게 기다릴 수 있습니까?


84

컨테이너 내부에서 서비스를 실행할 때 mongodb, 명령

docker run -d myimage

즉시 종료하고 컨테이너 ID를 반환합니다. 내 CI 스크립트에서 mongo 컨테이너를 실행 한 직후 클라이언트를 실행하여 mongodb 연결을 테스트합니다. 문제는 서비스가 아직 시작되지 않았기 때문에 클라이언트가 연결할 수 없다는 것입니다. sleep 10내 스크립트에 큰 것을 추가하는 것 외에 컨테이너가 실행될 때까지 기다리는 옵션이 없습니다.

Docker에는 wait컨테이너가 없기 때문에이 경우 작동하지 않는 명령 이 있습니다. 도커의 한계입니까?

답변:


50

docker 1.12 에 대한 유사한 문제 에서 언급했듯이

HEALTHCHECK지원은 도커 / 도커에 따라 업스트림으로 병합 됩니다.

docker 1.12rc3 (2016-07-14) 부터 사용할 수 있습니다.

docker-compose프로세스에 특정 조건 기다려야하는 기능을 지원.

그것은 libcompose(그래서 나는 도커 상호 작용을 다시 만들 필요가 없음)이를 위해 많은 구성 명령을 추가합니다. 여기에서 확인하세요 : 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


나는 이것이 가장 높은 득표율을 기대하고 있었다. 그러나 나는 그것이 아주 최근에 답변되었다는 것을 발견했습니다.
Shiplu Mokaddim

53

이 간단한 해결책을 찾았고 더 나은 것을 찾고 있었지만 운이 없었습니다 ...

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;

4
대신 while 루프를 사용하는 라이너 1 개while [ "`docker inspect -f {{.State.Health.Status}} $container_id`" != "healthy" ]; do sleep 2; done
Mouath

참고로, docker는 osx의 / usr / local / bin / docker에 있습니다. 스크립트를 크로스 플랫폼으로 만들기 위해 $ (어떤 도커)를 추가 할 가치가 있습니까?
con--

@ con-- 확실히 개선 될 것입니다. "크로스 플랫폼 스크립트"에 대한 문서는 질문의 범위에 대한 외부 관심사로 간주합니다. 편집을하고
superhero

이것이 저와 함께 일한 방법입니다 : #! / bin / bash until /usr/bin/docker inspect -f {{.State.Running}} local_mysql== true $ do sleep 0.1; 끝난; echo "mysql is on"
M.Hefny

@ M.Hefny 답에 표현 된 것과 같은 예입니다.
슈퍼 히어로

32

컨테이너를 연결할 계획이고 테스트를 위해 여러 인스턴스를 실행할 수있는 경우와 같이 포트를 노출하지 않으려면 이것이 한 줄로 수행하는 좋은 방법이라는 것을 알았습니다. :)이 예제는 다음과 같습니다. ElasticSearch가 준비 될 때까지 기다림을 기준으로합니다.

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

이를 위해서는 Ubuntu에서 표준 인 wget을 사용할 수 있어야합니다. 연결이 거부 되더라도 5 회, 3 초 간격으로 재 시도하고 아무것도 다운로드하지 않습니다.


난 당신이 사용하고자하는 생각 --waitretry=3대신에--wait=3
jpbochi

호기심 많은 wget man 페이지 --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.
aimless

25

시작한 컨테이너화 된 서비스가 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 주소에 액세스하므로 테스트중인 포트를 노출 할 필요가 없습니다. 그러나 이것은 도커 호스트 데몬이 루프백 (127.xxx)을 수신하는 경우에만 작동합니다. 예를 들어 Mac에서 boot2docker VM을 실행하는 경우 Mac 셸에서 컨테이너의 '비공개'IP 주소로 라우팅 할 수 없기 때문에이 방법을 사용할 수 없습니다.


--format이 답변 이후 옵션이 변경 되었다고 생각합니다 . 지금 작동하는 것은 docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [NAME|ID...]( docs.docker.com/engine/reference/commandline/inspect 의 예제 참조 )입니다.
Kurt Peek

16

MongoDB 서버의 호스트 + 포트를 알고 있다고 가정하면 (을 사용했거나을 -link주입했기 때문에 -e) curlMongoDB 서버가 실행 중이고 연결을 수락하는지 확인하는 데 사용할 수 있습니다 .

다음 스 니펫은 성공할 때까지 매초마다 연결을 시도합니다.

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

1
하지만 호스트의 포트를 바인딩해야합니다. (
Gravis

비슷한 문제가 있습니다. pidfiles를 사용하여 monit을 설정하려고 시도하고 수동으로 vars를 주입하지 않고 Docker 시작 / 중지에서 세부적인 이벤트를 트리거 할 수 없다는 것은 고통 스럽습니다. 즉, 일반 래퍼를 쉽게 작성할 수 없다는 의미입니다.
Alex Lynham

당신은 갈 수 있습니다 IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' mysql)( "MySQL은"이름 또는 컨테이너 ID입니다) MySQL의 컨테이너의 IP 주소를 얻을에 URL을 대체 : http://$IP:3306. 나를 위해 작동합니다!
Danyel 2014 년

12

나는 다음과 같은 결과를 얻었습니다.

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

10

내 자신의 솔루션을 던지고 있습니다.

도커 네트워크를 사용하고 있으므로 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 이미지에주의하십시오 ...
Gravis

이것은 약간의 수정만으로 잘 작동합니다. 1. postgres호스트가 해결되지 않아 docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgres대신 사용 합니다. 2. psql암호 pg_isready가 필요하며 더 적합합니다. 3. pg_isready에서 필요도 없습니다 -U myuser.
Alec Mev 2018

2

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이 호스트 컨테이너에서 수신 중인지 확인하십시오.


2

이 문제를 해결해야했고 아이디어가 떠 올랐습니다. 이 작업에 대한 조사를 할 때 여기에 왔기 때문에이 게시물의 향후 방문자와 내 솔루션을 공유 할 것이라고 생각했습니다.

Docker 작성 기반 솔루션

docker-compose를 사용하는 경우 내 도커 동기화 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

흥미로운 접근 방식. +1
VonC 2017

1

당신이 사용할 수있는 대기를 들어 - 그것 , " 호스트와 TCP 포트의 가용성에 대기 순수 bash는 스크립트. 그것은 스핀 업 링크 된 고정 표시기 용기와 같은 상호 의존적 인 서비스의 동기화하는 데 유용합니다. 그것은을하기 때문에 순수한 bash 스크립트, 외부 종속성이 없습니다 .

그러나 이러한 종류의 서비스 간의 상호 종속성을 피하기 위해 서비스를 디자인해야합니다. 서비스가 데이터베이스에 다시 연결을 시도 할 수 있습니까? 데이터베이스에 연결할 수없는 경우 컨테이너가 죽도록 내버려두고 컨테이너 오케 스트레이터 (예 : Docker Swarm)가 대신 수행하도록 할 수 있습니까?




0

mongoDB 도커 인스턴스의 경우이 작업을 수행했으며 매력처럼 작동합니다.

#!/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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.