Привет! Меня зовут Михаил Кажемский, я ведущий DevOps-инженер в ИТ‑интеграторе Hilbert Team. В этой статье я расскажу о различных подходах к созданию унифицированных типовых R&D-окружений. Прочитав её, вы поймёте, как разрабатывать цифровые продукты быстрее и эффективнее и избавить команды разработчиков от лишней рутины.
В чём суть проблемы
Возьмём для примера какой-нибудь средний или крупный бизнес, допустим, банк. В банке суровый энтерпрайз, много внутренних и внешних продуктов, и разработкой занимается большое количество команд. При этом у каждой команды процессы деплоя налажены по-своему.
Чтобы минимизировать расходы и помочь разработчикам сфокусироваться на качестве и безопасности продукта, а не на рутинных действиях, все эти процессы нужно унифицировать. Важно, чтобы у каждой команды был способ создания типового окружения, которое она могла бы использовать для разработки и тестирования продукта.
Такая инфраструктура должна соответствовать трём основным требованиям:
Унификация
Процесс развёртывания различных компонентов окружений должен быть общим для всех команд. Например, компонент PostgreSQL должен быть один для всех.Кастомизация
Разные команды имеют разный набор параметров и компонентов, поэтому необходима возможность кастомизировать окружения под себя.Безопасность
В компании есть различные требования к безопасности, и все окружения должны им соответствовать.
Глобально существует три общих подхода к созданию таких окружений:
динамический, при котором окружения создаются и удаляются по какому-то событию, например, при создании и удалении ветки в Git);
статический, при котором окружения создаются заранее и существуют до тех пор, пока в них есть необходимость;
подход GitOps.
Дальше я расскажу подробнее о реализации таких подходов:
создание динамических окружений в Kubernetes;
создание статических окружений с помощью Terraform и Terragrunt;
GitOps-подход — создании окружений с помощью CrossPlane и ArgoCD.
Динамические окружения в Kubernetes
Динамические окружения в Kubernetes представляют собой полностью изолированные Kubernetes namespaces, содержащие как необходимые инфраструктурные сервисы, например, БД и KVS, так и сами приложения и сервисы компании. Они динамически разворачиваются во время работы CD-пайплайна, например, с помощью Helm.
Далее на схеме представлен пример реализации CI/CD-пайплайна динамических окружений:
В отдельном централизованном репозитории хранится Helm-чарт деплоя приложения, содержащий в качестве subchart чарты деплоя ресурсов, например, PostgreSQL. Сами subcharts по умолчанию выключены (disabled).
В репозитории R&D-команды хранится values.yaml, который переопределяет дефолтные значения чарта приложения, в том числе необходимые компоненты приложения.
В Hashicorp Vault хранятся секреты, например, пароли к БД. Монтирование происходит на этапе деплоя чарта.
С помощью GitLab CI/CD-пайплайна объединяются чарт приложения, кастомные значения и секреты. С помощью Helm Release приложение отправляется в нужный namespace со всеми зависимостями.
Namespace для деплоя соответствует имени ветки репозитория.
Какие плюсы имеет этот подход:
Простота реализации и поддержки
Достаточно реализовать относительно простой CI/CD-пайплайн с деплоем приложения в нужный кластер.Простота кастомизации разработчиками
Сервисы разворачиваются с помощью Helm-чартов, и у разработчиков есть возможность поправить values чарта. А в случае острой необходимости, разобраться с go-темплейтами Helm не составит большого труда, это поможет разгрузить инженеров, отвечающих за написание чартов.Возможность использования с on-premise инфраструктурой
Поскольку в этом подходе всё окружение разворачивается в нужном namespace и не требует никаких облачных зависимостей, то нет принципиальной разницы, где развёрнут кластер Kubernetes: в облаке или на физических серверах.Экономическая выгода
При развёртывании окружений можно включать только те компоненты, которые необходимо протестировать, то есть не включать все зависимости. Это существенно сэкономит ресурсы в кластере, если не забывать про ограничения.
Какие ограничения имеют динамические окружения:
Необходимость контролировать ресурсы
Бывает так, что веток и окружений создаётся слишком много. В итоге все ресурсы кластера, где создаются окружения, могут закончиться, поэтому у компонентов необходимо выставлять лимиты. Можно делать это у компонентах, создаваемых на namespace. Также важно следить за количеством окружений, мониторить, не создаётся ли их слишком много, и удалять их вручную или автоматически, если они долго не используются.Необходимость настраивать сетевые политики в кластере
По умолчанию окружения в Kubernetes никак не изолированы на сетевом уровне — сервисы из одного namespace по определённому fqdn могут достучаться до сервиса в другом namespace Потенциально это может привести к ошибкам, поэтому необходимо изолировать namespace на сетевом уровне, например, с помощью Kubernetes Network Policies.Невозможность управлять всей инфраструктурой
Настройка инфраструктуры и управление ей происходит другим способом. В данном методе создаётся namespace в кластере Kubernetes, но сам кластер разворачивается другими инструментами, в том числе вручную.
Альтернативой такого подхода являются статические окружения.
Статические окружения
Статические окружения, в отличие от динамических, создаются по предварительному запросу какой-либо из команд. То есть, например, под каждую из R&D-команд уже создано окружение со всеми необходимыми зависимостями. Сами зависимые компоненты могут быть сервисами IaaS и PaaS. Также не исключается возможность использования зависимых компонентов, общих для всех окружений, например, общего хранилища логов или метрик.
На схеме выше приводится пример реализации унифицированных статических окружений в Yandex Cloud. Они состоят из набора PaaS-сервисов, предоставляемых облачным вендором:
Yandex Managed Service for Kubernetes для compute-нагрузки, например, для размещения приложений и сервисов заказчика.
Yandex Managed Service for PostgreSQL, Yandex Managed Service for Elasticsearch и Yandex Object Storage для хранения данных приложений и диагностической информации, например, логов.
Создание статических окружений с помощью Terraform и Terragrunt
Одним из способов создания статических окружений является использование подхода Infrastructure as Code (IaC) и инструментов Terraform и Terragrunt:
Terraform используется как для декларативного описания отдельных инфраструктурных модулей, например, БД и кластера Kubernetes, так и для описания всей инфраструктуры унифицированного статического окружения, состоящего из набора модулей.
Terragrunt-модули представляют собой шаблоны для развёртывания и кастомизации конечных окружений. Кастомизация определяется фактическими значениями параметров.
Причиной использования Terragrunt является возможность расширения Terraform:
Terragrunt позволяет повторно использовать Terraform-код и упрощает процесс его поддержки: один и тот же модуль Terraform можно параметризировать и использовать для развёртывания различных окружений, изменив значения параметров.
Terragrunt позволяет декларативно определять зависимости модулей Terraform и порядок их исполнения.
С помощью Terragrunt можно фиксировать версии модулей Terraform и контролировать объём изменений.
На схеме представлен пример реализации CI/CD-пайплайна для статических окружений:
Репозиторий с модулями Terraform содержит декларативное описание необходимых компонентов, например, сервисов Managed PostgreSQL, Managed Elasticsearch, описания чартов приложений и чартов инфраструктурных компонентов. При слиянии в мастер в этом репозитории происходит автоматическое тегирование ветки в соответствии с Semantic Versioning.
Репозиторий с манифестами Terragrunt содержит в каждой папке манифесты, которые ссылаются на терраформ модули Terraform, и запускает их в нужной последовательности через dependency. Каждая папка соответствует разворачиваемому окружению — в данном примере common для общих сервисов, prod, dev, stage и testing. При запросах слияния для каждого изменённого манифеста прогоняется Tterragrunt plan. Вывод отдаётся в форму мерж-реквеста, чтобы было видно, какие изменения планируется выполнить для более быстрого ревью.
Из репозитория с манифестами запускается GitLab Runner, который рекурсивно проходит по папкам в репозитории и выполняет Terragrant apply. Tfstate хранится в S3-storage.
Изменения применяются в облаке.
В итоге получается следующее: каждой папке в манифест репозитории соответствует каталог в облаке. В каталоге common содержатся общие сервисы, такие как Managed Service for Elasticsearch, Managed Service for PostgreSQL, инфраструктурный Managed Kubernetes с хранилищем метрик и indenty provider. В других каталогах развёрнуты приложения, которые могут использовать общие сервисы из каталога common, например, БД. Эти каталоги объединены в одну сеть VPC.
Структура каталогов с модулями и манифестами выглядит следующим образом:
Также существует каталог testing для тестирования инфраструктурных сервисов. Он не связан с другими окружениями, поэтому его можно удалить при необходимости без вреда для прода. В основном он нужен для команды DevOps.
Также существует каталог testing для тестирования инфраструктурных сервисов. Он не связан с другими окружениями и его можно ломать как душе угодно. В основном он нужен для команды DevOps.
Плюсы данного подхода:
Прогнозируемость ресурсов
За счёт того, что окружения статичны, текущие и будущие затраты на ресурсы легко прогнозируются.Управление ресурсами одной командой
Управлять ресурсами может одна команда, например, команда DevOps, поэтому окружения легче адаптировать под требования безопасности компании.Унификация окружений Prod и Dev
Благодаря унификации артефакт, разработанный и протестированный в одном окружении, будет корректно работать в другом, например, в выделенном prod-окружении.Возможность использования сервисов Iaas и PaaS
Поскольку окружения являются каталогами-облаками, а не изолированными namespaces, есть возможность использовать managed-сервисы для их работы и снижать нагрузку на их обслуживание. В том числе можно использовать один managed-сервис для нескольких окружений, это положительно влияет на итоговую стоимость облака.Возможность управления инфраструктурой
Помимо управления окружениями для R&D-команд этот подход даёт возможность управлять всей облачной инфраструктурой организации.
Ограничения подхода:
Сложность кастомизации разработчиками
Язык HCL для Terraform/Terragrunt не самый очевидный для понимания разработчиками. В случае острой необходимости научить разработчиков писать манифесты Terragrunt или модули Terraform будет сложнее и дольше, чем объяснить, что следует поправить или дополнить в YAML-манифесте.Относительно сложный процесс деплоя
Деплой происходит посредством раннера, который последовательно проходит по всем манифестам в репозитории. В случае ошибки в одном из манифестов, деплой последующих не происходит, и процесс останавливается до решения проблемы. Ошибки могут возникнуть из-за configuration drift или других причин. Также не стоит забывать об управлении Terraform state, в данном случае оно решено хранением его в S3-бакете.Расширяемость
Последовательность прохода раннера по репозиторию создаёт проблему расширяемости. Если инфраструктура большая и содержит много окружений, то процесс деплоя может быть долгим и увеличиваться с ростом количества манифестов. Если манифестов немного, то добавить окружение можно, создав новую папку с его описанием по имеющемуся шаблону.
Получается, что Terraform/Terragrunt даёт возможность управлять инфраструктурой целиком с помощью одного инструмента. Но узким горлышком тут является сложность распараллеливания этого процесса. Также увеличивается вероятность ошибок из-за configuration drift, поскольку обновление state инфраструктуры происходит только при запуске раннера.
Распараллелить процесс, постоянно обновлять state инфраструктуры и снизить вероятные ошибки изменений, выполняемых вручную, поможет подход GitOps.
GitOps
По своей сути GitOps — это инфраструктура на основе кода и операционные процедуры, использующие Git в качестве исходной системы управления.
Push-модель
В этой модели используются CI/CD-пайплайны, чтобы отправить изменения инфраструктуры из состояния Git в окружение. В такой схеме пайплайн обычно запускается по событию коммита в репозиторий. При деплое с помощью Terraform как раз используется такой подход.
Pull-модель
Чаще всего под GitOps понимают именно Pull-модель взаимодействия с Git. Эта модель предполагает, что информация о том, что и где мы разворачиваем, расположена в Git-репозитории. Внешний агент мониторит обновления в нём, сравнивает с состоянием Kubernetes и при необходимости меняет конфигурацию. Чаще всего агент расположен в том кластере, где происходит развёртывание.
Использование этого подхода защищает от некоторых проблем. Например, если пользователь напрямую внесёт изменения в кластер Kubernetes, внешний агент увидит это и вернёт кластер в состояние, прописанное в Git. Это мотивирует пользователей вместо прямого внесения изменений в кластер делать правки в единственном источнике, которым выступает репозиторий.
Создание окружений с помощью CrossPlane и Argo CD
Пример инструмента, который нужен для развёртывания облачной инфраструктуры и при этом хорошо ложится на модель GitOps — это Crossplane. В практическом плане Crossplane с Terraform во многом похожи.
Главное отличие Crossplane в основной идее проекта: он работает как оператор Kubernetes, и в итоге Kubernetes, в том числе Managed, может являться Control Plane для всего облака. Состояние описывается именно в виде Custom Resources в Kubernetes, и Crossplane постоянно поддерживает его актуальность. Terraform, напротив, синхронизирует состояние только во время выполнения вышеупомянутых CLI-команд.
Если посмотреть на популярность и распространённость, то вокруг Terraform за годы образовалось большое сообщество и множество провайдеров. Crossplane пока не так развит, но поддерживает три самых популярных облачных провайдера: AWS, Google Cloud, Azure.
Благодаря инструменту Upjet есть возможность за минимальное время сгенерировать Crossplane-провайдер из Terraform-провайдера. Так, например, портирован провайдер для Yandex Cloud.
На схеме представлен один из вариантов реализации GitOps-подхода с использованием ArgoCD и Crossplane:
Предлагается использовать два репозитория: для инфраструктуры (1) и для сервисов (2) с ресурсами (приложениями) ArgoCD. В них указано, что и куда деплоить. Сам ArgoCD и Crossplane располагаются в кластере по инфраструктуре (3). В качестве манифестов в приложении Argo указывается Helm-чарт по ссылке на другой репозиторий с чартами (4), которые параметризуется через values. Сами сервисы хранятся в отдельных репозиториях (5), а обновление Argo, например, версии образа, происходит через CI\CD-пайплайн в репозитории сервиса.
Если приложению необходима база в Managed Postgres, то шаблон манифеста для создания базы и пользователя через Crossplane лежит в чарте приложения, а описание самого кластера Managed Postgres — в отдельном чарте и относится к infra-репозиторию.
В Argo есть так называемое App of Apps, которое следит за вложенными приложениям в деплойных репах (1, 2). Если указать папку для просмотра, вложенные приложения добавятся к этому приложению и будут поддерживать их состояние.
По сути реализованный вариант похож на Terraform\Terragrunt. Отличием является то, что состояние инфраструктуры обновляется и мониторится постоянно. Кроме того, инфраструктура, задеплоенная таким способом, расширяется достаточно просто: для этого необходимо добавить Argo приложений в деплойный репозиторий (1, 2) с описанием, что и куда деплоить.
Pull Request Generator
Окружения с помощью GitOps оператора ArgoCD можно также создавать динамически при создании Pull/Merge-реквестов. Например, с помощью GitLab и ArgoCD Pull Request генераторов. Генераторы Pull Request работают не только с GitLab, но и другими сервисами, GitHub, Gitea и Bitbucket.
На картинке выше изображён объект ArgoCD ApplicationSet с заданным шаблоном приложения. При создании Merge Request в наблюдаемом репозитории, создаётся также и review-окружение в Kubernetes. Остановить и удалить его можно непосредственно в пайплайне Merge Request Gitlab.
Во многом такой подход обладает теми же преимуществами, что и подход с использованием Terraform, при этом решает некоторые ограничения. Кроме унификации окружений Prod и Dev, возможности использования сервисов IaaS и PaaS и возможности управления инфраструктурой, плюсом данного подхода являются простота кастомизации и расширения. Все сущности описываются с помощью манифестов YAML, и добавление или изменение приложения заключается в добавлении или изменении манифеста в нужном репозиторий.
Ограничения данного подхода:
Относительно новая технология
Несмотря на то, что CrossPlane уже далеко не сырой продукт, по сравнению c Terraform он достаточно молод. Если вы столкнётесь с какой-либо ошибкой, то не факт, что кто-то сталкивался с ней до вас, поэтому процесс дебага может затянуться. Большинство статей по развёртыванию инфраструктуры с помощью CrossPlane в основном описывают базовый простой сценарий, а по Terraform есть множество материалов, где рассматриваются сложные большие системы с большим количеством особенностей, в которых вы можете узнать свой пример и потратить меньше времени на самостоятельный разбор.Отсутствие провайдеров для некоторых облаков
Далеко не под все облака есть провайдеры. И часто бывает, что провайдеры для некоторых облаков не развиваются и не обновляются. Также нередки ситуации, когда для провайдера отсутствует документация, и вам придется погружаться в CustomReosurceDefenition и смотреть, какие поля можно настроить или задать.
Сравнение подходов
Таким образом можно подвести следующие итоги:
Kubernetes Namespace |
Terraform Terragrunt |
Crossplane Argo CD |
|
Простота реализации |
Просто |
Средне |
Средне |
Кастомизация разработчиками |
Легко |
Сложно |
Легко |
Простота поддержки |
Легко |
Средне |
Сложно |
Расширяемость |
Легко |
Средне |
Легко |
Управление инфраструктурой |
Нет |
Да |
Да |
Легче и быстрее всего создавать окружения в изолированных namespaces в Kubernetes.
Если вы захотите делегировать часть кастомизации разработчикам, сложнее всего будет использовать Terraform, поскольку язык HCL не часто встречается в разработке.
Проще всего поддерживать и исправлять ошибки в namespaces в Kubernetes — с очень высокой вероятностью такую проблему уже решали до вас.
Расширить подход на большое количество приложений сложнее с помощью Terraform из-за ограничения последовательного выполнения.
С помощью Kubernetes Namespaces не получится управлять инфраструктурой целиком — для этого нужен дополнительный инструмент.
Выбор подхода для создания автоматизированных окружений зависит от множества факторов: размера инфраструктуры, количества команд разработки и бюджета на инфраструктуру. Но общие рекомендации по выбору подходящего сценария такие:
Kubernetes Namespace |
Terraform/Terragrunt |
Crossplane/Argo CD |
Cloud Native / Multicloud |
Enterprise |
Фанаты K8s, хейтеры HCL |
Динамические окружения в Kubernetes больше подходит для тех команд, которые любят подход Cloud Native и плотно работают с Kubernetes. Если у вас Saas-решение, то для каждого отдельного клиента можете создать отдельный namespaces.
Статические окружения Terraform/Terragrunt являются вариантом для более зрелых и крупных Enterprise-команд, которым важно прогнозировать расход ресурсов.
GitOps-подход CrossPlane/Argo CD оптимален для любителей Kubernetes и YAML и тех, кто не любит HCL.
Полезные ссылки:
Для дальнейшего погружения в тему посмотрите запись вебинара Hilbert Team и Yandex Cloud «Автоматизация создания окружений для R&D-команд»,
Подпишитесь на канал Hilbert Team в Telegram, чтобы не пропустить выход новых материалов о безопасности, DevOps, FinOps и Data Tech.
Если вам нужна помощь в создании унифицированных окружений для R&D-команд, воспользуйтесь бесплатной консультацией от Hilbert Team, где мы с коллегами поможем составить роадмап по достижению ваших целей.