Перейти к содержимому
Hogin Hogin
Назад

Flux за вечер: GitOps для одного небольшого кластера

8 мин чтения

GitOps в 2026 — это уже не «продвинутая практика», а базовый способ эксплуатации Kubernetes. Но для маленькой команды он часто выглядит пугающе: кажется, что нужно поднять зоопарк операторов и месяц это настраивать. На деле минимально жизнеспособный GitOps на Flux ставится за вечер. Разберёмся, чем он лучше привычного «kubectl apply из CI» и как собрать конфигурацию, где git — единственный источник истины.

Содержание

Открыть содержание

Что не так с «kubectl apply из CI»

Самый частый способ катить в Kubernetes: CI собирает образ и в конце делает kubectl apply. Работает, но у этого подхода есть встроенные проблемы.

Во-первых, CI имеет ключи от прода. Раннер с правами kubectl apply на кластер — это лакомая мишень: скомпрометировали пайплайн, скомпрометировали кластер. Во-вторых, нет источника истины. Кто-то сделал kubectl edit руками в три часа ночи — и состояние кластера разъехалось с тем, что в репозитории, а вы об этом не знаете. В-третьих, apply — это «выстрелил и забыл»: он применяет манифест один раз и не следит, что будет дальше.

GitOps переворачивает модель: не CI пушит в кластер, а кластер сам тянет желаемое состояние из git и непрерывно приводит себя к нему.

GitOps-цикл: git как источник истины, reconciler приводит кластер к нему

Что такое Flux

Flux — это GitOps-инструмент из экосистемы CNCF (graduated-проект). Важная деталь: это не один большой агент, а набор маленьких контроллеров, каждый из которых делает одну вещь:

Flux — набор маленьких контроллеров вместо монолита

Такое разделение — не академизм. Маленькие контроллеры проще понять, проще чинить и проще масштабировать по отдельности. Вам не обязательно знать их все: для старта хватает source- и kustomize-контроллеров.

Reconcile, а не apply

Ключевое отличие — модель reconcile. kustomize-controller не «применил и ушёл», он крутится в цикле: взять желаемое состояние из git → сравнить с фактическим в кластере → устранить разницу. Это даёт две вещи, которых нет у kubectl apply.

Drift detection. Изменили что-то руками? На следующем цикле reconcile Flux заметит расхождение и вернёт состояние к тому, что записано в git. Кластер физически не может надолго разойтись с репозиторием.

Pruning. Удалили манифест из git? С prune: true Flux удалит соответствующий ресурс и из кластера. Не будет «ресурсов-призраков», которые когда-то заапплаили и забыли. Git становится действительно полной картиной того, что есть в кластере.

Есть и третий, неочевидный плюс — безопасность pull-модели. В схеме «CI делает apply» наружу торчит доступ к кластеру: у раннера есть kubeconfig с правами на прод, и этот доступ нужно где-то хранить и защищать. В GitOps всё наоборот: контроллер живёт внутри кластера и сам ходит за изменениями в git. Кластеру не нужны входящие соединения и внешние ключи к нему — он только читает репозиторий. Поверхность атаки сжимается: компрометация CI больше не означает автоматически компрометацию прода, потому что CI вообще не имеет прямого доступа к кластеру. Его задача — собрать образ и обновить манифест в git, а выкатка — забота Flux.

Сравнение в одной таблице

kubectl apply из CIGitOps (Flux)
Кто меняет кластерCI пушиткластер тянет сам
Ключи от продау раннера CIвнутри кластера
Источник истиныразмываетсяgit, всегда
Drift рукаминезаметеноткатывается reconcile
Удаление ресурсоввручнуюprune: true
Откатпере-деплойgit revert

Что нужно, чтобы поднять Flux

Понадобятся кластер, flux CLI и git-репозиторий (возьмём GitHub) с токеном. Самый приятный способ старта — flux bootstrap: команда сама ставит контроллеры в кластер и коммитит свои же манифесты в git, так что и сам Flux дальше управляется через GitOps.

export GITHUB_TOKEN=<personal-access-token>

flux bootstrap github \
  --owner=acme \
  --repository=infra \
  --branch=main \
  --path=clusters/prod \
  --personal

После этого в репозитории acme/infra появится папка clusters/prod с манифестами Flux. Теперь опишем источник и то, что из него синкать. Один GitRepository (откуда брать) и один Kustomization (что применять):

# clusters/prod/apps.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: infra
  namespace: flux-system
spec:
  interval: 1m                 # как часто проверять git
  url: https://github.com/acme/infra
  ref:
    branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: apps
  namespace: flux-system
spec:
  interval: 10m                # как часто сверять кластер с git
  sourceRef:
    kind: GitRepository
    name: infra
  path: ./apps                 # синкать всё из папки apps/
  prune: true                  # удалять из кластера то, что удалили из git

Это и есть минимально жизнеспособный GitOps: всё, что вы положите в папку apps/ репозитория, Flux применит и будет держать в актуальном состоянии. Хотите добавить сервис — кладёте его манифест в apps/ и делаете git push. Больше ничего.

Порядок и зависимости

Когда ресурсов становится больше, появляется вопрос порядка: например, CRD должны приехать раньше, чем объекты, которые их используют, а база — раньше приложения. В Argo CD для этого были sync waves; во Flux порядок задаётся через dependsOn между Kustomization-ами:

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: apps
spec:
  dependsOn:
    - name: infra-crds         # apps поедут только после infra-crds
  # ...

Секреты

Главный «а как же секреты» в GitOps решается просто: секреты тоже лежат в git, но зашифрованными. Flux нативно поддерживает SOPS — вы шифруете значения ключом age (или из облачного KMS), коммитите зашифрованный файл, а kustomize-controller расшифровывает его в кластере при применении. Приватный ключ лежит только в кластере, в репозитории — ничего читаемого.

Как масштабировать один root

Пока сервисов мало, хватает одного Kustomization на папку apps/. Когда их становится много, удобен паттерн «root, который порождает остальные»: один корневой Kustomization синкает папку, где лежат описания других Kustomization-ов, а те уже указывают на конкретные приложения. По смыслу это то же, что App-of-Apps в Argo CD, только средствами самого Flux.

Типичная раскладка репозитория выглядит так:

infra/
├── clusters/
│   └── prod/          # сюда bootstrap положил Flux + root-Kustomization
├── infrastructure/    # ingress, cert-manager, мониторинг
│   └── ...
└── apps/              # ваши сервисы
    ├── web/
    └── api/

Корневой Kustomization указывает на infrastructure/ и apps/, между ними — dependsOn, чтобы платформенные вещи приезжали раньше приложений. Добавление нового окружения или кластера сводится к новой папке и новому Kustomization, а не к переписыванию пайплайнов. Вся топология остаётся в git и читается как дерево каталогов.

Автообновление образов

Отдельная приятная вещь: Flux умеет сам обновлять теги образов в git. Контроллеры image-reflector и image-automation сканируют реестр, находят новый подходящий тег по заданной политике (например, semver-диапазон), коммитят изменение манифеста обратно в репозиторий — и дальше срабатывает обычный reconcile. Получается полностью замкнутый цикл: собрали и запушили образ → Flux заметил новый тег → обновил манифест в git → выкатил в кластер. При этом история деплоев остаётся в git как обычные коммиты, а откат — это git revert. Для маленькой команды это убирает последний ручной шаг между сборкой и продом.

Как проверить, что всё работает

Сначала смотрим, что Flux подхватил источник и синканул:

flux get kustomizations
# NAME   READY   MESSAGE
# apps   True    Applied revision: main@sha1:...

READY=True и свежая ревизия означают, что кластер приведён к состоянию из git. Теперь — показательный тест на drift. Поменяем что-нибудь руками и подождём цикл reconcile:

kubectl scale deploy/web --replicas=5      # «ручное» вмешательство
# ждём interval reconcile...
kubectl get deploy/web                      # реплик снова столько, сколько в git

Flux вернёт количество реплик к тому, что записано в репозитории — это и есть доказательство, что git действительно стал источником истины. Принудительно запустить сверку, не дожидаясь интервала, можно через flux reconcile kustomization apps.

Ловушки

Итог

GitOps — это всего две вещи: git как единственный источник истины и reconciler, который непрерывно приводит к нему кластер. Flux даёт это набором маленьких контроллеров, которые ставятся одной командой flux bootstrap и дальше управляют собой сами. «Слишком сложно» — миф из мира больших энтерпрайзов: для одной команды и одного кластера рабочая конфигурация — это GitRepository плюс Kustomization, собранные за вечер. А когда дорастёте до прогрессивных выкаток, поверх той же модели ляжет Flagger — но это уже тема отдельной статьи.


Поделиться:

Следующая статья
OpenTelemetry Collector: минимальный setup, который не стыдно в прод