使用带有CI的docker-compose-如何处理退出代码和守护程序链接的容器?


87

现在,我们的Jenkins代理为我们的每个Rails项目生成一个docker-compose.yml,然后运行docker-compose。docker-compose.yml具有一个主要的“ Web”容器,其中包含rbenv和我们所有其他的Rails依赖项。它链接到包含测试Postgres DB的数据库容器。

问题出在我们需要实际运行测试并生成退出代码时。仅当测试脚本返回exit 0时,我们的CI服务器才会部署,但是docker-compose始终返回0,即使其中一个容器命令失败。

另一个问题是,即使Web容器完成了运行测试之后,DB容器也会无限期地运行,因此docker-compose up永远不会返回。

有什么方法可以在此过程中使用docker-compose吗?我们将需要能够运行容器,但是在Web容器完成后退出并返回其退出代码。现在,我们使用docker手动启动数据库容器,并使用--link选项运行Web容器。

Answers:


76

从version开始1.12.0,您可以使用该--exit-code-from选项。

文档

-从服务退出代码

返回所选服务容器的退出代码。暗示-退出容器退出。


1
如果您使用的是docker-compose1.12.0及更高版本,那应该是正确的方法。也许也是你的情况。一个示例可能是:docker-compose up --exit-code-from test-unit。请注意,直到我set -e在脚本开始处添加a之前,它对我而言均无效。
Adrian Antunez

--exit-code-from不适用于-d。它将引发以下错误:using --exit-code-from implies --abort-on-container-exit--abort-on-container-exit and -d cannot be combined.
ericat


2
该文件是残酷的。这与哪些标志兼容?是仅一项服务还是您可以通过多项服务?
worc

42

docker-compose run是获取所需退出状态的简单方法。例如:

$ cat docker-compose.yml 
roit:
    image: busybox
    command: 'true'
naw:
    image: busybox
    command: 'false'
$ docker-compose run --rm roit; echo $?
Removing test_roit_run_1...
0
$ docker-compose run --rm naw; echo $?
Removing test_naw_run_1...
1

另外,您也可以选择检查已用完的容器。您可以使用该-f标志获取退出状态。

$ docker-compose up
Creating test_naw_1...
Creating test_roit_1...
Attaching to test_roit_1
test_roit_1 exited with code 0
Gracefully stopping... (press Ctrl+C again to force)
$ docker-compose ps -q | xargs docker inspect -f '{{ .Name }} exited with status {{ .State.ExitCode }}'
/test_naw_1 exited with status 1
/test_roit_1 exited with status 0

至于永不返回的db容器,如果使用,docker-compose up则需要对该容器进行签名。那可能不是你想要的。相反,您可以使用docker-compose up -d守护程序运行容器,并在测试完成后手动终止容器。docker-compose run 应该为您运行链接的容器,但是我听到有人在SO上闲聊着一个错误,该错误阻止了该容器立即按预期工作。


docker run的问题在于,使用-T运行时,它不提供任何输出,我们需要输出,以便我们可以检查失败的构建。
Logan Serman 2015年

1
@LoganSerman您可以使用docker-compose logs
kojiro

有没有一种方法可以在运行期间将这些日志不断地传递到STDOUT,以便我们在CI构建过程中可以看到它?
Logan Serman 2015年

我想我不明白您为什么要使用-T
kojiro 2015年

我们在容器内部运行的用于运行测试的某些命令可能会要求输入,我们想使用-T来避免这种情况。例如,Rbenv询问是否要重新安装Ruby版本(如果已存在)。
Logan Serman 2015年

23

以小次郎的回答为基础:

docker-compose ps -q | xargs docker inspect -f '{{ .State.ExitCode }}' | grep -v '^0' | wc -l | tr -d ' '

  1. 获取容器ID
  2. 获取每个容器ID的最后一次运行退出代码
  3. 仅以“ 0”开头的状态代码
  4. 非0状态码的计数
  5. 修剪空白

返回返回了多少非零退出代码。如果一切都以代码0退出,则将为0。


您还可以使用来自的非安静输出docker-compose ps,例如:docker-compose ps | grep -c "Exit 1"将为您docker-compose ps提供从显示中匹配“退出1”的计数(它提供了漂亮的结果汇总表)。退出代码在“状态”列中列出。
eharik

真的很棒 在我的情况下,在容器中运行的失败的测试套件不会使容器以代码1退出。如果有任何以1的代码退出,我将无法进行汇总,因为它们都不起作用。案件?
walkerrandophsmith18年

9

如果您愿意使用docker-compose run手动启动测试,请添加--rm奇怪的是,标志会使Compose准确反映您命令的退出状态。

这是我的示例:

$ docker-compose -v
docker-compose version 1.7.0, build 0d7bf73

$ (docker-compose run bash false) || echo 'Test failed!'  # False negative.

$ (docker-compose run --rm bash false) || echo 'Test failed!'  # True positive.
Test failed!

$ (docker-compose run --rm bash true) || echo 'Test failed!'  # True negative.

1
(docker-compose run --rm ...) || exit $?在发生错误时终止。在bash脚本中有用。
阿米尔雷扎·纳西里

8

使用docker wait以获取退出代码:

$ docker-compose -p foo up -d
$ ret=$(docker wait foo_bar_1)

foo是“项目名称”。在上面的示例中,我明确指定了它,但是如果您不提供它,它就是目录名。 bar是您在docker-compose.yml中为被测系统指定的名称。

请注意,docker logs -f容器停止时退出也是正确的做法。所以你可以放

$ docker logs -f foo_bar_1

docker-compose up和之间,docker wait因此您可以观看测试的运行情况。


8

--exit-code-from SERVICE并且--abort-on-container-exit在需要运行所有容器才能完成的情况下不起作用,但是如果其中一个容器退出较早,则会失败。例如,如果在不同的容器中同时运行2个测试服,则可能是一个示例。

有了@spenthil的建议,您可以包装docker-compose一个脚本,如果有容器运行,该脚本将失败。

#!/bin/bash
set -e

# Wrap docker-compose and return a non-zero exit code if any containers failed.

docker-compose "$@"

exit $(docker-compose -f docker-compose.ci.build.yml ps -q | tr -d '[:space:]' |
  xargs docker inspect -f '{{ .State.ExitCode }}' | grep -v 0 | wc -l | tr -d '[:space:]')

然后,在您的CI服务器上,只需更改docker-compose up./docker-compose.sh up


1
该脚本永远不会到达退出部分,因为其他容器(例如数据库,Web应用程序)将永久运行。在分离模式下运行它一旦退出的容器均达到
秃子

没错,这仅在您要运行所有容器才能完成时才起作用。可能不是特别常见,但是在撰写本文时对我很有用,我想我会分享的。
马特·科尔

无论如何都支持您的回答,因为它可以帮助我解决大部分问题!在分离模式下在每个测试容器上添加Docker等待使它工作。感谢您的分享:)
Baldy

2

docker-rails允许您指定将哪个容器的错误代码返回给主进程,以便您的CI服务器可以确定结果。对于使用docker进行Rails的CI和开发来说,这是一个很好的解决方案。

例如

exit_code: web

docker-rails.ymlweb通过命令生成容器退出代码docker-rails ci testdocker-rails.yml只是围绕标准的元包装docker-compose.yml,使您可以针对不同的环境(例如开发vs测试vs parallel_tests)继承/重用相同的基本配置。


2

如果您可能在一个docker引擎上运行更多具有相同名称的docker-compose服务,但您不知道确切名称:

docker-compose up -d
(exit "${$(docker-compose logs -f test-chrome)##* }")

echo %? -从test-chrome服务返回退出代码

好处:

  • 等待确切的服务退出
  • 使用服务名称,而不是容器名称

2

您可以通过以下方式查看退出状态:

echo $(docker-compose ps | grep "servicename" | awk '{print $4}')

感谢您开始使用。这是我这个版本的版本(对我而言,它更有效b / c我认为自从编写此答案以来,命令输出格式已更改)–docker-compose ps | grep servicename | grep -v 'Exit 0' && echo "Automation or integration tests failed." && exit 1
DTrejo
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.