docker-compose 2.1提供了一个很好的功能,可以使用condition
指定depends_on
。当前的docker-compose文档声明:
版本3不再支持depends_on的条件形式。
不幸的是,documentation没有解释,为什么删除了condition
表单,并且没有针对如何使用V3向上实现该行为的任何具体建议。
答案 0 :(得分:11)
从 1.27.0 开始,2.x 和 3.x 与 COMPOSE_SPEC 架构合并。
version 现在是可选的。因此,您可以删除它并像以前一样指定 condition:
services:
web:
build: .
depends_on:
redis:
condition: service_healthy
redis:
image: redis
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 1s
timeout: 3s
retries: 30
答案 1 :(得分:8)
在compose中指定容器依赖项已经有了一步之遥。它们仅在启动时有效,并且在运行时重新启动相关容器时无法工作。相反,每个容器应包括在删除连接时重试重新连接到从属服务的机制。许多连接到数据库或REST API服务的库都具有可配置的内置重试。我会调查一下。无论如何,它都需要生产代码。
答案 2 :(得分:3)
只是以为我会通过docker-compose运行postgres和应用程序时添加我的解决方案,我需要该应用程序等待初始化sql脚本完成后再开始。
dockerize似乎正在等待db端口可用(端口5432),这与depends_on
的等效端口可以在docker 3中使用:
version: '3'
services:
app:
container_name: back-end
depends_on:
- postgres
postgres:
image: postgres:10-alpine
container_name: postgres
ports:
- "5432:5432"
volumes:
- ./docker-init:/docker-entrypoint-initdb.d/
问题:
如果您的启动脚本较大,则该应用程序将在depends_on
仅等待数据库端口的情况下启动。
尽管我确实同意应在应用程序逻辑中实现该解决方案,但是我们遇到的问题仅是当我们要运行测试并用测试数据预填充数据库时,因此在代码外实施解决方案更有意义因为我倾向于不喜欢引入代码“使测试有效”
解决方案:
对postgres容器执行健康检查。
对我来说,这意味着检查pid 1的命令是postgres
,因为它将在初始化db脚本运行时在pid 1上运行另一个命令
在应用程序端编写一个脚本,该脚本将等待postgres
成为healthy
。脚本如下所示:
#!/bin/bash
function check {
STATUS=\`curl -s --unix-socket /var/run/docker.sock http:/v1.24/containers/postgres/json | python -c 'import sys, json; print json.load('sys.stdin')["State"]["Health"]["Status"]'\`
if [ "$STATUS" = "healthy" ]; then
return 0
fi
return 1
}
until check; do
echo "Waiting for postgres to be ready"
sleep 5
done
echo "Postgres ready"
然后docker-compose应该挂载脚本目录,以便我们不为应用程序编辑Dockerfile,如果我们使用的是自定义的postgres映像,则可以通过这种方式继续为您使用docker文件发布的图像。
我们还将覆盖应用程序docker文件中定义的入口点,以便我们可以在应用程序启动之前运行等待脚本
version: '3'
services:
app:
container_name: back-end
entrypoint: ["/bin/sh","-c","/opt/app/wait/wait-for-postgres.sh && <YOUR_APP_START_SCRIPT>"]
depends_on:
- postgres
volumes:
- //var/run/docker.sock:/var/run/docker.sock
- ./docker-scripts/wait-for-postgres:/opt/app/wait
postgres:
image: postgres:10-alpine
container_name: postgres
ports:
- "5432:5432"
volumes:
- ./docker-init:/docker-entrypoint-initdb.d/
- ./docker-scripts/postgres-healthcheck:/var/lib
healthcheck:
test: /var/lib/healthcheck.sh
interval: 5s
timeout: 5s
retries: 10
答案 3 :(得分:2)
与其尝试启动另一个容器,不如考虑在尝试访问可能未处于就绪状态的容器时收到连接错误,请故意从容器化的应用程序中退出。这种策略与适用于您的容器的重新启动策略一起使用将重新启动应用程序容器,直到连接可用或满足其他条件为止
答案 4 :(得分:1)
有一些外部工具可以让您模仿这种行为。例如,使用dockerize工具,您可以将CMD
或ENTRYPOINT
用dockerize -wait
包装,这将阻止在指定的服务准备就绪之前运行应用程序。
如果您的docker-compose文件以前看起来像这样:
version: '2.1'
services:
kafka:
image: spotify/kafka
healthcheck:
test: nc -z localhost 9092
webapp:
image: foo/bar # your image
healthcheck:
test: curl -f http://localhost:8080
tests:
image: bar/foo # your image
command: YOUR_TEST_COMMAND
depends_on:
kafka:
condition: service_healthy
webapp:
condition: service_healthy
然后您可以在dockerize
撰写文件中使用v3
,如下所示:
version: '3.0'
services:
kafka:
image: spotify/kafka
webapp:
image: foo/bar # your image
tests:
image: bar/foo # your image
command: dockerize -wait tcp://kafka:9092 -wait web://webapp:8080 YOUR_TEST_COMMAND