首页 文章

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

提问于
浏览
64

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

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

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

有没有办法在这个过程中使用docker-compose?我们需要能够运行容器,但在Web容器完成后退出并返回它的退出代码 . 现在,我们手动使用docker来启动数据库容器并使用--link选项运行Web容器 .

9 回答

  • 21

    从版本 1.12.0 开始,您可以使用 --exit-code-from 选项 .

    来自documentation

    --exit-code-from SERVICE返回所选服务容器的退出代码 . 意味着 - 在容器上退出 .

  • 46

    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
    

    或者,您可以选择inspect死容器 . 您可以使用 -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 那么你将需要sigkill那个容器;这可能不是你想要的 . 相反,您可以使用 docker-compose up -d 来运行守护进程的容器,并在测试完成时手动终止容器 . docker-compose run 应该为你运行链接的容器,但我听到有关一个错误的喋喋不休,因为它阻止了它现在按预期工作 .

  • 1

    以kojiro的回答为基础:

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

    • 获取容器ID

    • 获取最后一次运行每个容器ID的退出代码

    • 只有不以'0'开头的状态代码

    • 计数非0状态代码的数量

    • 修剪出白色空间

    返回返回的非0退出代码的数量 . 如果所有内容都以代码0退出,则为0 .

  • 0

    使用 docker wait 获取退出代码:

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

    foo 是"project name" . 在上面的示例中,我明确指定了它,但是如果您没有't supply it, it' s目录名称 . bar 是您在docker-compose.yml中为待测系统提供的名称 .

    请注意, docker logs -f 也是正确的,当容器停止时退出 . 所以你可以把

    $ docker logs -f foo_bar_1
    

    docker-compose updocker wait 之间,您可以观看您的测试运行 .

  • 34

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

    这是我的例子:

    $ docker-compose -v
    docker-compose version 1.7.0, build 0d7bf73
    
    $ (docker-compose run kpi false) || echo 'Test failed!'  # False negative.
    
    $ (docker-compose run --rm kpi false) || echo 'Test failed!'  # True positive.
    Test failed!
    
    $ (docker-compose run --rm kpi true) || echo 'Test failed!'  # True negative.
    
  • 4

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

    docker-rails允许您指定将哪个容器的错误代码返回到主进程,因此CI服务器可以确定结果 . 对于带有docker的rails,CI和开发是一个很好的解决方案 .

    例如

    exit_code: web
    

    docker-rails.yml 中,由于命令 docker-rails ci test ,将产生 web 容器退出代码 . docker-rails.yml 只是标准 docker-compose.yml 的元包装器,它为您提供了为不同环境继承/重用相同基本配置的潜力,即开发与测试对比parallel_tests .

  • 6

    您可以看到存在状态:

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

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

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

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

    优点:

    • 等待确切的服务退出

    • 使用服务名称,而不是容器名称

相关问题