Интроспекция стадий в интерактивном режиме

Контролировать результат работы правил сборки образа позволяет возможность интроспекции стадий. Интроспекция представляет собой запуск shell-сессии для пользователя в интерактивном режиме в собираемом образе. При интроспекции стадии, сборочный контейнер и его среда, содержат служебный инструментарий и служебные переменные окружения. Служебный инструментарий представляет собой набор специальных утилит, необходимых для сборки (его добавление осуществляется монтированием директорий из служебных контейнеров дистрибутивов dappdeps - в сборочном контейнере они доступны по пути /.dapp/deps).

В общем случае, если падает набор команд, создающих образ стадии Y из образа стадии X, то:

Если выполнение набора команд, создающих образ стадии Y из образа стадии X, завершается ошибкой, то:

  • c опцией --introspect-before-error пользователь попадет в контейнер с образом X;
  • с опцией --introspect-error пользователь попадет в сборочный контейнер в состоянии сразу после исполнения команды, которая завершилась ошибкой.

Например, есть Dappfile с ошибкой:

dimg do
  docker.from 'ubuntu:16.04'

  shell.before_install do
    run 'apt-get update'
  end

  shell.install do
    run 'apt-get install -y nonexistent'
  end
end

При запуске сборки dapp упадет с подобным сообщением:

$ dapp dimg build
From ...                                                                              [OK] 0.95 sec
Before install ...                                                                    [OK] 10.98 sec

Install group
  Install ...   Launched command: `apt-get install -y nonexistent`
                                                                                      [FAILED] 1.93 sec
Stacktrace dumped to /tmp/dapp-stacktrace-736a2035-4c8e-4ee3-9b55-8cfe5b4704a0.out
>>> START STREAM
Reading package lists...

Building dependency tree...

Reading state information...
E: Unable to locate package nonexistent
>>> END STREAM

Произошло следующее: при сборке образа стадии Install, выполнение команды apt-get install -y nonexistent завершилось ошибкой. При указании команде сборки опции --introspect-error, пользователь получает доступ в сборочный контейнер в состоянии сразу после исполнения команды apt-get install -y nonexistent:

$ dapp dimg build --introspect-error
From ...                                                                              [OK] 0.9 sec
Before install ...                                                                    [OK] 10.24 sec
Install group
  Install ...   Launched command: `apt-get install -y nonexistent`
                                                                                      [FAILED] 1.91 sec
root@18ae29cf201a:/# apt-get install -y nonexistent
Reading package lists... Done
Building dependency tree       
Reading state information... Done
E: Unable to locate package nonexistent
root@18ae29cf201a:/# apt-get install -y nginx
...
root@18ae29cf201a:/# exit
Stacktrace dumped to /tmp/dapp-stacktrace-4ecac017-bcaf-4304-b6e7-fe7ca481c7af.out
>>> START STREAM
Reading package lists...

Building dependency tree...

Reading state information...
E: Unable to locate package nonexistent
>>> END STREAM

В данном контейнере можно вручную выполнить команды, просмотреть состояние системы и понять в чем проблема.

Если использовать опцию --introspect-before-error для команды сборки, то пользователь соответственно получит доступ в сборочный контейнер для стадии, предшествующей стадии Before install. Т.е. ни одна команда для сборки стадии Before install в данном контейнере еще не будет выполнена.

Интроспекция стадий после успешной сборки

Во время разработки конфигурации, часто требуется запустить сборку, а затем вручную проверить результат сборки стадии. В случае если ошибок при сборке стадии не произошло, для этого используются опции --introspect-stage=<stage> для обычного образа и --introspect-artifact-stage=<stage> для образа артефакта. Возможно указание лишь одной стадии, для которой нужна интроспекция, но опции интроспекции стадии при возникновении ошибок и интроспекции успешно собранной стадии можно указывать одновременно.

Интроспекция стадий также позволяет прийти к необходимому результату в сборочном контейнере, а затем перенести соответствующие шаги (инструкции) в конфигурацию соответствующей стадии. Такой подход может быть полезен когда поставленная задача ясна, но необходимые для ее решения шаги не очевидны и требуют экспериментов. Также, при использовании интроспекции с Ansible-сборщиком, появляется возможность отладки Ansible playbooks в сборочном контейнере с последующим переносом Ansible-задач в соответствующие стадии конфигурации.

Возможные значения опции introspect-stage:

  • from
  • before_install
  • before_install_artifact
  • g_a_archive
  • g_a_pre_install_patch
  • install, g_a_post_install_patch
  • after_install_artifact
  • before_setup
  • before_setup_artifact
  • g_a_pre_setup_patch
  • setup
  • g_a_post_setup_patch
  • after_setup_artifact
  • g_a_latest_patch
  • docker_instructions

Возможные значения опции introspect-artifact-stage:

  • from
  • before_install
  • before_install_artifact
  • g_a_archive
  • g_a_pre_install_patch
  • install
  • g_a_post_install_patch
  • after_install_artifact
  • before_setup
  • before_setup_artifact
  • g_a_pre_setup_patch
  • setup
  • after_setup_artifact
  • g_a_artifact_patch
  • build_artifact

Сборочный кэш

Сборщик образов в dapp создает промежуточные docker образы после каждой успешной сборки стадии. Однако, создаваемые в процессе сборки образы являются скрытыми от пользователя dapp до завершения сборки. После сборки все промежуточные образы именуются и попадают в кэш образов. Образы из кэша используются при параллельных и повторных сборках, а также их можно интроспектить вручную через docker run.

Альтернативная схема кэширования

Стандартное поведение dapp предполагает, что при сборке формируется один docker-образ на все инструкции каждой стадии. Таким образом, любое изменение инструкций стадии приводит к полной пересборке стадии, со всеми её инструкциями. При выполнении ресурсоемких инструкций, это может требовать значительных временных затрат. Также, если сборка падает на одной из последних инструкций, то сборку необходимо выполнять заново, не имея возможности получить состояние среды до упавшей инструкции.

Для удобства разработки и отладки в dapp была введена альтернативная схема кэширования, которая доступна только в YAML-конфигурации и включается директивой asLayers. Директива asLayers может быть указана в конфигурации (dappfile.yaml) для конкретного приложения или его артефакта. При включении альтернативной схемы кэширования, dapp формирует один docker-образ на каждую команду для shell или каждый task для Ansible, что приводит к тому, что инструкции при сборке кэшируются по отдельности. Пересборка образа в этом случае осуществляется только при изменении порядка инструкций.

В случае использования опции интроспекции --introspect-before-error при альтернативной схеме кэширования, пользователь получит среду выполнения предыдущей перед проблемной инструкции, а не предыдущей стадии.

Для включение альтернативной схемы кэширования, в dappfile.yaml необходимо указать asLayers: true. При отсутствии директивы asLayers, или в случае указания asLayers: false, используется кэширование по умолчанию. Переключение между режимами сборки регулируется только директивой asLayers — остальные инструкции конфигурации остаются неизменными.

После того, как сборочные инструкции отлажены, asLayers необходимо выключить, т.к. альтернативная схема кэширования порождает избыточное количество docker-образов и не рассчитана на инкрементальную сборку (т.к. увеличивается время ожидания и размер сборочного кэша).

Режим разработчика

Для облегчения и ускорения процесса разработки конфигурации сборки, в dapp предусмотрен специальный режим разработчика, включаемый при использовании опции --dev. Особенности режима разработчика:

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

При разработке конфигурации, в стандартном режиме сборки необходимо коммитить изменения в git-репозиторий, чтобы dapp учитывал изменения. В режиме разработчика же, dapp при сборке учитывает некоммитнутые изменения локального Git-репозитория (учитываются пути из поддиректив git: add, includePaths, excludePaths, stageDependencies) и необходимости обязательно выполнять коммиты перед сборкой нет. Это может быть удобно, например, если вы на узле сборки вносите изменения в код приложения и хотите сразу же проверить ваши изменения. При добавлении git-submodules и вложенных Git-репозиториев, в режиме разработчика учитываются файлы .gitignore на всех уровнях.

Отдельный кэш образов создаваемый в режиме разработчика, никак не затрагивает обычный кэш образов используемый dapp, а также принудительно сохраняет успешно собранные образы, не смотря на появление ошибок на последующих этапах. Это может быть полезно, если вы хотите внести изменения и проверить сборку, но не хотите влиять на текущий кэш сборки. Также можно выполнять работу в режиме разработчика, чтобы по её завершении удалить только кэш режима разработчика.

Подробнее про кэширование в режиме разработчика рассказано в соответствующем разделе.