Эксперт OTUS - Владимир Дроздецкий приглашает всех желающих на бесплатный вебинар, в рамках которого он подробно расскажет о программе курса "DevOps практики и инструменты" и ответит на интересующие вопросы. А прямо сейчас, по устоявшейся традиции, делимся с вами интересным переводом. Недавно я общался с разработчиками из Humanitec (это Continuous Delivery-платформа для Kubernetes). Humanitec интересен тем, что вопреки современным тенденциям, он не основан на GitOps. Лично я большой фанат GitOps, потому что он позволяет строить CI/CD без сложных инструментов с использованием только Git и декларативного описания конфигураций. Но несмотря на то, что я недавно написал статью "11 Reasons for Adopting GitOps" (11 причин для внедрения GitOps), в своей практике я неоднократно сталкиваюсь с ограничениями этого подхода. Беседа с ребятами из Humanitec побудила меня написать об этом негативном опыте для того, чтобы предоставить вам более объективную картину GitOps и поговорить об альтернативных подходах. Приложения в процессе непрерывной поставки часто обновляются, и поставку этих обновлений необходимо автоматизировать. При успешном завершении процесса непрерывной интеграции CI-процесс должен создать Pull Request на выкатку релиза в тестовую среду. Однако редактирование и разрешение конфликтов в Git осуществляется вручную. И легко может возникнуть ситуация, когда несколько CI-процессов завершаются записью в один и то же GitOps-репозиторий, что приводит к конфликту. Конфликт возникает не в отдельных файлах, а между двумя процессами, клонировавшими репозиторий, когда один из них делает push раньше другого. Если после этого другой процесс попытается сделать push, то его локальная копия будет устаревшей, поэтому ему придется сделать pull, а потом повторный push. На этом этапе, если система достаточно нагружена, могут возникнуть конфликты с еще каким-нибудь процессом. Причины этого всего кроятся непосредственно в принципах работы Git. Этот эффект можно уменьшить, использовав больше репозиториев (например, по одному репозиторию на один namespace). Решение этой проблемы в одном из проектов, через добавление в Groovy-скрипты Jenkins сложного механизма повторов, нам стоило значительных затрат. В зависимости от способа мапинга ваших GitOps-репозиториев на среды развертывания приложений (см. предыдущий раздел), количество Git-репозиториев может увеличиваться с каждым новым приложением или средой. Также вам необходимо настраивать соответствующие права доступа в этих репозиториях и подключать агентов синхронизации на разных кластерах. (Агент синхронизации — это процесс или пайплайн, который следит за GitOps-репозиторием и синхронизирует его содержимое с необходимой средой приложения.) Команда, с которой я работал, потратила в сложной корпоративной среде более 30% всего времени разработки на автоматизацию провижининга GitOps-репозиториев. Эту проблему можно смягчить, используя меньшее количество репозиториев, например, по одному репозиторию на кластер. Но это увеличит нагрузку на конкретный репозиторий с точки зрения контроля доступа и управления Pull Request'ами. И самое главное, только усугубит проблему автоматического обновления, описанную в предыдущем разделе. GitOps обещает прозрачность того, что происходит в вашем окружении, так как все целевое состояние хранится в Git в виде обычного текста. Но это работает только для относительно простых конфигураций при небольшом количестве GitOps-репозиториев, содержащих обозримое количество файлов конфигурации. В корпоративной среде количество GitOps-репозиториев и/или файлов конфигурации растет с невероятной скоростью. В результате просмотр текстовых файлов становится не таким уж и удобным способом для поиска ответов на вопросы. Например, даже на вопрос о том, как часто развертываются определенные приложения, ответить трудно, поскольку изменения в Git-репозиториях сложно сопоставить с развертываниями приложений. Некоторые изменения могут привести к развертыванию нескольких приложений, а некоторые — это всего лишь минорные изменения в конфигурации. Сложные корпоративные окружения нуждаются в решениях по управлению секретами вне обычного CI/CD-процесса. Необходимо выполнять тщательный аудит секретных параметров, таких как закрытые ключи или пароли для доступа к базам данных. Есть смысл хранить их централизованно в безопасном хранилище, таком как Hashicorp Vault. GitOps не мешает этому подходу, но и не очень помогает. Git-репозитории — не самое лучшее место для хранения секретов, поскольку вам придется их шифровать и расшифровывать и они навсегда запоминаются в истории Git. Также при большом количестве репозиториев секреты распределяются по ним, что затрудняет отслеживание мест, где необходимо обновить секрет при его изменении.  GitOps-репозитории — это отличный инструмент для аудита процессов, так как они хранят полную историю всех изменений. Поэтому легко ответить на вопрос: «Что случилось с этим окружением?». Но так как GitOps-репозитории хранят версии текстовых файлов, ответить на другие вопросы становится сложнее. Например, для ответа на вопрос: «Когда разворачивалось приложение X?», — потребуется изучение истории Git и полнотекстовый поиск в текстовых файлах, что сложно реализовать и чревато появлением ошибок. Если Git-репозиторий является интерфейсом между кластером Kubernetes и CI/CD-процессом, то нет простого способа проверить закомиченные файлы. Представьте себе, что вместо Git PR вы делаете вызов API. В этом случае осуществляется валидация запроса, а в случае с GitOps вся проверка манифеста и Helm-файлов ложится на пользователя. Все эти недостатки не означают, что GitOps — это плохое решение для релиза приложений и изменения конфигурации. Этот подход дает множество преимуществ без применения тяжелых инструментов. Просто нужно помнить о проблемах, с которыми вы столкнетесь на своем пути, и учитывать их в ваших проектах. Итак, есть ли решение, со всеми преимуществами GitOps, но без указанных недостатков? Давайте сначала выделим то, что мы хотели бы сохранить: Логирование всех изменений окружения. Описание и конфигурация окружений в декларативном виде. Процесс утверждения/согласования изменений окружений. Контроль того, кто может вносить изменения в окружения. Просмотр целевого состояния среды и сверка его с реальным состоянием. Как упоминалось ранее, проблема заключается в том, что, хотя в Git и присутствуют все эти возможности, но он не рассчитан на частые автоматические обновления. Также Git не подходит для анализа хранящихся в нём данных. Очевидным решением является API-сервис со своей базой данных, и аналогичным GitOps-процессом синхронизации на основе агента. (Если вы читаете статью на маленьком экране, то можете скачать диаграмму здесь.) В базе данных хранятся все предыдущие версии манифестов и Helm-диаграмм. Таким образом можно поддержать большое количество обновлений API и убрать неудобный процесс разрешения конфликтов с помощью Git (если только мы не столкнемся с реальными конфликтами, вероятность которых в этом сценарии очень низка). Процесс утверждения запросов на изменение реализуется с помощью вызовов API и базы данных. RBAC реализуется аналогично. Все это довольно дорого для реализации. Но здесь мы можем получить дополнительный функционал: Структурированный поиск по базе данных (Как часто развертывается приложение X?). Единая централизованная система, обслуживающая все среды: отсутствует увеличение количества git-репозиториев. Простое управление конфигурациями нескольких сред. Можно реализовать иерархию конфигураций. Централизованное управление секретами или интеграция со сторонними продуктами. Валидация входных данных. Поскольку вышеупомянутое решение гораздо дороже в реализации, чем решение на базе GitOps, придется либо создавать свою внутреннюю реализацию, либо использовать стороннюю. Самый популярный из таких инструментов — Spinnaker. В свою очередь, Humanitec — это следующее поколение, полностью ориентированное на Kubernetes. Некоторые из вышеперечисленных возможностей в нем уже реализованы, а некоторые есть в планах. Мы видим большой потенциал в подобных системах как в альтернативе GitOps. Узнать подробнее о курсе.  Минимально жизнеспособный Kubernetes  Редактор