Использование docker-compose с CI - как бороться с кодами выхода и демонизированными связанными контейнерами?

88

Прямо сейчас наши агенты Jenkins генерируют docker-compose.yml для каждого из наших проектов Rails, а затем запускают docker-compose up. Docker-compose.yml имеет основной «веб-контейнер», внутри которого находится rbenv и все другие наши зависимости Rails. Он связан с контейнером БД, который содержит тестовую БД Postgres.

Проблема возникает, когда нам действительно нужно запустить тесты и сгенерировать коды выхода. Наш CI-сервер будет развернут только в том случае, если тестовый сценарий вернет exit 0, но docker-compose всегда возвращает 0, даже если одна из команд контейнера не работает.

Другая проблема заключается в том, что контейнер БД работает бесконечно, даже после того, как веб-контейнер завершил выполнение тестов, поэтому docker-compose upникогда не возвращается.

Есть ли способ использовать docker-compose для этого процесса? Нам нужно будет иметь возможность запускать контейнеры, но выходить после завершения веб-контейнера и возвращать его код выхода. Прямо сейчас мы застряли вручную, используя докер, чтобы развернуть контейнер БД и запустить веб-контейнер с параметром --link.

Логан Серман
источник

Ответы:

76

Начиная с версии 1.12.0, вы можете использовать --exit-code-fromопцию.

Из документации :

--exit-code-из СЕРВИСА

Вернуть код выхода выбранного сервисного контейнера. Подразумевается --abort-on-container-exit.

панджан
источник
1
Это должно быть правильным способом сделать это, если вы используете docker-compose1.12.0 и выше. Может быть, это и ваш случай. Примером может быть: docker-compose up --exit-code-from test-unit. Обратите внимание, что у меня это не сработало, пока я не добавил set -eв начало своего скрипта.
Адриан Антунес
--exit-code-fromне работает, -dхотя. Он выдаст следующие ошибки: using --exit-code-from implies --abort-on-container-exitи --abort-on-container-exit and -d cannot be combined.
ericat
3
Мне удалось заставить это работать над Travis CI: travis-ci.org/coyote-team/coyote/builds/274582053 вот travis.yml: github.com/coyote-team/coyote/blob/master/.travis.yml # L12
Субельский
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 об ошибке, мешающей этому работать, как задумано прямо сейчас.

Кодзиро
источник
Проблема с запуском докера заключается в том, что он не дает никакого вывода при запуске с -T, и нам нужен вывод, чтобы мы могли проверять неудачные сборки.
Логан Серман,
1
@LoganSerman, с помощью которого вы можете проверить выводdocker-compose logs
kojiro
Есть ли способ постоянно направлять эти журналы в STDOUT во время выполнения, чтобы мы могли видеть их во время сборки CI?
Логан Серман,
-T
Кажется,
Некоторые команды, которые мы запускаем внутри контейнера для запуска тестов, могут запрашивать ввод, мы хотим запустить с -T, чтобы этого избежать. Например, Rbenv спрашивает, хотите ли вы переустановить версию Ruby, если она уже существует.
Логан Серман,
23

Основываясь на ответе Кодзиро:

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

  1. получить идентификаторы контейнеров
  2. получить код выхода последних прогонов для каждого идентификатора контейнера
  3. только коды состояния, которые не начинаются с '0'
  4. подсчитать количество кодов состояния, отличных от 0
  5. обрезать пустое пространство

Возвращает количество возвращенных кодов выхода, отличных от 0. Было бы 0, если бы все вышло с кодом 0.

Спентил
источник
Вы также можете использовать тихий вывод из docker-compose ps, например: docker-compose ps | grep -c "Exit 1"даст вам счетчик, на котором совпадает «Выход 1» в отображении из docker-compose ps(что обеспечивает красиво напечатанную сводную таблицу результатов). Коды выхода перечислены в столбце «Состояние».
eharik 03
Это действительно круто. В моем случае неудачный набор тестов, запущенный в контейнерах, не приводит к выходу контейнеров с кодом 1. Я не могу агрегировать, если какой-либо из них вышел с кодом 1, поскольку ни один из них не работает .... Любая идея, как с этим справиться кейс?
walkerrandophsmith,
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не работают в сценариях, когда вам нужно запустить все контейнеры до завершения, но не работают, если один из них завершился раньше. Примером может быть одновременное выполнение двух тестовых костюмов в разных контейнерах.

С помощью предложения @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
этот сценарий никогда не достигает секции выхода, поскольку другие контейнеры (например, базы данных, веб-приложения) работают постоянно. работает в автономном режиме, он завершает работу, как только контейнер поднимается
Baldy
Правильно, это работает, только если вы хотите запустить все контейнеры до конца. Наверное, не особо распространенный, но он был полезен для меня на момент написания, и я подумал, что поделюсь им.
Мэтт Коул
В любом случае проголосовал за ваш ответ, так как он помог мне добиться успеха! Добавление Docker wait для каждого тестового контейнера в отдельном режиме заставило его работать. Спасибо, что поделились :)
Baldy
2

docker-rails позволяет вам указать, какой код ошибки контейнера возвращается в основной процесс, чтобы ваш CI-сервер мог определить результат. Это отличное решение для CI и разработки рельсов с докером.

Например

exit_code: web

в вашем docker-rails.ymlдаст webкод выхода контейнера в результате команды docker-rails ci test. docker-rails.yml- это просто мета-оболочка вокруг стандарта, docker-compose.ymlкоторая дает вам возможность наследовать / повторно использовать одну и ту же базовую конфигурацию для разных сред, например, разработка vs test vs parallel_tests.

кросс
источник
2

В случае, если вы можете запустить больше служб для создания докеров с тем же именем на одном движке докеров, и вы не знаете точное имя:

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

echo %? - возвращает код выхода из сервиса test-chrome

Льготы:

  • ждать точного выхода службы
  • использует имя службы, а не имя контейнера
cvakiitho
источник
2

Вы можете увидеть статус выхода с помощью:

echo $(docker-compose ps | grep "servicename" | awk '{print $4}')
RT Bathula
источник
Спасибо, что начали. Вот моя версия этого (которая работает лучше для меня b / c, я думаю, что формат вывода команды изменился с тех пор, как был написан этот ответ) -docker-compose ps | grep servicename | grep -v 'Exit 0' && echo "Automation or integration tests failed." && exit 1
DTrejo