Выполнение команды run --all для большой инфраструктуры Terragrunt тратит время на две задачи. Первая — это реальная работа: планирование и применение изменений в OpenTofu/Terraform. Вторая — накладные расходы: планирование, при котором доступный уровень параллелизма остается неиспользуемым, и скачивание одних и тех же провайдеров каждым модулем заново. В Terragrunt 1.0 решены обе эти проблемы благодаря переработанному механизму планирования под названием «пул воркеров» (runner pool) и автоматическому кэшированию провайдеров для OpenTofu 1.10 и выше.
Ничто из этого не является новостью, если вы следите за списком изменений, но это стоит понять детально, поскольку обеспечиваемое ими ускорение не является фиксированным процентом. Оно растет вместе с размером и структурой вашей инфраструктуры. В этой статье объясняется, как работает каждый из этих механизмов и что (если вообще что-то) нужно сделать, чтобы получить от них выгоду.
Как Terragrunt планирует run --all
Когда вы запускаете команду для множества модулей:
Terragrunt обнаруживает модули в рабочем каталоге, читает их блоки dependency и dependencies и строит на их основе направленный ациклический граф (DAG). Этот граф становится очередью выполнения: для plan и apply зависимости выполняются перед модулями, которые от них зависят; для destroy порядок меняется на противоположный.
Модули выполняются параллельно до лимита, которым вы управляете с помощью --parallelism, но ограничение порядка всегда соблюдается. Модуль не запустится до тех пор, пока все, от чего он зависит, не завершится успешно.
Это было справедливо для Terragrunt на протяжении многих лет. Что изменилось, так это то, насколько агрессивно Terragrunt заполняет доступный уровень параллелизма, уважая при этом граф.
Старая модель: группы зависимостей
До появления пула воркеров Terragrunt группировал модули по глубине зависимостей и запускал группы одну за другой. Модули без зависимостей попадали в группу 1, модули, зависящие только от группы 1, — в группу 2 и так далее:
Эту модель легко понять и легко выводить в логи, но у нее есть структурный изъян: группа начинается только тогда, когда завершилась вся предыдущая группа. Если vpc выполняется восемь минут, а dns — тридцать секунд, кэш простаивает семь с половиной минут, даже несмотря на то, что его единственная зависимость завершилась почти сразу. Каждая группа ждет самый медленный модуль в группе.
Ошибки имели такую же крупнозернистую гранулярность. Один сбойный модуль мог нарушить всю границу группы, приведя к сбою модулей, которые не имели никаких отношений зависимости с тем, что сломалось.
Модель пула
RFC о пуле воркеров заменил группы очередью и пулом. Каждый обнаруженный модуль попадает в очередь с метаданными о том, что его блокирует. Модули без незавершенных зависимостей готовы к работе; остальные заблокированы. Terragrunt поддерживает пул воркеров, размер которого задается параметром --parallelism, и в любой момент, когда воркер свободен, а модуль готов, модуль запускается.
Когда модуль успешно завершается, Terragrunt удаляет его из списков заблокированных для зависящих от него модулей, и любой зависимый модуль, чей список только что опустел, сразу же получает право на выполнение. Нет никаких границ групп, которых нужно ждать. В примере выше cache запускается в тот момент, когда завершается dns, в то время как vpc все еще работает.
Обработка ошибок в то же время стала более точной. Когда модуль дает сбой, только его зависимые модули (и транзитивно зависящие от них) отмечаются как имеющие сбойного предка и пропускаются. Несвязанные модули продолжают работать. Если вы предпочитаете старое поведение с остановкой всего процесса, флаг --fail-fast возвращает его:
Почему прирост масштабируется с размером инфраструктуры
В худшем случае, когда каждый модуль на определенном уровне занимает одинаковое количество времени, пул и модель групп завершают работу одновременно. Реальная инфраструктура выглядит иначе. Время выполнения модулей сильно различается (подготовка DNS-записи и кластера баз данных не требуют одинакового объема работы), и чем больше модулей и глубины в вашем графе, тем больше времени простоя накапливает модель групп на границах групп. Пул устраняет именно это время простоя, поэтому улучшение пропорционально размеру инфраструктуры: небольшие стеки видят скромный прирост, в то время как инфраструктуры с сотнями модулей и неравномерным временем выполнения завершаются гораздо быстрее.
Ничего не нужно включать. Пул воркеров внедрялся экспериментально в серии версий 0.x и является движком для каждого run --all и run --graph в версии 1.x. Документация по очереди выполнения охватывает элементы управления для ее настройки, включая --parallelism, --queue-ignore-errors и фильтрацию модулей.
Кэширование провайдеров
Планирование — это лишь половина дела. Другая крупная статья затрат при масштабировании — скачивание провайдеров.
Проблема
Без кэширования каждый модуль скачивает собственную копию каждого используемого им провайдера и распаковывает ее в свой собственный каталог .terraform. Цифры быстро растут: архив провайдера AWS (v6.50.0) весит около 200 МБ и около 900 МБ в распакованном виде. Проект с 50 модулями тратит около 10 ГБ трафика и 45 ГБ дискового пространства на то, что должно было быть единственной загрузкой на 200 МБ.
В OpenTofu/Terraform есть встроенный кэш плагинов (TF_PLUGIN_CACHE_DIR), но исторически он был небезопасен при параллельном выполнении run --all: параллельные процессы повреждали записи друг друга в кэше. Это заставляло делать неприятный выбор между кэшированием и параллелизмом.
Автоматическое кэширование провайдеров в OpenTofu 1.10+
OpenTofu 1.10 сделал параллельный доступ к кэшу плагинов безопасным, и Terragrunt опирается на это напрямую. Когда Terragrunt обнаруживает OpenTofu 1.10 или новее, он автоматически устанавливает TF_PLUGIN_CACHE_DIR в общий каталог кэша для каждого порождаемого процесса OpenTofu. Каждый провайдер скачивается один раз и используется повторно каждым модулем. Это функция автоматического кэша провайдеров (Automatic Provider Cache Dir), и она включена по умолчанию. Никаких флагов, никакой конфигурации:
Расположение кэша по умолчанию: $HOME/.cache/terragrunt/providers в Linux (с учетом $XDG_CACHE_HOME, если он задан), $HOME/Library/Caches/terragrunt/providers в macOS и %LocalAppData%\terragrunt\providers в Windows. Вы можете указать другое место:
или отключить его для конкретного запуска:
Две вещи, на которые стоит обратить внимание. Этот путь требует OpenTofu 1.10 или новее и работает только с OpenTofu, а не с Terraform. Если требования не выполнены, функция молча ничего не делает, и вы переходите к поведению, описанному ниже.
Сервер кэша провайдеров для Terraform и старых версий OpenTofu
Если вы используете Terraform или OpenTofu старше версии 1.10, сервер кэша провайдеров (Provider Cache Server) в Terragrunt решает ту же проблему другим способом. Terragrunt запускает локальный сервер реестра, настраивает каждый процесс OpenTofu/Terraform на него с помощью сгенерированной конфигурации CLI, и сервер гарантирует, что каждый провайдер скачивается и сохраняется ровно один раз, а модули получают символические ссылки на общий кэш. Поскольку сервер управляет кэшем, он справляется с параллелизмом, с которым старый встроенный кэш плагинов не мог справиться.
По умолчанию он выключен. Включите его с помощью флага:
или переменной окружения, что удобно в CI:
О сервере также стоит знать даже при использовании актуального OpenTofu. При очень больших масштабах борьба за блокировки файловой системы между процессами OpenTofu, синхронизирующимися в общем каталоге кэша, может стать узким местом, и сервер кэша позволяет этого избежать. Документация по производительности описывает, когда какой подход побеждает.
Загрузка исходного кода
Модули и юниты также многократно запрашивают исходный код модулей и самих юнитов. Хранилище с адресацией по содержимому (CAS) дедуплицирует эти запросы точно так же, как кэш провайдеров дедуплицирует загрузки провайдеров: оно сохраняет загруженный контент по хешу и выполняет повторные запросы локально. Это стало стандартом в Terragrunt 1.1, чему посвящен подробный пост о релизе Terragrunt 1.1.
Что и когда включать
- OpenTofu >= 1.10, текущая версия Terragrunt: ничего. Пул воркеров и автоматическое кэширование провайдеров включены по умолчанию.
- Terraform или OpenTofu < 1.10: добавьте --provider-cache (или TG_PROVIDER_CACHE=1) к вашим вызовам run --all. Пул воркеров по-прежнему применяется автоматически.
- В любом случае: не задавайте переменную TF_PLUGIN_CACHE_DIR самостоятельно при использовании run --all с Terraform или OpenTofu до версии 1.10. Это воссоздаст проблему параллельного повреждения данных, для предотвращения которой и существует кэш-сервер.
Несколько важных нюансов для CI. Оба кэша представляют собой директории в локальной файловой системе, поэтому на временных (ephemeral) раннерах они приносят пользу только в рамках одного задания, если только вы не сохраняете директорию кэша между запусками. Кроме того, кэш-сервер добавляет накладные расходы на запуск, поэтому для одиночного запуска terragrunt plan это может дать отрицательный эффект в целом; однако он полностью окупается при использовании run --all, где множество юнитов делят общую экономию.
В CI эти преимущества суммируются. Каждый план для каждого пулл-реквеста обходит один и тот же граф и запрашивает одних и тех же провайдеров, поэтому сокращение времени работы run --all на минуты экономит эти минуты при каждом запуске пайплайна, которого ждет ваша команда. Если вы управляете этими запусками с помощью Gruntwork Pipelines, улучшения планирования и кэширования применяются к каждому плану и применению автоматически, без каких-либо изменений в пайплайне.
Заключение
В основе прироста производительности лежат два механизма: пул воркеров запускает каждый юнит в момент завершения его зависимостей, не дожидаясь окончания границ групп, а кэширование провайдеров превращает N идентичных загрузок в одну. В OpenTofu 1.10+ с текущей версией Terragrunt оба механизма уже работают на вас. В Terraform или более старых версиях OpenTofu один флаг обеспечивает работу половины, отвечающей за кэширование. В документации по Run Queue, Automatic Provider Cache Dir и Provider Cache Server содержатся все подробности.
Такие улучшения, как пул воркеров и кэширование провайдеров, являются результатом нашей работы по поддержке крупных инфраструктур на базе Terragrunt. Если вы решаете именно эту задачу, обратите внимание на Terragrunt Scale.
Существует даже бесплатный тариф, на который вы можете зарегистрироваться, чтобы протестировать его уже сегодня.
