【问题标题】:Docker-Compose: how to wait for other service to be ready?Docker-Compose:如何等待其他服务准备好?
【发布时间】:2019-02-18 17:24:48
【问题描述】:

我有以下 docker-compose,我需要等待服务 jhipster-registry 启动并接受连接,然后再启动 myprogram-app

我按照官方文档https://docs.docker.com/compose/compose-file/compose-file-v2/尝试了healthcheck方式

version: '2.1'
services:
    myprogram-app:
        image: myprogram
        mem_limit: 1024m
        environment:
            - SPRING_PROFILES_ACTIVE=prod,swagger
            - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:$${jhipster.registry.password}@jhipster-registry:8761/eureka
            - SPRING_CLOUD_CONFIG_URI=http://admin:$${jhipster.registry.password}@jhipster-registry:8761/config
            - SPRING_DATASOURCE_URL=jdbc:postgresql://myprogram-postgresql:5432/myprogram
            - JHIPSTER_SLEEP=0
            - SPRING_DATA_ELASTICSEARCH_CLUSTER_NODES=myprogram-elasticsearch:9300
            - JHIPSTER_REGISTRY_PASSWORD=53bqDrurQAthqrXG
            - EMAIL_USERNAME
            - EMAIL_PASSWORD
        ports:
            - 8080:8080
        networks:
          - backend
        depends_on:
          - jhipster-registry:
              "condition": service_started
          - myprogram-postgresql
          - myprogram-elasticsearch
    myprogram-postgresql:
        image: postgres:9.6.5
        mem_limit: 256m
        environment:
            - POSTGRES_USER=myprogram
            - POSTGRES_PASSWORD=myprogram
        networks:
          - backend
    myprogram-elasticsearch:
        image: elasticsearch:2.4.6
        mem_limit: 512m
        networks:
          - backend
    jhipster-registry:
        extends:
            file: jhipster-registry.yml
            service: jhipster-registry
        mem_limit: 512m
        ports:
            - 8761:8761
        networks:
          - backend
        healthcheck:
          test: "exit 0"
networks:
  backend:
    driver: "bridge"

但在运行docker-compose up 时出现以下错误:

ERROR: The Compose file './docker-compose.yml' is invalid because:
services.myprogram-app.depends_on contains {"jhipster-registry": {"condition": "service_started"}}, which is an invalid type, it should be a string

是我做错了什么,还是不再支持此功能?如何实现服务之间的这种同步?

更新版本

version: '2.1'
services:
    myprogram-app:
        image: myprogram
        mem_limit: 1024m
        environment:
            - SPRING_PROFILES_ACTIVE=prod,swagger
            - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:$${jhipster.registry.password}@jhipster-registry:8761/eureka
            - SPRING_CLOUD_CONFIG_URI=http://admin:$${jhipster.registry.password}@jhipster-registry:8761/config
            - SPRING_DATASOURCE_URL=jdbc:postgresql://myprogram-postgresql:5432/myprogram
            - JHIPSTER_SLEEP=0
            - SPRING_DATA_ELASTICSEARCH_CLUSTER_NODES=myprogram-elasticsearch:9300
            - JHIPSTER_REGISTRY_PASSWORD=53bqDrurQAthqrXG
            - EMAIL_USERNAME
            - EMAIL_PASSWORD
        ports:
            - 8080:8080
        networks:
          - backend
        depends_on:
          jhipster-registry:
            condition: service_healthy
          myprogram-postgresql:
            condition: service_started
          myprogram-elasticsearch:
            condition: service_started
        #restart: on-failure
    myprogram-postgresql:
        image: postgres:9.6.5
        mem_limit: 256m
        environment:
            - POSTGRES_USER=myprogram
            - POSTGRES_PASSWORD=tuenemreh
        networks:
          - backend
    myprogram-elasticsearch:
        image: elasticsearch:2.4.6
        mem_limit: 512m
        networks:
          - backend
    jhipster-registry:
        extends:
            file: jhipster-registry.yml
            service: jhipster-registry
        mem_limit: 512m
        ports:
            - 8761:8761
        networks:
          - backend
        healthcheck:
          test: ["CMD", "curl", "-f", "http://jhipster-registry:8761", "|| exit 1"]
          interval: 30s
          retries: 20
          #start_period: 30s
networks:
  backend:
    driver: "bridge"

更新的版本给了我一个不同的错误,

ERROR: for myprogram-app  Container "8ebca614590c" is unhealthy.
ERROR: Encountered errors while bringing up the project.

说 jhipster-registry 的容器不健康,但是可以通过浏览器访问。如何修复运行状况检查中的命令以使其正常工作?

【问题讨论】:

标签: docker docker-compose wait


【解决方案1】:

documentation 建议在 Docker Compose 版本 2 文件中depends_on: 可以是字符串列表,也可以是键是服务名称而值是条件的映射。对于您没有(或不需要)健康检查的服务,有一个service_started 条件。

depends_on:
  # notice: these lines don't start with "-"
  jhipster-registry:
    condition: service_healthy
  myprogram-postgresql:
    condition: service_started
  myprogram-elasticsearch:
    condition: service_started

根据您对程序及其库的控制程度,如果您可以安排服务能够在其依赖项不一定可用的情况下启动(等效地,如果它的依赖项在服务终止时运行)正在运行),并且不使用depends_on: 选项。例如,如果数据库关闭,您可能会返回 HTTP 503 Service Unavailable 错误。另一个通常有用的策略是在您的依赖项不可用时立即退出,但使用a setting(如restart: on-error)要求协调器重新启动服务。

【讨论】:

  • 好的,之前的错误现在已经消失了。我用你的修复更新了这个问题,但使用的命令似乎不起作用。如果 jhipser-registry:8761 已启动,如何让 curl 返回 0(我应该读过,但实际上没有)?
  • curl man page 有一个非常详尽的退出代码列表; Docker HEALTHCHECK docs 建议将 curl -f http://localhost/ || exit 1 作为典型的健康检查(这需要您的图像安装 curl)。
  • 这个答案不再相关。来自文档:Version 3 no longer supports the condition form of depends_on
  • 又是相关的。从 docker-compose 1.27.0 开始,它支持 v3 的 COMPOSE_SPEC 标准,其中包括 conditiondepends_on,参见 release notes
【解决方案2】:

更新到版本 3+。

请遵循版本 3 中的documents

使用depends_on时有几点需要注意:

depends_on 不等待 db 和 redis 之前“准备好” 启动网络 - 仅在它们启动之前。
如果您需要等待 要准备好服务,请参阅控制启动顺序了解更多信息 这个问题和解决它的策略。
版本 3 不再 支持depends_on的条件形式。
depends_on 选项是 在使用 3 版 Compose 以 swarm 模式部署堆栈时被忽略 文件。

我会考虑使用 restart_policy 选项来配置您的 myprogram-app 以重新启动,直到 jhipster-registry 启动并接受连接:

 restart_policy:
        condition: on-failure
        delay: 3s
        max_attempts: 5
        window: 60s

【讨论】:

    【解决方案3】:

    最佳方法 - 弹性应用启动

    虽然 docker 确实 支持启动依赖项,但他们官方建议更新您的应用启动逻辑以测试外部依赖项的可用性并重试。除了规避docker compose up 中的竞争条件之外,这对于可能在运行中重新启动的健壮应用程序有很多好处

    Docker 方法 - wait-for-it.sh

    根据他们在Control startup and shutdown order in Compose 上的文档,docker 推荐的方法是下载wait-for-it.sh,它接受domain:port 进行轮询,然后如果成功则执行下一组命令。

    version: "2"
    services:
      web:
        build: .
        ports:
          - "80:8000"
        depends_on:
          - "db"
        command: ["./wait-for-it.sh", "db:5432", "--", "python", "app.py"]
      db:
        image: postgres
    

    注意:这需要覆盖映像的启动命令,因此请确保您知道要传递的内容以保持默认启动的奇偶性。

    历史方法 - depends_on - service_healthy(已弃用 3+)

    从历史上看,您可以定义一个healthcheck(仍然是一个很好的做法),然后将depends_on 的条件设置为service_healthy,但是这依赖于的条件方差在 3.0 中已弃用

    version: '3.0'
    services:
      php:
        build:
          context: .
          dockerfile: tests/Docker/Dockerfile-PHP
        depends_on:
          redis:
            condition: service_healthy
      redis:
        image: redis
        healthcheck:
          test: ["CMD", "redis-cli", "ping"]
          interval: 1s
          timeout: 3s
          retries: 30
    

    进一步阅读

    【讨论】:

      【解决方案4】:

      虽然您已经得到了答案,但应该提到的是,您试图实现的目标存在一些令人讨厌的风险。

      理想情况下,服务应该是自给自足的并且足够智能,可以重试并等待依赖项可用(在出现故障之前)。否则,您将更容易受到传播到其他服务的故障的影响。还要考虑系统重启,不像手动启动可能会忽略依赖顺序。

      如果一个服务崩溃导致您的所有系统崩溃,您可能有一个工具可以重新启动所有内容,但最好有能够抵抗这种情况的服务。

      【讨论】:

        【解决方案5】:

        我发现的最佳方法是检查入口点中的所需端口。有不同的方法可以做到这一点,例如wait-for-it 但我喜欢使用这种在 apline 和 bash 图像之间跨平台且不从 GitHub 下载自定义脚本的解决方案:

        安装netcat-openbsd(与aptapk 一起使用)。然后在入口点(与#!/bin/bash#!/bin/sh 一起使用):

        #!/bin/bash
        
        wait_for()
        {
          echo "Waiting $1 seconds for $2:$3"
          timeout $1 sh -c 'until nc -z $0 $1; do sleep 0.1; done' $2 $3 || return 1
          echo "$2:$3 available"
        }
        
        
        wait_for 10 db 5432
        wait_for 10 redis 6379
        

        如果您不想打印任何内容,也可以将其制成 1-liner。

        【讨论】:

          猜你喜欢
          • 2019-03-15
          • 1970-01-01
          • 1970-01-01
          • 2021-10-27
          • 2023-02-02
          • 1970-01-01
          • 2018-12-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多