Использование 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, чтобы этого избежать. Например, 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

Льготы:

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

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.