Привет всем. Меня зовут Алексей, я DevOps-инженер, и сегодня я хочу рассказать немного об одном инфраструктурном решении моего ключевого заказчика.

Немного о моей работе и разделении ответственности. Я предоставляю услуги по настройке и сопровождению облачной инфраструктуры на основе GCP, а также мониторинг, алертинг, логи и т.д. В Altenar я являюсь частью команды автоматизации, которая также занимается релизными пайплайнами, улучшением различных процессов в компании и другими важными вещами. CI, сборки, сеть, доступы - за это отвечают другие команды, и этих частей я касаюсь очень мало либо совсем не трогаю их.

В моей команде издревна использовался flux v1 для кластеров на VM и для GKE в формате "один кластер - один репозиторий", с подключенными "шаблонами" в виде Git submodules. У такого подхода есть свои минусы, один из самых существенных - первая версия flux уже не поддерживается с конца 2020 года.

Таким образом, у нас был некий техдолг в виде первого flux, и необходимость рано или поздно обновляться до версии 2 или переходить на другие инструменты. Был выбран первый вариант, заодно я решил кое-что улучшить в нашей текущей структуре, убрав кучу различных репозиториев и отказавшись от "шаблонов-сабмодулей".

Особенность переезда - часть кластеров я мог пересоздавать без проблем сразу с новой инсталляцией flux v2, например, мой сэндбокс и дев кластер, а часть - в продакшене, для которых надо было продумать вопросы DR если что-то пойдет не так и создать мануал для переезда с минимальным downtime.

Как я построил новую монорепу и почему.

Старая схема.

repo-1
└── flux-v1-cluster
    ├── submodule-base
    │   └── charts
    └── submodule-gke
        └── charts

repo-2
└── submodule-base
    └── charts

repo-3
└── submodule-gke
    └── charts

Допустим, мне надо посмотреть, что там у меня за чарт катится в кластер. Я вижу в кластере, что это часть flux, иду в репо flux-v1-cluster, пытаюсь там найти искомое и не нахожу. Ищу дальше, вспоминаю, что тут есть сабмодули, лезу в Submodule-base, ищу там, а там нет... иду в соседнее репо Submodule-gke и ага! Оно тут! ????

Причем в следующий раз, когда ты вспоминаешь, что какой-то чарт ты находил в Submodule-gke, ты идешь сразу туда, а там его нет... потому что он в другой репе Submodule-base... В общем, если вы когда-нибудь оперировали связанными репозиториями раз в месяц-два, вы уже поняли в чем проблема.

Сюда же добавляется проблема с версионированием сабмодулей. В какой-то момент я долго не мог понять, что не так, т.к. в один кластер лился чарт, которого не было ни в одном сабмодуле. Как оказалось, этот чарт был добавлен в какую-то кастомную ветку сабмодуля, и догадаться об этом было бы довольно сложно, если не вчитываться в версию используемого сабмодуля.

Как сказала нейронка про эту структуру:

Такая организация кода имеет несколько недостатков:

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

2. Увеличение сложности развертывания: такая организация кода может привести к усложнению процесса развертывания, поскольку необходимо координировать обновления в различных репозиториях и сабмодулях.

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

4. Увеличение времени разработки: в случае, если несколько команд работают над различными репозиториями и сабмодулями, это может привести к увеличению времени разработки и снижению производительности команды в целом.

5. Затруднение в масштабировании: в случае, если требуется добавить новый компонент или изменить имеющийся, это может привести к затруднениям в масштабировании системы, поскольку необходимо будет координировать изменения в нескольких репозиториях и сабмодулях.

Новая схема.

Несмотря на то, что flux v2 также поддерживает "сабмодули" в виде отдельных репозиториев, я реализовал монорепозиторий по одному из примеров в документации:

https://fluxcd.io/flux/guides/repository-structure

.
├── apps
│   ├── common
│   │   └── monitoring
│   │       ├── prometheus
│   │       └── prometheus-rules
│   ├── kafka
│   ├── rabbitmq
│   └── redis
├── clusters
│   ├── cluster-1
│   │   ├── custom-resources
│   │   └── sync-code
│   ├── cluster-2
│   │   ├── custom-resources
│   │   └── sync-code
│   ├── gke-template
│   └── self-hosted-template
└── infrastructure
    ├── controllers
    │   └── nginx-ingress
    └── lb
        └── metallb

Преимущества:

  1. Все нужные "модули" и приложения у меня на виду, в одном репозитории.

  2. Легко добавлять новые приложения как в кластер, так и в обобщенные "шаблоны" кластеров.

  3. При использовании кастомной ветки для кластера - все приложения также под рукой и их версии не "уедут" случайно из-за работы коллег с мастер веткой.

  4. При создании ПРов в мастер монорепы в отличие от ПРов в сабмодуль - ты точно уверен, что это влияет на все ресурсы и поэтому ты будешь более внимателен ????

Недостатки:

  1. Если вы плохо продумали структуру кластера и решили ощутимо поменять ее на ходу, когда несколько кластеров уже используют монорепу - будет довольно больно.

  2. Про ПРы и их влияние на всю инфраструктуру сразу справедливо и к минусам отнести.

У нейронки своё мнение на этот счет, конечно же, попросил ее сравнить, что лучше:

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

Однорепозиторная структура имеет следующие преимущества:

1. Упрощение управления зависимостями: такая структура позволяет упростить управление зависимостями между компонентами, поскольку все они находятся в одном репозитории.

2. Упрощение процесса развертывания: упрощение управления зависимостями также приводит к упрощению процесса развертывания, поскольку обновления можно координировать в рамках одного репозитория.

3. Улучшение гибкости: в случае необходимости изменения компонента, изменения можно вносить в рамках одного репозитория, что улучшает гибкость и уменьшает время на изменение системы.

4. Улучшение производительности команды: однорепозиторная структура может улучшить производительность команды, поскольку упрощает координацию работы над различными компонентами.

Однако, у однорепозиторной структуры также есть некоторые недостатки:

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

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

3. Увеличение времени разработки: увеличение размера репозитория может привести к увеличению времени разработки и снижению производительности команды в целом.

Итак, обе структуры имеют свои преимущества и недостатки, и выбор между ними зависит от конкретных требований проекта и команды разработчиков.

Немного про упаковку приложений

Даже прочитав несколько раз документацию, я долгое время не мог понять, что именно из себя представляет "упакованное приложение для flux v2".

В итоге, самая простая и понятная схема для описания этого у меня получилась такая:

По сути, чтобы упаковать приложение для монорепы, ты собираешь набор ресурсов, которые нужны приложению, например, неймспейс, полиси и прочие роу манифесты. Если у приложения есть хелм чарт - добавляешь хельм релиз. И все это ты просто кладешь в одну директорию и добавляешь kustomization, в котором перечисляешь используемые файлы.

Best practice from my experience:

Если приложение имеет какие-либо конфигурируемые параметры (например, классические values для helm чарта) - подключайте их сразу как configmap и делайте его использование обязательным. Если потребуется установка с параметрами по умолчанию - просто добавьте пустой configmap.

Если вы хотите добавить в кластер какие-то компоненты, требующие CRD - подумайте о том, как вы их будете упаковывать в отдельное “приложение” в flux v2.

Пример - kafka-alerts в моём репозитории.

Домашнее задание - попробуйте добавить любые прометей правила сразу в кластер и вы сразу поймете, зачем я разделил код кластера на 2 директории: custom-resources и sync-code.

Пример упаковки прометея можно посмотреть в репозитории с примером и даже раскатить себе, подробнее в конце статьи.

Порядок миграции flux v1 -> flux v2

Тут всё более-менее просто, но есть ряд нюансов.

Мы используем практику подготовки плана ченжа “как для пятилетнего ребенка”, чтобы любой из доступных инженеров мог пройти по пунктам чеклиста и ничего критичного не забыть (хотя, ситуации бывают разные).

Если у вас сильно устаревший прометей, как был у меня - он не зареконсилится, т.к. у него не совпадут CRD с новой версией. Необходимо удалить все старые ресурсы и definitions прометея ручками перед выкаткой нового.

В общих чертах:

  1. Скейлим деплоймент flux v1 в 0

  2. Бутстрапим flux v2 в кластер (есть несколько вариантов, как вы будете это делать, в моем репозитории описан самый простой)

  3. Применяем код нового кластера

  4. Меняем\удаляем старые аннотации руками у компонентов, которые управлялись flux v1 (но я предпочитаю удалить старую инсталляцию и установить приложение заново, т.к. я могу себе это позволить в окно обслуживания)

В целом, все должно пройти без проблем. Ну а в случае, если вы делаете это впервые - рекомендую экспериментировать на демо-кластере.

Пример кода для создания сэндбокса в gke через terraform тут: https://github.com/ksemele/tf-gke-test
Пример кода монорепы flux v2 для этого кластера тут: https://github.com/ksemele/fluxv2-test 

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