使用码头-与 CI 组合-如何处理出口代码和被妖魔化的连接集装箱?

现在我们的詹金斯探员正在制作码头作品。为每个 Rails 项目添加 yml,然后运行 docker-compup。码头作曲。Yml 有一个主“ web”容器,其中包含 rbenv 和所有其他 Rails 依赖项。它链接到一个包含测试 PostgresDB 的 DB 容器。

当我们需要实际运行测试并生成退出代码时,问题就出现了。我们的 CI 服务器只有在测试脚本返回出口0的情况下才会部署,但即使其中一个容器命令失败,docker-compose 也总是返回0。

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

有没有一种方法,我们可以使用码头组合为这个过程?我们需要能够运行容器,但退出后,Web 容器完成并返回它的退出代码。现在我们只能手动使用 docker 旋转数据库容器并使用—— link 选项运行 web 容器。

71812 次浏览

docker-compose run is the simple way to get the exit statuses you desire. For example:

$ 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

Alternatively, you do have the option to inspect the dead containers. You can use the -f flag to get just the exit status.

$ 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

As for the db container that never returns, if you use docker-compose up then you will need to sigkill that container; that's probably not what you want. Instead, you can use docker-compose up -d to run your containers daemonized, and manually kill the containers when your test is complete. docker-compose run should run linked containers for you, but I have heard chatter on SO about a bug preventing that from working as intended right now.

docker-rails allows you to specify which container's error code is returned to the main process, so you CI server can determine the result. It is a great solution for CI and development for rails with docker.

For example

exit_code: web

in your docker-rails.yml will yield the web containers exit code as a result of the command docker-rails ci test. docker-rails.yml is just a meta wrapper around the standard docker-compose.yml that gives you the potential to inherit/reuse the same base config for different environments i.e. development vs test vs parallel_tests.

Building on kojiro's answer:

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

  1. get container IDs
  2. get last runs exit code for each container ID
  3. only status codes that does not start with '0'
  4. count number of non-0 status codes
  5. trim out white space

Returns how many non-0 exit codes were returned. Would be 0 if everything exited with code 0.

If you're willing to use docker-compose run to manually kick off your tests, adding the --rm flag, oddly enough, causes Compose to accurately reflect your command's exit status.

Here's my example:

$ 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.

Use docker wait to get the exit code:

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

foo is the "project name". In the example above, I specified it explicitly, but if you don't supply it, it's the directory name. bar is the name you give to the system under test in your docker-compose.yml.

Note that docker logs -f does the right thing, too, exiting when the container stops. So you can put

$ docker logs -f foo_bar_1

between the docker-compose up and the docker wait so you can watch your tests run.

Since version 1.12.0, you can use the --exit-code-from option.

From documentation:

--exit-code-from SERVICE

Return the exit code of the selected service container. Implies --abort-on-container-exit.

--exit-code-from SERVICE and --abort-on-container-exit don't work in scenarios where you need to run all containers to completion, but fail if one of them exited early. An example might be if running 2 test suits in concurrently in different containers.

With @spenthil's suggestion, you can wrap docker-compose in a script that will fail if any containers do.

#!/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:]')

Then on your CI server simply change docker-compose up to ./docker-compose.sh up.

You can see exit status with:

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

In case you might run more docker-compose services with same name on one docker engine, and you don't know the exact name:

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

echo %? - returns exit code from test-chrome service

Benefits:

  • wait's for exact service to exit
  • uses service name, not container name