Docker Compose在启动Y之前等待容器X


324

我正在从这里使用Rabbitmq和一个简单的python示例以及docker -compose。我的问题是我需要等待rabbitmq完全启动。从到目前为止的搜索结果来看,我不知道如何等待容器x(在我的情况下为worker)启动y(rabbitmq)。

我发现此博文中他检查其他主机是否在线。我还发现了这个docker命令

等待

用法:泊坞窗等待CONTAINER [CONTAINER ...]

阻塞直到容器停止,然后打印其退出代码。

等待容器停止可能不是我想要的,但是如果是,可以在docker-compose.yml中使用该命令吗?到目前为止,我的解决方案是等待几秒钟并检查端口,但这是实现此目的的方法吗?如果我不等,我会报错。

docker-compose.yml

worker:
    build: myapp/.
    volumes:
    - myapp/.:/usr/src/app:ro

    links:
    - rabbitmq
rabbitmq:
    image: rabbitmq:3-management

python hello示例(rabbit.py):

import pika
import time

import socket

pingcounter = 0
isreachable = False
while isreachable is False and pingcounter < 5:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.connect(('rabbitmq', 5672))
        isreachable = True
    except socket.error as e:
        time.sleep(2)
        pingcounter += 1
    s.close()

if isreachable:
    connection = pika.BlockingConnection(pika.ConnectionParameters(
            host="rabbitmq"))
    channel = connection.channel()

    channel.queue_declare(queue='hello')

    channel.basic_publish(exchange='',
                          routing_key='hello',
                          body='Hello World!')
    print (" [x] Sent 'Hello World!'")
    connection.close()

工人的Dockerfile:

FROM python:2-onbuild
RUN ["pip", "install", "pika"]

CMD ["python","rabbit.py"]

2015年11月更新

Shell脚本或在程序内部等待可能是一种解决方案。但是看到此问题后,我正在寻找docker / docker-compose本身的命令或功能。

他们提到了实施健康检查的解决方案,这可能是最佳选择。打开的tcp连接并不意味着您的服务已准备就绪或可能保持就绪。除此之外,我需要在dockerfile中更改入口点。

因此,我希望对docker-compose on board命令有一个答案,希望他们能解决此问题。

2016年3月更新

提议提供一种确定容器是否“有效”的内置方式。因此,docker-compose可能会在不久的将来使用它。

2016年6月更新

健康检查似乎将在1.12.0版中集成到docker中

2017年1月更新

我找到了一个docker-compose解决方案,请参阅: Docker Compose在开始Y之前等待容器X


2
docker-compose 2.3中已弃用使用运行状况检查,以鼓励使分布式系统容错。请参阅:docs.docker.com/compose/startup-order
Kmaid '17

Answers:


283

终于找到了一个使用docker-compose方法的解决方案。从docker-compose文件格式2.1开始,您可以定义healthchecks

我在一个示例项目中做到了, 您至少需要安装docker 1.12.0+。我还需要扩展rabbitmq-management Dockerfile,因为官方映像上未安装curl。

现在,我测试Rabbitmq容器的管理页面是否可用。如果curl以exitcode 0结束,则容器应用程序(python pika)将启动并将消息发布到hello队列。现在可以正常工作了(输出)。

docker-compose(2.1版):

version: '2.1'

services:
  app:
    build: app/.
    depends_on:
      rabbit:
        condition: service_healthy
    links: 
        - rabbit

  rabbit:
    build: rabbitmq/.
    ports: 
        - "15672:15672"
        - "5672:5672"
    healthcheck:
        test: ["CMD", "curl", "-f", "http://localhost:15672"]
        interval: 30s
        timeout: 10s
        retries: 5

输出:

rabbit_1  | =INFO REPORT==== 25-Jan-2017::14:44:21 ===
rabbit_1  | closing AMQP connection <0.718.0> (172.18.0.3:36590 -> 172.18.0.2:5672)
app_1     |  [x] Sent 'Hello World!'
healthcheckcompose_app_1 exited with code 0

Dockerfile(rabbitmq + curl):

FROM rabbitmq:3-management
RUN apt-get update
RUN apt-get install -y curl 
EXPOSE 4369 5671 5672 25672 15671 15672

版本3不再支持条件形式depends_on 因此,我从depends_on移至重新启动失败。现在,我的应用程序容器将重新启动2-3次,直到工作为止,但它仍然是docker-compose功能,而不会覆盖入口点。

docker-compose(版本3):

version: "3"

services:

  rabbitmq: # login guest:guest
    image: rabbitmq:management
    ports:
    - "4369:4369"
    - "5671:5671"
    - "5672:5672"
    - "25672:25672"
    - "15671:15671"
    - "15672:15672"
    healthcheck:
        test: ["CMD", "curl", "-f", "http://localhost:15672"]
        interval: 30s
        timeout: 10s
        retries: 5

  app:
    build: ./app/
    environment:
      - HOSTNAMERABBIT=rabbitmq
    restart: on-failure
    depends_on:
      - rabbitmq
    links: 
        - rabbitmq

6
@svenhornberg ping使用ICMP,因此不支持TCP端口。也许nc要测试一个TCP端口。可能更好地使用psql -h localhost -p 5432和查询某些内容。
马特

36
版本3中删除了“取决于” docs.docker.com/compose/compose-file/#dependson
nha

48
@nha似乎已删除的condition形式depends_on,但depends_on本身仍处于v3中
akivajgordon

14
怎样才能健康检查仍然可以用来控制启动顺序是否depends_oncondition已被删除?
弗朗兹(Franz)

42
仍然难以置信这种痛苦
npr

71

在本地,这还不可能。另请参阅此功能请求

到目前为止,您需要在容器中执行此操作CMD以等待所有必需的服务都在那里。

Dockerfiles中,CMD您可以引用自己的启动脚本,其中包含启动容器服务的启动。在开始之前,您需要等待类似的依赖项:

Docker文件

FROM python:2-onbuild
RUN ["pip", "install", "pika"]
ADD start.sh /start.sh
CMD ["/start.sh"]

start.sh

#!/bin/bash
while ! nc -z rabbitmq 5672; do sleep 3; done
python rabbit.py

可能还需要在其中安装netcat Dockerfile。我不知道python映像上预装了什么。

有一些工具可提供易于使用的等待逻辑,用于简单的tcp端口检查:

对于更复杂的等待:


您能解释一下CMD的意思吗?这是否意味着我的程序必须执行此操作,就像我执行端口检查一样?还是您要为此指定来自linux的特定CMD?
svenhornberg 2015年

感谢您的解释,我赞成您的回答。但是,我认为即将发布的功能请求将是我所提出问题的正确答案,因此到目前为止我仍未回答。
svenhornberg

44

使用restart: unless-stoppedrestart: always可以解决此问题。

如果worker container在rabbitMQ未准备好时停止,它将重新启动直到准备就绪。


3
对于这种情况,我很喜欢这种解决方案,但是对于运行该子流程之一失败的容器,该解决方案不起作用。例如,即使Tomcat容器运行的Java servlet无法连接到数据库服务器,它也会继续运行。当然,Docker容器几乎不需要像Tomcat这样的servlet容器。
德里克·马哈尔

@DerekMahar,如果您有一个仅支持REST调用的基于Java的Web应用程序,那么您将使用什么代替Jetty / Tomcat?
JoeG

2
@JoeG,我的意思是Tomcat是Servlet容器,可以承载许多应用程序,而不是嵌入式Tomcat。例如,Docker使得前者几乎没有必要,而使后者在微服务中更受欢迎。
德里克·马哈尔

35

他们最近添加了该depends_on功能

编辑:

从撰写版本2.1+开始,您可以depends_on结合使用healthcheck以实现以下目的:

从文档

version: '2.1'
services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
  redis:
    image: redis
  db:
    image: redis
    healthcheck:
      test: "exit 0"

在2.1版之前

您仍然可以使用depends_on,但是它只会影响服务启动的顺序 -不会影响从属服务启动之前的服务就绪状态。

似乎至少需要版本1.6.0。

用法如下所示:

version: '2'
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres 

从文档:

表达服务之间的依赖关系,这有两个作用:

  • docker-compose up将以依赖关系顺序启动服务。在以下示例中,db和redis将在web之前启动。
  • docker-compose up SERVICE将自动包含SERVICE的依赖项。在以下示例中,docker-compose up网站还将创建并启动db和redis。

注意:据我了解,尽管这确实设置了加载容器的顺序。它不能保证容器内的服务已实际加载。

例如,您的postgres 容器可能启动了。但是postgres服务本身可能仍在容器内进行初始化。


10
dnephin写道:只有排序。为了实际延迟另一个容器的启动,将需要某种方式来检测进程何时完成了自身的初始化。
svenhornberg '16

15
“版本3不再支持的条件形式depends_on。” docs.docker.com/compose/compose-file/#dependson
akauppi'4

depends_on不要等到容器处于ready状态(这对您而言意味着什么)。它只等到容器处于“运行”状态。
htyagi '17

19

您也可以将其添加到命令选项中,例如。

command: bash -c "sleep 5; start.sh"

https://github.com/docker/compose/issues/374#issuecomment-156546513

在端口上等待,您也可以使用类似这样的东西

command: bash -c "while ! curl -s rabbitmq:5672 > /dev/null; do echo waiting for xxx; sleep 3; done; start.sh"

为了增加等待时间,您可以增加一点:

command: bash -c "for i in {1..100} ; do if ! curl -s rabbitmq:5672 > /dev/null ; then echo waiting on rabbitmq for $i seconds; sleep $i; fi; done; start.sh"

13

restart: on-failure 为我做了把戏..见下文

---
version: '2.1'
services:
  consumer:
    image: golang:alpine
    volumes:
      - ./:/go/src/srv-consumer
    working_dir: /go/src/srv-consumer
    environment:
      AMQP_DSN: "amqp://guest:guest@rabbitmq:5672"
    command: go run cmd/main.go
    links:
          - rabbitmq
    restart: on-failure

  rabbitmq:
    image: rabbitmq:3.7-management-alpine
    ports:
      - "15672:15672"
      - "5672:5672"


7

您还可以通过使用netcat(使用docker-wait脚本)设置一个端点来等待服务启动来解决此问题。我喜欢这种方法,因为您仍有一个干净的command部分,docker-compose.yml不需要在应用程序中添加特定于Docker的代码:

version: '2'
services:
  db:
    image: postgres
  django:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    entrypoint: ./docker-entrypoint.sh db 5432
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

然后您的docker-entrypoint.sh

#!/bin/sh

postgres_host=$1
postgres_port=$2
shift 2
cmd="$@"

# wait for the postgres docker to be running
while ! nc $postgres_host $postgres_port; do
  >&2 echo "Postgres is unavailable - sleeping"
  sleep 1
done

>&2 echo "Postgres is up - executing command"

# run the command
exec $cmd

如今,这已在官方docker文档中进行了说明

PS:netcat如果此选项不可用,则应安装在Docker实例中。为此,请将其添加到您的Docker文件中:

RUN apt-get update && apt-get install netcat-openbsd -y 

4

有一个随时可用的实用程序,名为“ docker-wait ”,可用于等待。


1
谢谢,但这只是一个shell脚本,因此就像h3nrik答案或在python中等待。它不是docker-compose本身的功能。也许您可以在github.com/docker/compose/issues/374中进行查看,他们计划实施运行状况检查,这将是最好的方法。打开的tcp连接并不意味着您的服务已准备就绪或可能保持就绪。除此之外,我需要在dockerfile中更改入口点。
svenhornberg 2015年

3

尝试了许多不同的方法,但是喜欢这种方法的简单性:https : //github.com/ufoscout/docker-compose-wait

您可以使用ENV的想法在泊坞窗撰写文件瓦尔提交服务的主机列表(带口),这应该是“等待”是这样的:WAIT_HOSTS: postgres:5432, mysql:3306, mongo:27017

因此,假设您有以下docker-compose.yml文件(从回购自述文件复制/粘贴):

version: "3"

services:

  mongo:
    image: mongo:3.4
    hostname: mongo
    ports:
      - "27017:27017"

  postgres:
    image: "postgres:9.4"
    hostname: postgres
    ports:
      - "5432:5432"

  mysql:
    image: "mysql:5.7"
    hostname: mysql
    ports:
      - "3306:3306"

  mySuperApp:
    image: "mySuperApp:latest"
    hostname: mySuperApp
    environment:
      WAIT_HOSTS: postgres:5432, mysql:3306, mongo:27017

接下来,为了等待服务,您需要将以下两行添加到您的Dockerfile中(到应等待其他服务启动的服务的Dockerfile中):

ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.5.0/wait /wait
RUN chmod +x /wait

此类示例Dockerfile的完整示例(同样来自项目repo README):

FROM alpine

## Add your application to the docker image
ADD MySuperApp.sh /MySuperApp.sh

## Add the wait script to the image
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.5.0/wait /wait
RUN chmod +x /wait

## Launch the wait tool and then your application
CMD /wait && /MySuperApp.sh

有关可能用法的其他详细信息,请参见自述文件


我在寻找类似的答案。我通常使用过hub.docker.com/r/dadarek/wait-for-dependencies,因为它在下面使用了netcat。您提供的是基于Rust的。无法评论您的画质,但对我来说,没有其他层次绝对可以。
Filip Malczak

1
我强烈建议出于安全考虑反对这样做。您正在通过超链接运行任意可执行文件。更好的解决方案是使用通过COPY复制到图像中的静态脚本执行相同的操作
Paul K,

@PaulK当然,通过超链接运行任何内容并不安全,这是可以理解的,但这只是如何制作超链接的演示 https://github.com/ufoscout/docker-compose-wait库工作:)使用该库的方式不会改变可以利用某些lib的答案。安全是一个复杂的话题,如果我们走得更远,即使我们进行复制,也应该检查该库在内部的运行情况:)因此,最好在您的评论中更具体,例如:“我强烈建议您不要使用该库。从超链接”。希望您同意,谢谢提示!
Evereq

2

基于此博客文章https://8thlight.com/blog/dariusz-pasciak/2016/10/17/docker-compose-wait-for-dependencies.html

docker-compose.yml如下所示配置了我的:

version: "3.1"

services:
  rabbitmq:
    image: rabbitmq:3.7.2-management-alpine
    restart: always
    environment:
      RABBITMQ_HIPE_COMPILE: 1
      RABBITMQ_MANAGEMENT: 1
      RABBITMQ_VM_MEMORY_HIGH_WATERMARK: 0.2
      RABBITMQ_DEFAULT_USER: "rabbitmq"
      RABBITMQ_DEFAULT_PASS: "rabbitmq"
    ports:
      - "15672:15672"
      - "5672:5672"
    volumes:
      - data:/var/lib/rabbitmq:rw

  start_dependencies:
    image: alpine:latest
    links:
      - rabbitmq
    command: >
      /bin/sh -c "
        echo Waiting for rabbitmq service start...;
        while ! nc -z rabbitmq 5672;
        do
          sleep 1;
        done;
        echo Connected!;
      "

volumes:
  data: {}

然后我为运行=>:

docker-compose up start_dependencies

rabbitmq服务将以守护程序模式启动,start_dependencies将完成工作。


大声笑,通过查询来查询"curl", "-f", "http://localhost:15672"您需要安装的management插件,并使用已经过时的healthcheck-最佳答案。通过nc-downvote 进行检查的简单工作示例。哈,好吧……
伊戈尔·科玛

答案不使用本地docker功能,如果您使用curl,nc或其他工具,则该功能无关紧要。而!nc与其他答案中已发布的内容相同。
svenhornberg


1
@IgorKomar,谢谢,您救了我的一天!:3我用几乎相同的机制在启动实际应用程序之前检查mysql服务器是否准备就绪。;)我正在将类似的命令传递给docker-compose run --name app-test --rm "app" bash -l -c 'echo Waiting for mysql service start... && while ! nc -z db-server 3306; do sleep 1; done && echo Connected! && /bin/bash /script/ci_tests.sh'
TooroSan '18

1

在Docker Compose文件的版本3中,您可以使用RESTART

例如:

docker-compose.yml

worker:
    build: myapp/.
    volumes:
    - myapp/.:/usr/src/app:ro
    restart: on-failure
    depends_on:
    - rabbitmq
rabbitmq:
    image: rabbitmq:3-management

请注意,由于链接在版本3中已弃用,因此我使用了depends_on而不是链接

即使可行,它也不是理想的解决方案,因为每次发生故障时都要重新启动Docker容器。

也看一下RESTART_POLICY。它使您可以微调重启策略。

当你 在生产中使用的撰写,它实际上是用重新启动策略的最佳做法:

指定重启策略,如重启:始终避免停机



0

这是main容器worker开始响应ping时等待的示例:

version: '3'
services:
  main:
    image: bash
    depends_on:
     - worker
    command: bash -c "sleep 2 && until ping -qc1 worker; do sleep 1; done &>/dev/null"
    networks:
      intra:
        ipv4_address: 172.10.0.254
  worker:
    image: bash
    hostname: test01
    command: bash -c "ip route && sleep 10"
    networks:
      intra:
        ipv4_address: 172.10.0.11
networks:
  intra:
    driver: bridge
    ipam:
      config:
      - subnet: 172.10.0.0/24

但是,正确的方法是使用healthcheck(> = 2.1)。


0

不建议用于严重的部署,但实际上这是一个“ wait x seconds”命令。

随着docker-compose版本3.4start_period增加,说明已添加到中healthcheck。这意味着我们可以执行以下操作:

docker-compose.yml

version: "3.4"
services:
  # your server docker container
  zmq_server:
    build:
      context: ./server_router_router
      dockerfile: Dockerfile

  # container that has to wait
  zmq_client:
    build:
      context: ./client_dealer/
      dockerfile: Dockerfile
    depends_on:
      - zmq_server
    healthcheck:
      test: "sh status.sh"
      start_period: 5s

status.sh

#!/bin/sh

exit 0

这里发生的healthcheck是5秒钟后调用。这将调用status.sh脚本,该脚本始终返回“没问题”。我们只是让zmq_client容器在开始前等待5秒钟!

注意:拥有一个很重要version: "3.4"。如果.4不存在,则docker-compose抱怨。


1
作为一个简单的“等待5s”解决方案,这是一个非常巧妙的方法。我会投票赞成,但我不会,因为这实际上不适用于类似产品的设置,而且我担心有人会看票数而不是仔细阅读。不过,我还是想说“男人,那很聪明”;)
Filip Malczak

PS。对于更复杂的解决方案,请参阅Evereq的答案
菲利普Malczak

不是什么start_period。这种配置意味着有一段宽限期,在此期间,健康检查失败不会被视为重试。如果它能早日成功,那就被认为是健康的。启动期过后,失败将被视为重试。参见docs.docker.com/engine/reference/builder/#healthcheck
Capi Etheriel

-4

我只有2个撰写文件,然后先开始一个,然后再开始一个。我的脚本如下所示:

#!/bin/bash
#before i build my docker files
#when done i start my build docker-compose
docker-compose -f docker-compose.build.yaml up
#now i start other docker-compose which needs the image of the first
docker-compose -f docker-compose.prod.yml up

这不是一个好习惯。然后,您不能从一个撰写文件中交付包含多个着色器的解决方案。
juergi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.