Привет, Хабр! Я Алексей Колосков, DevOps‑инженер Hilbert Team. Сегодня я расскажу вам историю о компании, которая из маленькой облачной инфраструктуры выросла до сложного решения с использованием Infrastructure as Code (IaC) и Terraform. И, как это часто бывает, в процессе роста учли не всё. Все персонажи вымышлены и любые совпадения случайны (ну, почти).

В этой статье покажу грабли, на которые можно наступить при росте облачной инфраструктуры, и расскажу, как их обойти.

Сначала инфраструктура была маленькой

Итак, жила‑была одна компания, которая решила выйти на рынок со своим небольшим стартапом. Чтобы не было заморочек с on‑premise‑инфраструктурой, решили развернуть всё в Yandex Cloud.

Самый первый вариант инфраструктуры выглядел следующим образом:

*схема инфраструктуры упрощена для статьи.
*схема инфраструктуры упрощена для статьи.

Каталог Common Folder включает общие ресурсы: Compute Cloud с deploy‑host для доступа к инфраструктуре и GitLab Managed Service для хранения репозиториев приложений и конфигураций CI/CD.

Каталог Env Folder предназначен для рабочего окружения, в нём расположен Managed Kubernetes, а в нём: GitLab Runner, который собирает и билдит образы вашего приложения, GitLab Agent для деплоя приложений, а также само приложение. В этом каталоге размещён Yandex Container Registry для хранения и управления Docker‑образами, Managed PostgreSQL для хранения данных приложения.

Такую инфраструктуру можно поднять даже «руками»: просто накликать через веб‑интерфейс, что в нашей маленькой компании и сделали.

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

Инфраструктура подросла

В результате облако стало выглядеть так:

Итак, инфраструктура выросла до пяти окружений, и разворачивать всё вручную стало, во‑первых, долго, а во‑вторых, заметно увеличился риск человеческой ошибки, из‑за чего времени на траблшутинг стало уходить гораздо больше.

Настало время автоматизации развёртывания инфраструктуры с применением подхода IaC (Infrastructure as Code) — и для этого в мире DevOps есть признанный стандарт: Terraform от HashiCorp.

Пора автоматизировать и звать на помощь Terraform

Напомню вкратце, что за зверь этот Terraform. Любое облако с точки зрения инженера — это API. Инженер описывает требуемое состояние облачной инфраструктуры в HCL‑манифестах. Задача Terraform — привести облачные ресурсы к описанному состоянию. Для того чтобы узнать, как должно быть, Terraform анализирует эти HCL‑файлы. А чтобы узнать, как есть — Terraform идёт в API облака, используя Terraform Provider который ‭«знает», как работать с API данного конкретного облака, смотрит состояние ресурсов в облаке и сохраняет их себе в стейт. После сверяет описанную в манифестах инфраструктуру со стейтом и выполняет необходимые вызовы к API облака, чтобы привести состояние инфраструктуры к описанному:

  • создаёт ресурсы, которых нет;

  • удаляет ресурсы, которых не должно быть;

  • изменяет ресурсы, которые отличаются.

Основное, что из этого следует запомнить — в Terraform‑стейте хранится состояние всех облачных ресурсов.

Чтобы стало проще жить, инженеры нашего подросшего стартапа взяли свою облачную инфраструктуру и описали её в Terraform:

  • Описали один каталог в Terraform.

  • Копировали его 4 раза.

  • Применили необходимые параметры. Поскольку инфраструктурные компоненты в K8s задеплоили также с помощью Terraform, то для каждого каталога настроили Kubernetes‑провайдер на подключение к своему кластеру.

  • Запустили создание инфраструктуры с помощью Terraform.

  • И… получили ошибку.

  • Стали разбираться и выяснили, что упёрлись в квоты облака на количество групп безопасности.

❗ Небольшое лирическое отступление. Группы безопасности в Yandex Cloud — это такой ресурс, связанный с сетевой безопасностью. В них описаны правила обработки сетевых пакетов: например, с каких IP-адресов по каким протоколам и на какие порты разрешён входящий или исходящий сетевой трафик. Группы безопасности назначаются виртуальным машинам или сервисам у которых под капотом виртуальные машины, например, управляемые базы данных или Managed Kubernetes.

Именно так большинство новичков обычно сталкиваются со спецификой облачной инфраструктуры и начинают постигать разницу между квотами и лимитами. Давайте вспомним, что же скрывается за этими понятиями.

Квоты и лимиты есть в любом зрелом облаке.

Квоты — организационные ограничения, которые вас защищают от случайного перерасхода бюджета (например, ограничения на vCPU, количество дисков, сетей, подсетей), а облако защищают от ошибок пользователей, которые случайно могут создать миллион виртуальных машин, сетей или других ресурсов. Так как квоты — это организационные ограничения, их можно расширить. В Yandex Cloud для этого предназначена специальная форма, в которой вы указываете количество нужных вам ресурсов. В результате автоматически создаётся тикет в техподдержку, который в большинстве случаев сразу выполняется.

Лимиты — технические ограничения, обусловленные особенностями архитектуры Yandex Cloud (например, 4 GPU на 1 ВМ, 32 vCPU на 1 ВМ). Изменить лимиты невозможно. Подробнее можете почитать о них в документации.

Как может выглядеть процесс расширения квот:

  • Открываем в веб‑интерфейсе Наше облако > Квоты.

  • Смотрим, чего не хватает. Например, упёрлись в количество групп безопасности.

  • Считаем и указываем нужное количество групп безопасности и запрашиваем увеличение.

  • Автоматически создаётся тикет и автоматически же резолвится (не прошло и минуты).

  • Всё ок, едем дальше.

Если у нас небольшая инфраструктура из пяти окружений, то с увеличением квот можно разобраться быстро в пару кликов.

Итак, инженеры стартапа расширили квоты, применили Terraform — ошибок нет, инфраструктура создалась. Но на этом история не заканчивается.

Ещё подросли. (Не)множко

Наш стартап продолжил расти и развиваться, немного потратился на рекламу: вышло удачно, пришло много новых клиентов… И ВНЕЗАПНО окружений стало нужно 80 — такие уж особенности архитектуры, количество окружений пропорционально количеству клиентов.

Начинаются приключения!

Что делать, когда появляется потребность масштабировать ресурсы не просто кратно, а в 10 с лишним раз?

Возникает сразу несколько трудностей:

  • Как вы помните, для пяти окружений мы копировали terraform‑код каталога с окружением и меняли настройки вручную. Но для 80 окружений это может оказаться затратно по времени.

  • Плюс при ручном изменении настроек вероятность ошибиться и потонуть в болоте траблшутинга также очень велика.

На помощь к нам снова приходит автоматизация!

Быстрое решение — автоматизировать всё bash‑скриптами. Так и сделали инженеры нашего стартапа. Однако bash — не лучший инструмент для сложной автоматизации и подходит только как быстрое решение. Поэтому записали в техдолг автоматизировать как положено.

Давайте делать по порядку.

Первое: помните, что стартап деплоил в K8s всё так же с помощью Terraform, и нужно передавать kubeconfig Terraform‑провайдеру? Эта задача решается шаблонизацией. В этом поможет утилита envsubst.

Второе: кратный рост количества окружений означает резкое увеличение квот в несколько раз и может не пройти автоматически. Для этого потребуется согласовать увеличение квот с техподдержкой. Ребята ответят быстро, но нужно чётко и понятно объяснить, зачем нужно столько ресурсов сразу. Общение сложностей не вызывает, с ребятами из саппорта Yandex Cloud всегда приятно поговорить. Но нечёткая формулировка запроса может увеличить время ожидания.

Итак, написали необходимые скрипты автоматизации, в этот раз компания подготовилась, заранее увеличила квоты, применила Terraform и… снова получила ошибку. Смотрим, что за ошибка: и это оказываются снова квоты! Но их же уже увеличили? Давайте разберёмся. Инфраструктура была описана таким образом, что Terraform сначала создавал каталоги, которых уже 81 (один инфраструктурный и 80 под окружения), — но по умолчанию в Yandex Cloud есть неявная квота на 50 каталогов.

Написали тикет, в ответ техподдержка запросила информацию о цели и необходимости увеличения, описали ситуацию — получили ответ, что для решения нужна помощь коллег. Снова ожидание. В течение часа квота на каталоги была увеличена. Однако стало ясно, как может помочь для экономии времени заранее оформленный запрос: если в одном тикете описать кейс и все квоты, которые нужно увеличить, будет быстрее.

С квотами разобрались. Снова применили Terraform, он пыхтел минут 40, после чего съел все ресурсы deploy‑хоста, и хост завис. И снова давайте разбираться. Что мы имеем? 80 окружений, в каждом по Kubernetes, плюс репозитории в GitLab, деплой инфраструктурных компонентов в K8s и ещё много чего… Сразу хочу отметить, что на данном этапе компания запускала Terraform руками с виртуальной машины внутри облака, где на неё навешен сервисный аккаунт с правами на создание ресурсов. К слову, у этой ВМ 2 vCPU и 4 Гб оперативной памяти, а вдобавок ещё и медленный сетевой HDD‑диск. Итог: порядка 3000 ресурсов, которые Terraform просто не смог прожевать на такой ВМ.

Решение — нужно увеличить ресурсы deploy‑хоста.

А теперь следите за руками:

  1. Тушим деплой‑хост, увеличиваем ресурсы, запускаем.

  2. Снова применяем Terraform.

  3. Ловим ошибку о заблокированном стейте, разблокируем стейт.

  4. Снова применяем Terraform.

  5. Terraform теперь снова долго пыхтит, и спустя примерно час мы получаем план.

  6. Просматриваем план (вы же всегда анализируете план перед применением?) и... находим ошибку.

  7. Исправляем ошибку.

  8. Снова запускаем Terraform.

  9. Он опять пыхтит примерно час, выдаёт план…

    Думаю, вы поняли. Так пишется картина известного художника:

Далее инженеры начали искать решение системной проблемы. Что можно сделать с очень большим стейтом Terraform? Правильно, разбить на много маленьких. Это можно сделать и в Terraform, однако между различными ресурсами может быть много зависимостей, а делать зависимости между ресурсами в разных стейтах Terraform — не лучшая практика. Поэтому инженеры нашего стартапа стали искать подходящий инструмент.

Инструментов можно подобрать много, однако одним из самых популярных является Terragrunt. Его и выбрали.

Итак, на сцену выходит новый супергерой.

Нас спасёт Terragrunt!

Простыми словами, Terragrunt — это такая обёртка‑враппер над Terraform. Он помогает организовать инфраструктурный код более удобным и поддерживаемым образом. Позволяет избежать многоразового повторения кода Terraform и избежать использования других костылей связанных с ограничениями Terraform.

Terragrunt придерживается принципа DRY («don't repeat yourself»). Мы создаем необходимые модули Terraform, а в Terragrunt‑модулях уже вызываем их и только передаем необходимые параметры и описываем зависимости.

Terraform code DRY
Terraform code DRY

Также Terragrunt позволяет один раз описать бэкенд в корневом каталоге с использованием шаблонизации и в результате получить много маленьких стейтов, для каждого окружения свой. Теперь, если какой‑то стейт окажется заблокированным, с остальными мы сможем работать.

Backend code DRY
Backend code DRY

Terragrunt — это CLI‑утилита, она в том числе позволяет передавать команды Terraform, например, force-unlock, или тот же plan/apply. А ещё Terragrunt позволяет описать зависимости между Terragrunt‑модулями, чтобы определить порядок создания ресурсов, описанных в разных модулях, а также использовать выходные значения модулей в качестве значений в параметрах других модулей. Напомню, что у каждого модуля Terragrunt свой Terraform‑стейт. То есть фактически мы получаем механизм рабочих зависимостей между разными стейтами.

В результате можно выполнять команды в корневом каталоге или каталоге окружения, и ресурсы будут созданы в нужной последовательности. Например, сначала создается управляемая БД и K8s, и только потом в K8s деплоится приложение, которое будет ходить в эту БД.

Если вы хотите лучше разобраться с Terragrunt, начните разбираться и вы разберётесь ?. В любом случае, всегда можно сходить в документацию.

Вернёмся к инфраструктуре

Таким образом, было принято решение избавиться от bash‑скриптов (привет техдолг!) и переписать всё на Terragurnt. Снова запустили создание инфраструктуры — всё стало значительно лучше, процесс занял не час, а порядка 20 минут. Но ошибки всё равно были, нужно было ещё немного разобраться.

Причиной новых трудностей оказался лимит на количество одновременных обращений к API облака. По умолчанию Terragrunt не лимитирует количество одновременно обрабатываемых модулей. Например, если модулей пять — он будет применять одновременно все пять, разумеется, с учётом зависимостей. Если десять — то десять, и так далее.

Ограничить количество одновременно обрабатываемых модулей можно с помощью опции --terragrunt-parallelism. Если запустить Terragrunt с опцией --terragrunt-parallelism, нас наконец ждёт успех — получаем развёрнутую инфраструктуру.

Кому‑то это может показаться всё равно долго. Поэтому есть лайфхак: лимит на количество обращений к API облака тоже можно увеличить через техподдержку.

Выводы

Какие из всего этого можно сделать выводы? Мы вывели четыре основных рекомендации:

  1. Планировать заранее количество необходимых ресурсов.

  2. Увеличивать квоты одним запросом. Когда уже есть понимание, какие ресурсы понадобится и сколько их — описать всё в одном тикете в техподдержку. Можно увеличивать квоты итеративно — но это может занять больше времени.

  3. Ресурсов deploy‑хоста может не хватить. Если планируется большая инфраструктура — лучше заранее сделать хост побольше.

  4. Использовать подходящие инструменты, такие как Terragrunt. Они позволяют сделать работу с Terraform значительно комфортней.

Делюсь с вами примером инфраструктурного terragrunt-репозитория для деплоя в Yandex Cloud.

Другие полезные ссылки

Комментарии (6)


  1. Thomas_Hanniball
    18.11.2024 17:06

    Вывод один - всё делать постепенно, а не всё сразу. Деплоить 1 окружение, потом 5, а дальше пачкой по 10, а не сразу все 40. Это называется канареечный деплой.

    Но статьях хорошая, в закладки добавил. Примерно по такому же сценарию будут работать и другие компании, которые перейдут в облако и будут там постепенно расти и масштабироваться.