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 환경에는 두 개의 서비스 server1 및 server2가 있으며 두 서버가 시작될 때까지 대기 한 다음 두 서버 모두에 요청을 보내고 종료 하는 클라이언트 서비스가 있습니다.

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.