首页 文章

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

提问于
浏览
184

我正在使用rabbitmq和一个来自here的简单python示例以及docker-compose . 我的问题是我需要等待rabbitmq完全启动 . 从我到目前为止搜索到的,我不知道如何等待容器x(在我的情况下是 Worker )直到y(rabbitmq)开始 .

我发现这是blogpost,他检查其他主机是否在线 . 我也发现了这个docker command

wait用法: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 sample (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 for worker:

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

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

Update Nov 2015

shell脚本或在程序中等待可能是一种可能的解决方案 . 但看到这个Issue后,我正在寻找docker / docker-compose本身的命令或功能 .

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

所以我希望通过docker-compose on board命令得到一个答案,如果他们完成这个问题,希望如此 .

Update March 2016

有一个proposal用于提供内置方法来确定容器是否为"alive" . 所以docker-compose可以在不久的将来使用它 .

Update June 2016

似乎 Health 检查将在版本1.12.0中进入docker integrated

Update January 2017

我找到了一个docker-compose解决方案,请参阅:Docker Compose wait for container X before starting Y

12 回答

  • 4

    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"
    
  • 32

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

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

  • 154

    我无法使用这种方法,我实际上无法在docker-compose中使用volume,在这两种情况下我都无法工作:

    s-flyway_1_d7a78571d25c |环境:无法执行'bash':没有这样的文件或目录docker_s-flyway_1_d7a78571d25c已退出,代码为127

    我在这里想念的是什么?我现在有点迷失了.....

    项目链接:https://github.com/jbrasileiro/introducing-example/blob/master/introducing-example-docker-flyway/src/docker/docker-compose.yml

    Dockerfile

    FROM dhoer/flyway:alpine
    COPY ./sql/*.sql /flyway/sql
    VOLUME ./wait-for-it.sh:/usr/local/bin/wait-for-it.sh
    ADD ./wait-for-it.sh /
    RUN chmod 777 /wait-for-it.sh
    

    docker version : '3'

    version: '3'
    services:
      hello-world:
        image: ubuntu
        volumes:
          - ./wait-for-it.sh:/usr/local/bin/wait-for-it.sh
        command:
          - wait-for-it.sh
          - github.com:80
          - --
          - echo
          - "hello world"
    

    docker version : '2'

    version: '2'
    services:
      s-postgres:
        image: postgres:9.4-alpine
        hostname: host-postgres
        environment:
          - POSTGRES_USER=postgres
          - POSTGRES_PASSWORD=postgres
          - POSTGRES_DB=postgres
        networks:
           - network-docker
      s-flyway:
        image: dhoer/flyway:alpine
        build: flyway/
        entrypoint: /wait-for-it.sh s-postgres 5432 --timeout=0 --strict
        command: -url=jdbc:postgresql://s-postgres:5432/postgres -baselineOnMigrate=true -schemas=public -user=postgres -password=postgres migrate
        networks:
           - network-docker
        depends_on:
          - s-postgres
    networks:
      network-docker:
        driver: bridge
    
  • 26

    另一种解决方案是使用像Kubernetes这样的容器编排解决方案 . Kubernetes支持init容器,这些容器在其他容器启动之前就可以运行完毕 . 您可以在此处找到SQL Server 2017 Linux容器的示例,其中API容器使用init容器初始化数据库

    https://www.handsonarchitect.com/2018/08/understand-kubernetes-object-init.html

  • 0

    原生那是不可能的 . 另见feature request .

    到目前为止,您需要在容器_137239中执行此操作,等待所有必需的服务都存在 .

    Dockerfile s CMD 中,您可以引用自己的启动脚本来启动容器服务 . 在你开始之前,你要等待一个依赖的人:

    Dockerfile

    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
    

    可能你需要在你的 Dockerfile 中安装netcat . 我不知道python镜像上预装了什么 .

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

    对于更复杂的等待:

  • 1

    您还可以通过使用netcat(使用docker-wait脚本)设置等待服务启动的 endpoints 来解决此问题 . 我喜欢这种方法,因为你的 docker-compose.yml 中仍然有一个干净的 command 部分,并且你不需要在你的应用程序中添加特定于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 documentation记录了这一点 .

    PS:如果没有,您应该在docker实例中安装 netcat . 为此,请将此添加到 Docker 文件中:

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

    你也可以将它添加到命令选项,例如 .

    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"
    
  • 5

    有一个名为“docker-wait”的可立即使用的实用程序,可用于等待 .

  • 14

    基于这篇博文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: {}
    

    然后我做run =>:

    docker-compose up start_dependencies

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

  • 0

    用于容器开始订购使用

    depends_on:
    

    等待以前的容器启动使用脚本

    entrypoint: ./wait-for-it.sh db:5432
    

    本文将帮助您https://docs.docker.com/compose/startup-order/

  • 5

    终于找到了一个docker-compose方法的解决方案 . 由于docker-compose文件格式2.1,您可以定义healthchecks .

    我是在example project中完成的,至少需要安装docker 1.12.0 . 我还需要extend the rabbitmq-management Dockerfile,因为curl没有安装在官方图片上 .

    现在我测试rabbitmq-container的管理页面是否可用 . 如果curl使用exitcode 0完成,则将启动容器应用程序(python pika)并将消息发布到hello队列 . 它现在正在工作(输出) .

    docker-compose (version 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
    

    output:

    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
    

    Version 3 no longer supports the condition form of depends_on. 所以我从depends_on转移到重启失败 . 现在我的app容器将重启2-3次直到它正常工作,但它仍然是一个docker-compose功能而不会覆盖入口点 .

    docker-compose (version 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
    
  • 61

    最近他们添加了depends_on feature .

    Edit:

    从撰写版本2.1开始,您可以将 depends_onhealthcheck 结合使用来实现此目的:

    From the docs

    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"
    

    Before version 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 web还将创建并启动db和redis .

    Note: 据我了解,虽然这确实设置了容器的加载顺序 . 它不保证容器内的服务实际已加载 .

    例如,你postgres容器可能已启动 . 但postgres服务本身可能仍在容器内初始化 .

相关问题