ADG Оставить заявку
Блог DevOps 6 мин чтения

GitOps на практике: Flux, pull request вместо kubectl и синхронизация кластера

Weaveworks назвал это GitOps: инфраструктурные изменения только через Git, Flux отслеживает расхождения и синхронизирует кластер автоматически. Пробуем на деле.

Контекст момента

Weaveworks публикует концепцию GitOps - управление кластером через Git как единственный источник истины

На прошлой неделе Weaveworks опубликовал статью, в которой Алекс Полви назвал свой подход к CD в Kubernetes словом «GitOps». Концепция не новая - идея держать желаемое состояние инфраструктуры в Git и автоматически приводить реальное к желаемому витала в воздухе давно. Но статья расставила точки над i и дала имя тому, что многие делали интуитивно и вразнобой. Мы в этот же день полезли разбираться, потому что проблема, которую это решает, у нас совершенно реальная.

Чем нас достал привычный подход

На managed-инфраструктуре у нас несколько клиентских Kubernetes-кластеров. Деплои делаются по-разному: кто-то через CI/CD-пайплайн с kubectl apply, кто-то через Helm из Jenkins, у одного клиента вообще есть скрипт на bash, который инженер запускает руками. Итог предсказуемый: реальное состояние кластера и то, что лежит в репозитории, расходятся. Классика - кто-то сделал kubectl edit deployment прямо в кластере чтобы быстро поправить лимиты, в репо не отразил, и теперь никто не знает почему в git одно, а в кластере другое.

Это называется drift - кластер уехал от описания. Обнаруживается обычно в самый неудобный момент.

Что такое GitOps в понимании Weaveworks

Идея простая до красоты. Git-репозиторий - это единственный источник истины для состояния кластера. Любое изменение - только через pull request. Никакого kubectl apply руками, никаких правок через дашборд. Если хочешь изменить количество реплик - открываешь PR, получаешь ревью, мержишь. Дальше автоматика сама разберётся.

Автоматику делает Flux - инструмент от Weaveworks, который живёт внутри кластера и работает в режиме pull. Он периодически смотрит в git-репозиторий, сравнивает что там написано с тем что реально крутится, и если видит расхождение - синхронизирует кластер с репозиторием. Не человек пушит изменения в кластер, а кластер сам вытягивает из Git.

Разница принципиальная. При push-модели CI/CD нужен доступ к kube API снаружи, нужны credentials в CI-системе. При pull-модели кластер сам инициирует обращение к Git - снаружи ему ничего не нужно открывать.

Как выглядит это на практике

Мы взяли один из тестовых кластеров и попробовали поднять Flux. Установка несложная - Flux разворачивается как Deployment в кластере, ему нужен SSH-ключ для доступа к git-репозиторию (публичная часть добавляется как deploy key на GitHub) и адрес репо.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: flux
  namespace: flux
spec:
  replicas: 1
  selector:
    matchLabels:
      name: flux
  template:
    metadata:
      labels:
        name: flux
    spec:
      containers:
      - name: flux
        image: weaveworks/flux:1.1.0
        args:
        - --git-url=git@github.com:adg/infra-cluster-prod.git
        - --git-branch=master
        - --git-path=namespaces,workloads
        - --sync-interval=1m

Flux смотрит в папки namespaces и workloads в репозитории, раз в минуту сверяет с кластером. Если в репо поменялся образ - Flux обновит Deployment. Если добавился новый Namespace - создаст его.

Есть и обратная функция: Flux умеет следить за container registry и когда в реестре появляется новый образ с нужным тегом - сам делает коммит в git, обновляя тег в манифесте. Это удобно для автообновления, хотя и немного ломает голову сначала: git-репозиторий пишется автоматически, а не только людьми. Мы пока эту фичу не включали - хотим сначала разобраться с базовой механикой.

Что реально изменилось в процессе

Первый эффект заметили сразу: kubectl apply руками перестал работать так как раньше. В смысле - технически он работает, но Flux через минуту откатит изменение обратно к тому, что в git. Поначалу это сбивает с толку - правишь Deployment, смотришь что применилось, отворачиваешься, поворачиваешься - а там опять старое. Нужно перестраивать рефлекс: сначала PR, потом кластер.

Второй эффект - ревью стало осмысленным. Раньше инженер деплоил из CI, в PR лежал только код приложения, а инфраструктурные изменения шли отдельными скриптами без ревью. Сейчас манифесты живут в том же потоке, что и код - PR на обновление ресурсных лимитов выглядит как обычный diff:

-          memory: "512Mi"
+          memory: "1Gi"

Любой из команды видит это, может задать вопрос, может попросить обоснование.

Третий эффект - drift обнаруживается автоматически. Если кто-то всё-таки поправил что-то в кластере минуя git, Flux это видит при следующей синхронизации и откатывает. Можно смотреть на флаг --dry-run в логах Flux чтобы понимать что именно он хочет синхронизировать.

Где трётся

Честно - не всё гладко. Несколько реальных проблем которые мы уже нашли:

  • Секреты. Манифесты с Secret нельзя просто так класть в репозиторий - там реальные данные в base64, который не шифрование. Нужно или шифровать секреты перед коммитом (есть подходы вроде sealed-secrets, но это отдельная история), или хранить их в Vault и инжектировать. Пока держим секреты вне основного потока, что ломает идею «всё в git».

  • Порядок применения. Flux применяет манифесты в алфавитном порядке файлов. Если Namespace должен создаться раньше Deployment - нужно называть файлы соответственно или класть в отдельные папки. Это решаемо, но требует дисциплины.

  • Helm-чарты. Flux умеет работать с Helm через FluxHelmRelease CRD, но это пока ощущается как надстройка поверх надстройки. Мы используем Helm для части компонентов, и интеграция с Flux там требует отдельного компонента - Helm Operator. Не попробовали ещё вживую, пока держим Helm-деплои вне Flux.

  • Дебаггинг расхождений. Когда Flux говорит что синхронизировал - не всегда очевидно что именно и почему. Логи информативные, но нужно привыкнуть читать их.

Итог пока

Неделю работаем с Flux на тестовом кластере. Концепция рабочая и ощущается правильно: git как source of truth, автоматическая синхронизация, PR-процесс для любых изменений. Это убирает целый класс проблем - дрейф конфигурации, неясность кто и что деплоил, ручные правки без аудита.

Но это не «включил и забыл». Нужно выстроить процесс работы с секретами, договориться о структуре репозитория, обучить команду что kubectl apply больше не главный инструмент деплоя. Организационный overhead реальный.

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

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.