Làm cách nào để tôi có thể đợi một bộ chứa docker được thiết lập và chạy?


84

Khi chạy một dịch vụ bên trong một vùng chứa, giả sử mongodb, lệnh

docker run -d myimage

sẽ thoát ngay lập tức và trả về id vùng chứa. Trong tập lệnh CI của tôi, tôi chạy một ứng dụng khách để kiểm tra kết nối mongodb, ngay sau khi chạy vùng chứa mongo. Vấn đề là: máy khách không thể kết nối vì dịch vụ chưa hoạt động. Ngoài việc thêm một đoạn mã lớn sleep 10trong tập lệnh của mình, tôi không thấy bất kỳ tùy chọn nào để chờ một vùng chứa được thiết lập và chạy.

Docker có một lệnh waitkhông hoạt động trong trường hợp đó, vì vùng chứa không tồn tại. Nó có phải là một hạn chế của docker?

Câu trả lời:


50

Như đã nhận xét trong một vấn đề tương tự cho docker 1.12

HEALTHCHECKhỗ trợ được hợp nhất ngược dòng theo docker / docker # 23218 - điều này có thể được coi là để xác định thời điểm một vùng chứa hoạt động tốt trước khi bắt đầu tiếp theo trong đơn đặt hàng

Điều này có sẵn kể từ docker 1.12rc3 (2016-07-14)

docker-composeđang trong quá trình hỗ trợ một chức năng để chờ các điều kiện cụ thể.

Nó sử dụng libcompose(vì vậy tôi không phải xây dựng lại tương tác docker) và thêm một loạt các lệnh cấu hình cho việc này. Kiểm tra nó tại đây: https://github.com/dansteen/controlled-compose

Bạn có thể sử dụng nó trong Dockerfile như sau:

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

Tài liệu chính thức: https://docs.docker.com/engine/reference/builder/#/healthcheck


Tôi đã mong đợi điều này được bình chọn cao nhất. Nhưng sau đó tôi phát hiện ra nó đã được trả lời rất gần đây.
Shiplu Mokaddim

53

Tìm thấy giải pháp đơn giản này, đang tìm kiếm thứ gì đó tốt hơn nhưng không may mắn ...

until [ "`/usr/bin/docker inspect -f {{.State.Running}} CONTAINERNAME`"=="true" ]; do
    sleep 0.1;
done;

hoặc nếu bạn muốn đợi cho đến khi vùng chứa báo cáo là khỏe mạnh (giả sử bạn đã kiểm tra sức khỏe)

until [ "`/usr/bin/docker inspect -f {{.State.Health.Status}} CONTAINERNAME`"=="healthy" ]; do
    sleep 0.1;
done;

4
thay vào đó một lớp lót sử dụng vòng lặp whilewhile [ "`docker inspect -f {{.State.Health.Status}} $container_id`" != "healthy" ]; do sleep 2; done
Mouath 21/12/18

Chỉ cần lưu ý, docker nằm trong / usr / local / bin / docker trên osx. Có thể đáng để thêm $ (docker nào) để tạo kịch bản đa nền tảng?
con--

@ con-- chắc chắn, đó sẽ là một cải tiến, mặc dù tôi coi tài liệu về "tập lệnh đa nền tảng" là mối quan tâm bên ngoài đối với phạm vi câu hỏi. Không bao giờ ít hơn, nếu bạn muốn chỉnh sửa, hãy tiếp tục :)
siêu anh hùng

đây là cách nó hoạt động với tôi: #! / bin / bash cho đến khi /usr/bin/docker inspect -f {{.State.Running}} local_mysql== true $ do sleep 0,1; làm xong; echo "mysql is on"
M.Hefny

@ M.Hefny Đó là ví dụ tương tự được thể hiện trong câu trả lời.
siêu anh hùng

32

Nếu bạn không muốn để lộ các cổng, như trường hợp bạn định liên kết vùng chứa và có thể đang chạy nhiều phiên bản để thử nghiệm, thì tôi thấy đây là một cách tốt để làm điều đó trong một dòng :) Ví dụ này là dựa trên việc đợi ElasticSearch sẵn sàng:

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

Điều này yêu cầu wget phải có sẵn, đây là tiêu chuẩn trên Ubuntu. Nó sẽ thử lại 5 lần, 3 giây giữa các lần thử, ngay cả khi kết nối bị từ chối và cũng không tải xuống bất kỳ thứ gì.


Tôi nghĩ rằng bạn muốn sử dụng --waitretry=3thay vì--wait=3
jpbochi

cho trang người đàn ông tò mò, wget --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.
không có mục đích

25

Nếu dịch vụ container mà bạn bắt đầu không nhất thiết phải đáp ứng tốt các yêu cầu curl hoặc wget (rất có thể xảy ra đối với nhiều dịch vụ) thì bạn có thể sử dụng ncthay thế.

Đây là một đoạn mã từ tập lệnh máy chủ bắt đầu một vùng chứa Postgres và đợi nó có sẵn trước khi tiếp tục:

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

Chỉnh sửa - Ví dụ này không yêu cầu bạn EXPOSE cổng bạn đang thử nghiệm, vì nó truy cập địa chỉ IP 'riêng tư' do Docker gán cho vùng chứa. Tuy nhiên, điều này chỉ hoạt động nếu daemon máy chủ docker đang lắng nghe trên loopback (127.xxx). Nếu (ví dụ) bạn đang sử dụng máy Mac và chạy máy ảo boot2docker, bạn sẽ không thể sử dụng phương pháp này vì bạn không thể định tuyến đến các địa chỉ IP 'riêng tư' của các vùng chứa từ trình bao Mac của bạn.


Tôi tin rằng --formattùy chọn đã thay đổi kể từ câu trả lời này; những gì hoạt động bây giờ là docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [NAME|ID...](xem ví dụ trên docs.docker.com/engine/reference/commandline/inspect ).
Kurt Peek

16

Giả sử rằng bạn biết máy chủ + cổng của máy chủ MongoDB của mình (do bạn đã sử dụng -linkhoặc do bạn đã chèn chúng vào -e), bạn chỉ có thể sử dụng curlđể kiểm tra xem máy chủ MongoDB có đang chạy và chấp nhận kết nối hay không.

Đoạn mã sau sẽ cố gắng kết nối mỗi giây, cho đến khi nó thành công:

#!/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
Nhưng bạn cần để ràng buộc các cổng trên máy chủ :(
Gravis

Tôi gặp vấn đề tương tự. Cố gắng thiết lập theo dõi bằng cách sử dụng pidfiles và không thể kích hoạt một sự kiện chi tiết trên Docker start / stop mà không đưa vars theo cách thủ công là một điều khó khăn, điều đó có nghĩa là tôi không thể chỉ viết các trình bao bọc chung chung một cách dễ dàng.
Alex Lynham

Bạn có thể đi với IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' mysql)để có được địa chỉ IP của container mysql của bạn (nơi "mysql" là tên hoặc container id) và thay thế các url với: http://$IP:3306. làm việc cho tôi!
Danyel

12

Tôi đã kết thúc với một cái gì đó như:

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

Đưa giải pháp của riêng tôi ra khỏi đó:

Tôi đang sử dụng mạng docker nên thủ thuật netcat của Mark không hoạt động với tôi (không có quyền truy cập từ mạng chủ) và ý tưởng của Erik không hoạt động với vùng chứa postgres (vùng chứa được đánh dấu là đang chạy mặc dù chưa có postgres có sẵn để kết nối với). Vì vậy, tôi chỉ đang cố gắng kết nối với postgres thông qua một vùng chứa tạm thời trong một vòng lặp:

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

Đó là những gì chúng ta đang làm hôm nay. Hãy cẩn thận với hình ảnh postgres, vì máy chủ sẽ khởi động một lần, trước khi khởi động lại ...
Gravis

Điều này hoạt động tốt, với một vài sửa đổi nhỏ. 1. postgresmáy chủ không được giải quyết, vì vậy tôi sử dụng docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgresthay thế. 2. psqlcần một mật khẩu, pg_isreadylà một phù hợp tốt hơn. 3. Với pg_isreadycũng không cần trong -U myuser.
Alec Mev

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

Tôi đang kiểm tra như thế này xem cổng có đang nghe hay không. Trong trường hợp này, tôi đã chạy thử nghiệm từ bên trong vùng chứa, nhưng cũng có thể từ bên ngoài cho dù mongodb đã sẵn sàng hay chưa.

$ docker run -p 37017:27017 -d myimage

Và kiểm tra xem cổng 37017 có đang lắng nghe từ vùng chứa máy chủ hay không.


2

Tôi phải giải quyết khoản thu này và nảy ra một ý tưởng. Khi thực hiện nghiên cứu cho nhiệm vụ này, tôi đã nhận được ở đây, vì vậy tôi nghĩ tôi sẽ chia sẻ giải pháp của mình với những khách truy cập trong tương lai của bài đăng này.

Giải pháp dựa trên Docker-soạn

Nếu bạn đang sử dụng docker-comp, bạn có thể xem POC đồng bộ hóa docker của tôi . Tôi đã kết hợp một số ý tưởng trong các câu hỏi khác (cảm ơn vì điều đó - đã ủng hộ).

Ý tưởng cơ bản là mọi thùng chứa trong hỗn hợp đều thể hiện một dịch vụ chẩn đoán. Việc gọi dịch vụ này sẽ kiểm tra xem tập hợp các cổng được yêu cầu có đang mở trong vùng chứa hay không và trả về trạng thái tổng thể của vùng chứa (WARMUP / RUNNING theo POC). Mỗi vùng chứa cũng có một tiện ích để kiểm tra khi khởi động xem các dịch vụ phụ thuộc có đang hoạt động hay không. Chỉ sau đó thùng chứa mới khởi động.

Trong môi trường docker-soạn ví dụ, có hai dịch vụ server1server2 và dịch vụ khách sẽ đợi cả hai máy chủ khởi động sau đó gửi yêu cầu đến cả hai và thoát.

Trích từ 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

Đang chờ nhiều vùng chứa:

trap 'kill $(jobs -p)' EXIT

for server in $DEPENDS_ON
do
    /assets/wait_for_server.sh $server &
    wait $!
done

Triển khai cơ bản dịch vụ chẩn đoán ( 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";

Kết nối dịch vụ chẩn đoán với một cổng:

nc -v -lk -p 7070 -e /assets/checkports.sh

Cách tiếp cận thú vị. +1
VonC

1

Bạn có thể sử dụng wait-for-it ", một tập lệnh bash thuần túy sẽ đợi tính sẵn có của máy chủ lưu trữ và cổng TCP. Nó rất hữu ích cho việc đồng bộ hóa tập hợp các dịch vụ phụ thuộc lẫn nhau, chẳng hạn như các vùng chứa docker được liên kết. Vì nó là một tập lệnh bash thuần túy, nó không có bất kỳ phụ thuộc bên ngoài nào ".

Tuy nhiên, bạn nên cố gắng thiết kế các dịch vụ của mình để tránh những loại phụ thuộc lẫn nhau giữa các dịch vụ. Dịch vụ của bạn có thể thử kết nối lại với cơ sở dữ liệu không? Bạn có thể để bộ chứa của mình chết nếu nó không thể kết nối với cơ sở dữ liệu và để một bộ điều phối vùng chứa (ví dụ: Docker Swarm) làm điều đó cho bạn?



0

Giải pháp soạn thư docker

Sau docker-soạn, tôi không biết tên của bộ chứa docker, vì vậy tôi sử dụng

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

và kiểm tra truenhư tại đây https://stackoverflow.com/a/33520390/7438079


0

Đối với phiên bản docker mongoDB, chúng tôi đã làm điều này và hoạt động giống như một sự quyến rũ:

#!/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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.