Використання docker-compose з CI - як боротися з кодами виходу та демонізованими пов'язаними контейнерами?


89

Зараз наші агенти Jenkins генерують docker-compose.yml для кожного з наших проектів Rails, а потім запускають docker-compose. У docker-compose.yml є основний "веб-" контейнер, який містить rbenv та всі інші наші залежності Rails. Він пов’язаний з контейнером БД, який містить тестову БД Postgres.

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

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

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

Відповіді:


77

Починаючи з версії 1.12.0, ви можете використовувати цю --exit-code-fromопцію.

З документації :

--exit-код-від СЕРВІСУ

Поверніть код виходу обраного контейнера служби. Мається на увазі --abort-on-container-exit.


1
Це має бути правильним способом, якщо ви використовуєте docker-compose1.12.0 і вище. Можливо, це теж ваш випадок. Прикладом може бути: docker-compose up --exit-code-from test-unit. Зверніть увагу, що це не працювало для мене, поки я не додав a 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
Я зміг отримати цю роботу на Тревіс 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

Це справді чудово. У моєму випадку невдалий набір тестів, що працює в контейнерах, не змушує контейнери виходити з кодом 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 не працюють у сценаріях, коли вам потрібно запустити всі контейнери до кінця, але не вдається, якщо один із них вийшов раніше. Прикладом може бути, якщо одночасно запускати 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 на кожному тестовому контейнері у від’єднаному режимі змусило його працювати. Дякуємо, що поділилися :)
— Болді

2

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

Наприклад

exit_code: web

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


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}')

Дякуємо, що розпочали. Ось моя версія цього (який для мене працює краще б / к. Я думаю, що формат виводу команди змінився з моменту написання цієї відповіді) -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.