ArgoCD vs Flux: миграция с одного на другой и где грабли
В моём опыте кластеры с GitOps-инструментами делятся на два типа: те, что начали с ArgoCD, и те, что начали с Flux. И раз в пару лет в команду приходит человек с предыдущим опытом и предлагает мигрировать в другую сторону. Дважды я через это проходил, в обе стороны. Расскажу, чем они реально отличаются в 2026, как мигрировать без даунтайма и где обычно болит.
Версии — ArgoCD 2.13, Flux v2 (2.4). k8s 1.30.
Что общего
Оба — pull-based GitOps. Декларация в Git → контроллер в кластере следит за репо → применяет изменения. Оба умеют Helm, Kustomize, plain YAML. Оба ставят CRD и ходят к API-серверу.
Базовая модель identical. Различия — в архитектуре, UI и том, как они делают «синхронизацию».
ArgoCD: централизованный, с UI
Архитектура
Один контроллер на кластер, который мониторит N репозиториев. Все Application-объекты — в namespace argocd (или другом, но обычно одном). Есть отдельный Web UI с авторизацией через OIDC, RBAC, поиском по приложениям.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/manifests
targetRevision: main
path: apps/api/prod
destination:
server: https://kubernetes.default.svc
namespace: prod
syncPolicy:
automated:
prune: true
selfHeal: trueЧто хорошо
- UI. Реально полезный. Видно дерево ресурсов, diff между Git и кластером, sync history, логи синков. Нормальные люди (не только DevOps) могут смотреть статус деплоя.
- ApplicationSet — генератор приложений из шаблонов. Удобно, когда у тебя 50 микросервисов с одинаковой структурой.
- Sync waves — порядок применения ресурсов. Сначала CRDs, потом RBAC, потом Deployments. Через annotations.
- Multi-cluster из коробки. Один ArgoCD рулит десятком кластеров, через его API регистрируешь новые.
Что плохо
- Монолит. Application-controller и repo-server — heavy-комоненты, на больших инсталляциях (1000+ Applications) приходится тюнить шарды и memory limits.
- Один namespace для всех Applications. С RBAC можно разнести, но это дополнительный конфиг.
- UI может пропадать (perception bug), пока всё на самом деле синхронизируется. Народ начинает паниковать раньше времени.
Flux: модульный, без UI
Архитектура
Не один контроллер, а несколько: source-controller (тянет Git), kustomize-controller, helm-controller, notification-controller. Каждый занимается своим.
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: manifests
namespace: flux-system
spec:
url: https://github.com/myorg/manifests
ref:
branch: main
interval: 1m
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: api
namespace: flux-system
spec:
interval: 5m
path: ./apps/api/prod
prune: true
sourceRef:
kind: GitRepository
name: manifests
targetNamespace: prodЧто хорошо
- Cloud-native архитектура. Каждый контроллер scaling-ится отдельно, restart одного не валит остальные.
- CRD-first. Никакого собственного API за CRD-объектами, всё через kubectl.
- Multi-tenancy через namespace-isolation работает естественно: команда A держит свои Kustomization в своём namespace, контроллер за это пускает с её ServiceAccount.
- Notifications через свой controller — Slack, Teams, GitHub status. ArgoCD это тоже умеет, но у Flux нативнее.
Что плохо
- Нет UI. Есть Weave GitOps (бывший wego) и сторонние проекты, но это не родное.
flux get all -Aв терминале — основной способ смотреть статус. - Нет дерева ресурсов «что получилось из Application». Нужно
kubectl getпо namespace и руками сопоставлять. - Sync waves — менее зрелые, чем в ArgoCD. Через
dependsOnмежду Kustomization, но менее гранулярно. - ImageUpdateAutomation — отдельный controller, и он делает PR в Git автоматически. Удобно, но настройка не тривиальна.
Когда что выбирать в новом кластере
Беру ArgoCD, если:
- Команда не только DevOps. Девелоперам и QA нужно смотреть статус деплоя, и UI критичен.
- Multi-cluster из одной точки управления.
- Нужен ApplicationSet для генерации.
- Команда более классическая ops-ориентированная, привыкла к admin-консолям.
Беру Flux, если:
- Cloud-native команда, всё через kubectl и YAML.
- Многотенантность важна с самого начала.
- Не хочется поддерживать ещё один stateful-сервис с UI.
- Делаем «всё через Git» строго — даже статус деплоя смотрят через Git PRs/issues, а не дашборд.
Миграция с одного на другой
Самая болезненная часть. Делал в обе стороны. Главное — миграция без даунтайма приложений. Сами приложения не должны заметить.
Шаг 1: Inventory
Собираешь список всех управляемых объектов. В ArgoCD:
argocd app list -o name > apps.txt
argocd app get <app> -o yaml > backup/<app>.yamlВ Flux:
flux get kustomizations -A > ks.txt
kubectl get kustomizations -A -o yaml > backup/all-ks.yamlЭто база, на которую ты потом сверяешься.
Шаг 2: Поднимаешь target-инструмент в read-only
Ставишь второй инструмент рядом, но без auto-sync. Например, мигрируем с Flux на ArgoCD: ставишь ArgoCD, создаёшь Applications с syncPolicy: manual. Они появятся в UI как OutOfSync, но ничего не делают.
spec:
syncPolicy:
syncOptions:
- ApplyOutOfSyncOnly=true
- PruneLast=true
# без automated блока — ручной syncВ этот момент к одним и тем же ресурсам в кластере имеют отношение оба контроллера, но реально применяет только Flux.
Шаг 3: По одному приложению переключаешь
Берёшь некритичное приложение (тестовый сервис, dev-окружение). В Flux:
flux suspend kustomization test-app -n flux-systemВ ArgoCD:
argocd app sync test-app
argocd app set test-app --auto-prune --self-healПроверяешь, что pod-ы на месте, ничего не дрогнуло. Если всё ок — переходишь к следующему.
Потом удаляешь Flux-объект, чтобы он его не «возрождал»:
kubectl delete kustomization test-app -n flux-systemТут есть нюанс: Flux может triggernuть prune при удалении Kustomization. Чтобы этого не было, перед удалением:
spec:
prune: false # сначала меняешь этоПрименяешь, потом удаляешь Kustomization. Тогда удаление CR не дёрнет prune ресурсов.
Шаг 4: Переключаешь критичные сервисы
Когда тренировался на тестах — переходишь к prod. Я всегда делаю это в часы низкой нагрузки и с включённым алёртингом.
Самое страшное — двойное управление: Flux откатывает то, что ArgoCD только что применил. Гарантия избежать — пошагово (suspend в одном → sync в другом → delete в первом), не пытаться двигать «батчем».
Шаг 5: Cleanup
После последнего приложения сносишь старый контроллер целиком:
flux uninstall --namespace=flux-systemБэкап CRDs в стороне на случай отката. После недели работы можно удалять.
Грабли, на которые я наступал
- Helm-релизы с разными именами. Flux HelmRelease создаёт Helm release с именем CR, ArgoCD — с именем Application. При переходе Helm release в кластере именуется иначе, и команды
helm listпоказывают «два релиза». Лечится тем, что не миксуй: либо все через Helm-операторы (тогда совместимо), либо все через template-and-apply. - Sealed Secrets. Если у тебя их провизионирует Flux, и SealedSecret-controller сам, и ArgoCD начинает «видеть» их как drift — будут вопросы. Лечится
argocd.argoproj.io/compare-options: IgnoreExtraneousи игнор-листами. - Webhooks. Если в Git был настроен webhook на Flux notification — забыл его перенаправить, потерял время на «почему мои PR-чеки не работают».
Что запомнить
ArgoCD — для команд, которым нужен UI и multi-cluster из одного места. Flux — для cloud-native подхода с CRD-first и multi-tenancy. Миграция возможна без даунтайма, но требует пошаговости: target в read-only, по одному приложению переключаешь, потом cleanup.
Куда копать: документация ArgoCD и Flux. И обязательно посмотри ApplicationSet (ArgoCD) и Flux ImageUpdateAutomation — это те фичи, ради которых иногда и переезжают.