Використання 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.