GitOps: git как source of truth для desired state. Pull-based controller (ArgoCD/Flux) внутри кластера наблюдает за git и непрерывно reconciles фактический state с желаемым. Три сценария: PR merge → ArgoCD sync → cluster reconcile; drift detection (manual kubectl change → auto-revert); Argo Rollouts canary с analysis gates. Включает Kustomize/Helm overlays, SOPS/External Secrets/Vault, Prometheus-based progressive delivery.
В классическом CI/CD деплой выглядит так: pipeline на стороне CI-сервера получает creds кластера и делает kubectl apply. Это удобно ровно до тех пор, пока кто-то не запускает kubectl edit руками "на минутку посмотреть", пока CI-токен с cluster-admin не утекает через лог сборки, пока для отката не приходится бегать по истории Jenkins-jobs и искать "тот самый зелёный билд". Восстановить состояние кластера из git становится невозможно: git показывает только то, что кто-то пытался задеплоить, но не то, что реально крутится.
GitOps переворачивает зависимость. Желаемое состояние (desired state) живёт в git: deployments, services, configmaps, ingress, RBAC, network policies, даже namespaces. Внутри кластера сидит controller (Argo CD, Flux), который непрерывно сравнивает git с реальностью и устраняет дрейф. CI больше не имеет cluster-admin: его задача — собрать образ и обновить тег в манифесте через PR. Все изменения кластера = git-коммиты, ревьюнутые людьми. Откат = git revert. Аудит = git log. Disaster recovery = git clone + bootstrap controller'а.
Pull-based архитектура снимает два больших класса проблем: безопасность (creds никогда не покидают периметр кластера) и observability (drift детектится непрерывно, а не "когда кто-то заметит"). Цена — operational overhead на controller, eventual consistency между merge и rollout, и необходимость дисциплины: запрещать прямые kubectl apply в prod, иначе git перестаёт быть source of truth и весь смысл практики разрушается.
Думайте о GitOps как о реализации control loop из теории управления, только вместо термостата — Application Controller, вместо температуры — состояние кластера, вместо setpoint — манифесты в git. Три элемента:
kubectl get).diff(desired, observed) и применяет действия, чтобы свести разницу к нулю.Ключевая идея: controller — единственный writer в кластер. Люди пишут в git, controller пишет в kube-apiserver, и эти потоки не пересекаются. Когда они пересекаются (kubectl edit в обход git) — это drift, и его либо чинят (selfHeal: true), либо подсвечивают как алерт.
Второй слой — progressive delivery. Базовый GitOps делает "apply и молись"; Argo Rollouts добавляет canary/blue-green поверх. Теперь reconciliation идёт пошагово: 10% трафика → анализ метрик из Prometheus → решение продолжать или откатить. Желаемое состояние всё равно в git (Rollout CR вместо Deployment), но путь к нему — управляемый, с автоматическими guard rails.
Слева — Developer Workflow и Git Repository: developer открывает PR, проходит review, мержит в main. В git лежит Kustomize-структура: base/ с общими манифестами и overlays/{dev,staging,prod} с env-specific патчами (replicas, resources, image tags).
Ниже — Secrets Management: секреты не коммитятся в открытом виде. Два паттерна — SOPS (шифруем YAML и кладём в git, ArgoCD расшифровывает при sync через age/KMS-key) и External Secrets Operator + Vault (в git лежит только ссылка типа ExternalSecret { secretRef: vault:/path }, ESO подтягивает реальный секрет из Vault и создаёт K8s Secret).
Центр — ArgoCD Control Plane внутри кластера: Repo Server (клонит git, рендерит Helm/Kustomize), Application Controller (diff и apply), API/UI (визуализация и ручные действия), Redis (кеш отрендеренных манифестов).
Правее — Argo Rollouts: отдельный controller для progressive delivery. Реагирует на Rollout CR, управляет ReplicaSet'ами вручную (вместо встроенного Deployment), запускает AnalysisRun для проверки метрик в Prometheus и принимает решение promote/abort.
Правый блок — Kubernetes Cluster: kube-apiserver, etcd, stable и canary deployments, Service/Ingress, который делит трафик по weight. Снизу — End Users, чьи запросы попадают через Ingress на pods.
1. PR merge → ArgoCD sync (happy path). Developer меняет replicas: 3 → 5 в overlays/prod/deployment.yaml, открывает PR, проходит ревью, мержится в main. ArgoCD Repo Server либо полл-ит git каждые ~3 минуты, либо получает webhook от GitHub и подтягивает изменения сразу. Application Controller рендерит финальный YAML (base + overlay), сравнивает с тем, что в кластере, видит OutOfSync, и при включённом auto-sync делает server-side apply через kube-apiserver. Deployment controller масштабирует ReplicaSet, появляются новые pods, ArgoCD ставит Synced + Healthy, зелёная галка в UI. Весь путь от merge до running pods — минуты, без участия человека и без CI-токенов с cluster-admin.
2. Drift detection и auto-heal. Junior-инженер, не дочитав onboarding, делает kubectl scale deploy/api --replicas=10 в обход git. kube-apiserver принимает изменение и пишет в etcd — он не знает про GitOps. Deployment controller масштабирует поды до 10. Через несколько секунд Application Controller замечает diff через K8s watch API (live=10, git=5), помечает Application как OutOfSync, отправляет alert в Slack. Если включён selfHeal: true — controller сам делает apply git-состояния и возвращает replicas=5. Manual change стёрт, в git audit остаётся только legitimate история, junior получает урок: "kubectl — read-only, изменения через PR".
3. Canary с Argo Rollouts. PR с image: api:v2 мержится; ArgoCD синкает Rollout CR (вместо обычного Deployment). Rollouts Controller выкатывает ReplicaSet v2 с 1 подом (10% от 10), настраивает service mesh weight на 90/10. Запускается AnalysisRun: каждые 30 секунд гоняется PromQL-запрос на error rate v2 vs v1. Через 5 минут error rate на v2 — 2.5% (норма 0.05%) — failure threshold пробит. AnalysisRun → Failed, Rollout abort: ReplicaSet v2 скейлится до нуля, весь трафик возвращается на v1. В UI и Slack — alert, git "запинен" на старой версии до нового PR. Никто не дёргал kubectl rollout undo руками.
Pull vs push. Push (классический CI с cluster creds) проще: один pipeline, прямой путь от merge до apply. Но cluster-admin токен живёт на CI-сервере, и его компрометация = компрометация кластера. Drift не детектируется. Multi-cluster требует копий creds. Pull (GitOps): creds остаются в кластере, controller подтягивает git, drift детектируется непрерывно, multi-cluster — просто ставите controller в каждый. Цена: дополнительный компонент (Argo/Flux), eventual consistency (sync interval), сложнее дебажить (где смотреть: в git, в Application CR, в логах controller'а?).
Auto-sync vs manual sync. Auto-sync = production-grade GitOps: git merge → автоматически в кластер. Полная декларативность, но требует доверия к ревью-процессу: плохой PR = плохой prod. Manual sync = "git как очередь на approve, человек жмёт Sync в UI". Безопаснее для регулируемых сред, но добавляет лишний шаг и провоцирует "ой, забыл засинкать". Компромисс: auto-sync для dev/staging, manual для prod, или auto + AnalysisTemplate как guard.
Один репо vs два (app + manifests). Один монорепо: код приложения и манифесты вместе, проще атомарные изменения "новая фича + новый env var". Но release требует двух merge: PR в код, потом PR в манифест с новым image tag. Два репо: code repo собирает образ и автоматически создаёт PR в manifest repo с обновлённым тегом — чистое разделение responsibilities, можно дать разные права на read/write, manifest repo можно протекторить жёстче. Большинство зрелых команд приходят к двум репо.
Branches per env vs folders per env. Branches (dev, staging, prod): просто, но cherry-pick между ветками ад, git log дробится, и нет атомарного "промоушена" фичи через все среды. Folders (overlays/dev, overlays/staging, overlays/prod через Kustomize): один main, изменения через overlay-патчи, promotion = PR, меняющий image tag в нужном overlay. Folders — рекомендуемая практика; branches остались legacy.
Secrets: SOPS vs External Secrets Operator. SOPS: шифруем YAML локально (age или KMS-key), коммитим зашифрованное в git, controller расшифровывает при sync. Просто, audit-friendly (git хранит историю секретов), но ротация = новый коммит, и ключ расшифровки должен быть у controller'а. ESO: в git только ExternalSecret { backend: vault, key: ... }, реальные секреты в Vault/AWS Secrets Manager/GCP Secret Manager. Ротация прозрачна, secrets никогда не касаются git, но добавляется зависимость на внешнюю систему и runtime-фейл, если Vault недоступен.
Argo CD — самый популярный GitOps-controller для Kubernetes, родом из Intuit, теперь CNCF Graduated. Богатая UI, multi-tenant через AppProjects, hooks, sync waves. Используется в Tesla, Adobe, Red Hat OpenShift, Tinkoff. Flux v2 — конкурент от Weaveworks, CLI-first, более модульная (Source/Kustomize/Helm controllers отдельно), используется в GitLab, RingCentral, государственных проектах ЕС.
Spinnaker — старший товарищ (Netflix, 2015), multi-cloud, поддерживает не только K8s, но и EC2, GCE. Тяжёлый и сложный — рекомендуется только если у вас по-настоящему мульти-облачный pipeline. Atlantis — GitOps для Terraform: на PR с .tf изменениями запускает terraform plan, постит diff в PR-комментарий, на apply-команду применяет. Не для K8s, но та же философия: git как source of truth для infra.
Jenkins X — попытка переупаковать Jenkins под GitOps; не взлетел широко. ArgoCD + GitHub Actions для CI (билд образа + bump тега в manifest repo) — самая распространённая boring-комбинация в 2024-2025. Для progressive delivery — Argo Rollouts (с ArgoCD) или Flagger (с Flux), оба умеют canary/blue-green/A-B testing с метриками из Prometheus, Datadog, New Relic.
kubectl apply руками в prod при включённом GitOps. Selfheal либо откатит вашу правку через минуту, либо (если selfheal выключен) кластер уйдёт в перманентный drift, и git перестанет быть source of truth. Если нужно срочно — открывайте hotfix PR.argocd.argoproj.io/sync-wave: -1.lookup и randAlphaNum. Эти функции делают рендер недетерминированным: один и тот же git revision рендерит разный YAML при каждом sync, ArgoCD считает это вечным drift. Уберите non-deterministic функции или зафиксируйте значения через values.kubectl apply -k overlays/prod из ноутбука работает и понятен.replicas в git как фиксированное число — controller будет вечно дрожать. Исключите replicas из reconciliation через ignoreDifferences.kubectl apply, только медленнее". Сначала культура, потом инструмент.iac-terraform, kubernetes-fundamentals, progressive-delivery, secrets-management.