मैं एक डॉक कंटेनर के उठने और चलने का इंतजार कैसे कर सकता हूं?


84

एक कंटेनर के अंदर एक सेवा को चलाने के दौरान, कमांड को मोंगोडब कहते हैं

docker run -d myimage

तुरंत बाहर निकल जाएगा, और कंटेनर आईडी वापस कर देगा। मेरी सीआई स्क्रिप्ट में, मैं एक ग्राहक को मूंगोडब कनेक्शन का परीक्षण करने के लिए चलाता हूं, सही मूंगो कंटेनर को चलाने के बाद। समस्या यह है: ग्राहक कनेक्ट नहीं कर सकता क्योंकि सेवा अभी तक नहीं है। sleep 10अपनी स्क्रिप्ट में एक बड़ा जोड़ने के अलावा , मुझे कंटेनर के उठने और चलने का इंतजार करने का कोई विकल्प नहीं दिखता है।

डॉकर के पास एक कमांड है waitजो उस स्थिति में काम नहीं करता है, क्योंकि कंटेनर मौजूद नहीं है। क्या यह कर्ता की मर्यादा है?

जवाबों:


50

जैसा कि डॉकटर १.१२ में इसी तरह के मुद्दे पर टिप्पणी की गई है

HEALTHCHECKसमर्थन को docker / docker # 23218 के अनुसार अपस्ट्रीम में मिला दिया जाता है - यह तब निर्धारित किया जा सकता है जब एक कंटेनर क्रम में अगला शुरू करने से पहले स्वस्थ हो

यह 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


मैं उम्मीद कर रहा था कि यह सबसे ज्यादा मतदान होगा। लेकिन तब मुझे पता चला कि इसका उत्तर हाल ही में दिया गया था।
शिप्लू मोकादिम

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 [ "`docker inspect -f {{.State.Health.Status}} $container_id`" != "healthy" ]; do sleep 2; done
Mouath

बस एक नोट, डॉक ऑस्कर में / usr / स्थानीय / बिन / डॉक में है। स्क्रिप्ट को क्रॉस-प्लेटफ़ॉर्म बनाने के लिए $ (जो डॉकटर) जोड़ने लायक हो सकता है?
con--

@ con-- यकीन है, यह एक सुधार होगा, हालांकि मैं "क्रॉस प्लेटफ़ॉर्म स्क्रिप्ट" के प्रलेखन को प्रश्न के दायरे में बाहरी चिंता मानता हूं। कभी भी कम न करें, यदि आप एक संपादन करना पसंद करते हैं, तो इसके लिए जाएं :)
सुपरहीरो

इस तरह से मेरे साथ काम किया है: #! / bin / bash तक /usr/bin/docker inspect -f {{.State.Running}} local_mysql== true $ do sleep 0.1; किया हुआ; इको "mysql चालू है"
M.Hefny

@ M.Hefny वही उदाहरण है जो उत्तर में व्यक्त किया गया है।
सुपर हीरो

32

यदि आप बंदरगाहों को उजागर नहीं करना चाहते हैं, तो जैसा कि आप कंटेनर को जोड़ने की योजना बना रहे हैं और परीक्षण के लिए कई उदाहरण चला रहे हैं, तो मैंने पाया कि यह एक लाइन में करने का एक अच्छा तरीका था :) यह उदाहरण है तैयार होने के लिए ElasticSearch की प्रतीक्षा पर आधारित:

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

इसके लिए उपलब्ध होना आवश्यक है, जो उबंटू पर मानक है। यह 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.
लक्ष्यहीन

25

यदि आपने जो कंटेनरीकृत सेवा शुरू की है, वह जरूरी नहीं है कि आप कर्ल या वॉग रिक्वेस्ट (जो कई सेवाओं के लिए काफी संभव हो) का जवाब दें, तो आप ncइसके बजाय उपयोग कर सकते हैं ।

यहाँ एक होस्ट स्क्रिप्ट से एक स्निपेट है जो एक पोस्टग्रेज कंटेनर शुरू करता है और इसे जारी रखने से पहले उपलब्ध होने की प्रतीक्षा करता है:

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

संपादित करें - इस उदाहरण की आवश्यकता नहीं है कि आप जिस पोर्ट का परीक्षण कर रहे हैं, उसे निर्यात करें क्योंकि यह कंटेनर के लिए डॉकर द्वारा निर्दिष्ट 'निजी' आईपी पते तक पहुंचता है। हालांकि यह केवल तभी काम करता है जब डॉक होस्ट डेमॉन लूपबैक (127.xxx) पर सुन रहा हो। यदि (उदाहरण के लिए) आप एक मैक पर हैं और boot2docker VM चला रहे हैं, तो आप इस विधि का उपयोग करने में असमर्थ होंगे क्योंकि आप अपने मैक शेल से कंटेनरों के 'निजी' आईपी पतों पर नहीं जा सकते।


मेरा मानना ​​है कि --formatइस उत्तर के बाद से विकल्प बदल गया है; अब क्या काम करता है docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [NAME|ID...]( docs.docker.com/engine/reference/commandline/inspect पर उदाहरण देखें )।
कर्ट पीक

16

यह मानते हुए कि आप अपने 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"

1
लेकिन आपको मेजबान पर बंदरगाह को बांधने की जरूरत है :(
ग्रेविस

मुझे भी इसी तरह की समस्या है। Pidfiles का उपयोग करके मोनिट सेट करने की कोशिश करना और डॉकटर स्टार्ट पर एक दानेदार घटना को ट्रिगर करने में सक्षम नहीं होना / मैन्युअल रूप से इंजेक्शन लगाने के बिना स्टॉप एक दर्द है, इसका मतलब है कि मैं बस जेनेरिक रैपर आसानी से नहीं लिख सकता।
एलेक्स लिन्हम

आप के साथ जा सकते IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' mysql)अपने mysql कंटेनर (जहां "mysql" नाम या कंटेनर आईडी है) का IP पता हो और साथ यूआरएल को बदलने के लिए: http://$IP:3306। मेरे लिये कार्य करता है!
डैनियल

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

मेरा अपना समाधान वहाँ फेंकना:

मैं docker नेटवर्कों का उपयोग कर रहा हूँ, इसलिए मार्क की netcat ट्रिक ने मेरे लिए काम नहीं किया (होस्ट नेटवर्क से कोई पहुंच नहीं), और एरिक का विचार एक पोस्टग्रैज कंटेनर के लिए काम नहीं करता है (कंटेनर को चिह्नित किया जा रहा है भले ही पोस्टग्रेज अभी तक नहीं है से कनेक्ट करने के लिए उपलब्ध है)। तो मैं एक लूप में एक अल्पकालिक कंटेनर के माध्यम से पोस्टग्रेज से कनेक्ट करने का प्रयास कर रहा हूं:

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

आज हम यही कर रहे हैं। पोस्टग्रेज छवि के साथ सावधान रहें, चूंकि सर्वर एक बार शुरू हो रहा है, पुनरारंभ करने से पहले ...
Gravis

यह कुछ मामूली संशोधनों के साथ अच्छी तरह से काम करता है। 1. postgresहोस्ट हल नहीं होता है, इसलिए मैं docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgresइसके बजाय उपयोग करता हूं । 2. psqlएक पासवर्ड की जरूरत है, pg_isreadyएक बेहतर फिट है। 3. pg_isreadyइसमें भी कोई जरूरत नहीं है -U myuser
एलेक मेव

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

मैं इस तरह का परीक्षण कर रहा हूं कि बंदरगाह सुन रहा है या नहीं। इस मामले में मेरे पास कंटेनर के अंदर से परीक्षण चल रहा है, लेकिन यह बाहर से भी संभव है कि मोंगोडब तैयार है या नहीं।

$ docker run -p 37017:27017 -d myimage

और जांचें कि क्या पोर्ट 37017 होस्ट कंटेनर से सुन रहा है या नहीं।


2

मुझे इस पुनरावृत्ति से निपटना था और एक विचार आया। जब मैं इस कार्य के लिए शोध कर रहा था, तो मैंने सोचा कि मैं इस पोस्ट के भविष्य के आगंतुकों के साथ अपना समाधान साझा करूंगा।

डॉकर-रचना-आधारित समाधान

यदि आप docker- रचना का उपयोग कर रहे हैं, तो आप मेरे docker सिंक्रोनाइज़ेशन POC की जाँच कर सकते हैं । मैंने अन्य प्रश्नों में कुछ विचारों को संयुक्त किया (इसके लिए धन्यवाद - उत्थित)।

मूल विचार यह है कि समग्र में प्रत्येक कंटेनर एक नैदानिक ​​सेवा को उजागर करता है। इस सेवा को कॉल करना यह जांचता है कि पोर्ट में आवश्यक सेट कंटेनर में खुला है या कंटेनर की समग्र स्थिति (WOCUP / RUNNING POC के अनुसार) वापस कर देता है। प्रत्येक कंटेनर में स्टार्टअप पर जांच करने की एक उपयोगिता भी होती है, यदि आश्रित सेवाएँ ऊपर और चल रही हों। तभी कंटेनर स्टार्ट होता है।

उदाहरण के लिए 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

नैदानिक ​​srervice बुनियादी कार्यान्वयन ( 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

1

आप वेट-फॉर- इट का उपयोग कर सकते हैं , " एक शुद्ध बैश स्क्रिप्ट जो एक होस्ट और टीसीपी पोर्ट की उपलब्धता पर प्रतीक्षा करेगी। यह अन्योन्याश्रित सेवाओं के स्पिन-अप को सिंक्रनाइज़ करने के लिए उपयोगी है, जैसे कि लिंक किए गए डॉकटर कंटेनर। शुद्ध बैश स्क्रिप्ट, इसमें कोई बाहरी निर्भरता नहीं है "।

हालांकि, आपको सेवाओं के बीच इस तरह की अन्योन्याश्रितताओं से बचने के लिए अपनी सेवाओं को डिजाइन करने का प्रयास करना चाहिए। क्या आपकी सेवा डेटाबेस को फिर से जोड़ने की कोशिश कर सकती है? क्या आप अपने कंटेनर को बस मरने दे सकते हैं अगर वह डेटाबेस से नहीं जुड़ सकता है और आपके लिए एक कंटेनर ऑर्केस्ट्रेटर (जैसे डोकर झुंड) को ऐसा करने देता है?


1

यह सत्यापित करने के लिए कि क्या एक PostgreSQL या MySQL (वर्तमान में) डॉकर कंटेनर ऊपर है और चल रहा है (विशेष रूप से फ्लाईवे जैसे माइग्रेशन टूल के लिए), आप प्रतीक्षा के लिए बाइनरी का उपयोग कर सकते हैं : https://github.com/ArcanjoQueiroz-wait-for


0

डॉकटर-रचना समाधान

डॉकटर-कंपोज के बाद मुझे डॉकटर कंटेनर का नाम नहीं पता है, इसलिए मैं उपयोग करता हूं

docker inspect -f {{.State.Running}} $(docker-compose ps -q <CONTAINER_NAME>)

और trueयहाँ की तरह की जाँच https://stackoverflow.com/a/33520390/7438079


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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.