Использование docker-compose с CI - как работать с кодами выхода и демонтированными связанными контейнерами?
Сейчас наши агенты Jenkins генерируют docker-compose.yml для каждого из наших проектов Rails, а затем запускают сборку докеров. У docker-compose.yml есть основной "веб-контейнер", в котором есть rbenv и все наши другие Rails-зависимости внутри. Он связан с контейнером DB, который содержит тестовую базу Postgres.
Проблема возникает, когда нам нужно фактически запускать тесты и генерировать коды выхода. Наш CI-сервер будет развертываться только в том случае, если тест script возвращает выход 0, но docker-compose всегда возвращает 0, даже если одна из команд контейнера терпит неудачу.
Другая проблема заключается в том, что контейнер DB работает неограниченно, даже после того, как веб-контейнер завершен, поэтому docker-compose up
никогда не возвращается.
Есть ли способ, которым мы можем использовать docker-compose для этого процесса? Нам нужно будет иметь возможность запускать контейнеры, но выходить после завершения веб-контейнера и возвращать его код завершения. Прямо сейчас мы застреваем вручную, используя докер, чтобы развернуть контейнер БД и запустить веб-контейнер с опцией -link.
Ответы
Ответ 1
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
должен запускать связанные контейнеры для вас, но я слышал болтовню о SO об ошибке, которая препятствует тому, чтобы она работала по назначению прямо сейчас.
Ответ 2
Основываясь на ответе Коджиро:
docker-compose ps -q | xargs docker inspect -f '{{ .State.ExitCode }}' | grep -v 0 | wc -l | tr -d ' '
- получить идентификаторы контейнера
- получить последний код выхода для каждого идентификатора контейнера
- только не-коды состояния
- подсчитать количество кодов состояния, отличных от 0
- обрезать пробел.
Возвращает количество возвращаемых кодов, отличных от 0. Было бы 0, если все вышло с кодом 0.
Ответ 3
Начиная с версии 1.12.0
, вы можете использовать опцию --exit-code-from
.
Из документация:
- exit-code - из SERVICE
Возвращает код выхода выбранного контейнера службы. Подразумевает - выход из контейнера-контейнера.
Ответ 4
Если вы хотите использовать 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.
Ответ 5
Используйте 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
, чтобы вы могли наблюдать за выполнением ваших тестов.
Ответ 6
docker-rails позволяет указать, какой код ошибки контейнера возвращается основному процессу, поэтому сервер CI может определить результат. Это отличное решение для CI и разработки для рельсов с докером.
Например
exit_code: web
в вашем docker-rails.yml
выдает код выхода контейнеров web
в результате команды docker-rails ci test
. docker-rails.yml
- это всего лишь мета-обертка вокруг стандартного docker-compose.yml
, который дает вам возможность наследовать/повторно использовать ту же базовую конфигурацию для разных сред, то есть разработки против теста vs parallel_tests.
Ответ 7
--exit-code-from SERVICE
и --abort-on-container-exit
не работают в сценариях, где вам нужно запустить все контейнеры до завершения, но сбой, если один из них вышел раньше. Например, если вы запускаете 2 тестовых костюма одновременно в разных контейнерах.
С предложением @spenthil вы можете обернуть docker-compose
в script, который будет терпеть неудачу, если какие-либо контейнеры.
#!/bin/bash
set -e
# Wrap docker-compose and return a non-zero exit code if any containers failed.
docker-compose "[email protected]"
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
.
Ответ 8
Вы можете видеть существующий статус с помощью:
echo $(docker-compose ps | grep "servicename" | awk '{print $4}')