Привет, Хабр! Я Алексей Колосков, 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‑хоста.
А теперь следите за руками:
Тушим деплой‑хост, увеличиваем ресурсы, запускаем.
Снова применяем Terraform.
Ловим ошибку о заблокированном стейте, разблокируем стейт.
Снова применяем Terraform.
Terraform теперь снова долго пыхтит, и спустя примерно час мы получаем план.
Просматриваем план (вы же всегда анализируете план перед применением?) и... находим ошибку.
Исправляем ошибку.
Снова запускаем Terraform.
-
Он опять пыхтит примерно час, выдаёт план…
Думаю, вы поняли. Так пишется картина известного художника:
Далее инженеры начали искать решение системной проблемы. Что можно сделать с очень большим стейтом Terraform? Правильно, разбить на много маленьких. Это можно сделать и в Terraform, однако между различными ресурсами может быть много зависимостей, а делать зависимости между ресурсами в разных стейтах Terraform — не лучшая практика. Поэтому инженеры нашего стартапа стали искать подходящий инструмент.
Инструментов можно подобрать много, однако одним из самых популярных является Terragrunt. Его и выбрали.
Итак, на сцену выходит новый супергерой.
Нас спасёт Terragrunt!
Простыми словами, Terragrunt — это такая обёртка‑враппер над Terraform. Он помогает организовать инфраструктурный код более удобным и поддерживаемым образом. Позволяет избежать многоразового повторения кода Terraform и избежать использования других костылей связанных с ограничениями Terraform.
Terragrunt придерживается принципа DRY («don't repeat yourself»). Мы создаем необходимые модули Terraform, а в Terragrunt‑модулях уже вызываем их и только передаем необходимые параметры и описываем зависимости.
Также Terragrunt позволяет один раз описать бэкенд в корневом каталоге с использованием шаблонизации и в результате получить много маленьких стейтов, для каждого окружения свой. Теперь, если какой‑то стейт окажется заблокированным, с остальными мы сможем работать.
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 облака тоже можно увеличить через техподдержку.
Выводы
Какие из всего этого можно сделать выводы? Мы вывели четыре основных рекомендации:
Планировать заранее количество необходимых ресурсов.
Увеличивать квоты одним запросом. Когда уже есть понимание, какие ресурсы понадобится и сколько их — описать всё в одном тикете в техподдержку. Можно увеличивать квоты итеративно — но это может занять больше времени.
Ресурсов deploy‑хоста может не хватить. Если планируется большая инфраструктура — лучше заранее сделать хост побольше.
Использовать подходящие инструменты, такие как Terragrunt. Они позволяют сделать работу с Terraform значительно комфортней.
Делюсь с вами примером инфраструктурного terragrunt-репозитория для деплоя в Yandex Cloud.
Другие полезные ссылки
Документация «Квоты и лимиты для сервисов Yandex Cloud»,
Документация по Terragrunt,
Посмотреть доклад «Деплой больших инфраструктур в Yandex Cloud» на
Yandex Scale Kazakhstan,Подписаться на канал Hilbert Team в Telegram, чтобы не пропустить выход новых материалов.
Thomas_Hanniball
Вывод один - всё делать постепенно, а не всё сразу. Деплоить 1 окружение, потом 5, а дальше пачкой по 10, а не сразу все 40. Это называется канареечный деплой.
Но статьях хорошая, в закладки добавил. Примерно по такому же сценарию будут работать и другие компании, которые перейдут в облако и будут там постепенно расти и масштабироваться.