м. Київ, вул. Кирилівська 102

21 вересня 2026 р.

ArgoCD та GitOps

ArgoCD та GitOps

GitOps — це модель керування інфраструктурою та застосунками, у якій Git зберігає бажаний стан, а спеціальний контролер постійно порівнює його з реальним станом середовища. Для Kubernetes одним із найпоширеніших таких контролерів є Argo CD. Він читає YAML, Helm chart або Kustomize overlay з репозиторію, показує різницю та синхронізує ресурси з кластером.

На відміну від pipeline, який виконує `kubectl apply` ззовні, Argo CD працює за pull-моделлю всередині Kubernetes. CI збирає образ, перевіряє його та оновлює декларативну конфігурацію в Git. Argo CD бачить новий commit і приводить кластер до описаного стану. Такий поділ зменшує кількість довгоживучих cluster credentials у CI та залишає зрозумілу історію змін.

У матеріалі розгорнемо Argo CD, підготуємо конфігураційний репозиторій, створимо ресурс `Application`, увімкнемо контрольовану автоматичну синхронізацію та розберемо захист production-середовища. Для впровадження подібної платформи команда ITheal надає DevOps-послуги, а побудову процесу від тестів до оновлення Git-конфігурації можна включити в налаштування CI/CD.

Як працює GitOps і що робить Argo CD

У звичайній push-схемі pipeline отримує доступ до Kubernetes API й сам застосовує manifests. Результат залежить не лише від Git, а й від команд pipeline, його credentials і ручних дій адміністраторів. Якщо хтось змінить Deployment через `kubectl edit`, Git більше не відображатиме фактичний стан.

Argo CD реалізує reconciliation loop. Його application controller регулярно виконує такі дії:

1. читає revision з Git або Helm registry; 2. рендерить manifests; 3. отримує live-ресурси з Kubernetes API; 4. порівнює бажаний і фактичний стан; 5. позначає застосунок як `Synced` або `OutOfSync`; 6. за заданою політикою виконує sync або чекає ручного підтвердження.

Окремо Argo CD оцінює health ресурсів. Наприклад, Deployment може бути синхронізованим за специфікацією, але залишатися `Progressing` через недоступний image або `Degraded` через невдалі readiness probes. Тому статус sync не замінює перевірку здоров'я workload.

GitOps-репозиторій стає операційним джерелом бажаного стану. Це не означає, що до Git потрібно складати все. Паролі, приватні ключі та токени не повинні зберігатися у відкритому вигляді. Так само runtime-дані, логи й вміст persistent volumes залишаються поза декларативним репозиторієм.

Практичний процес оновлення виглядає так:

text
Developer -> application repository -> CI -> container registry
                                      -> update image tag in config repository

Config repository -> Argo CD -> Kubernetes API -> workloads

Commit або merge request у config repository має містити точну зміну: новий image digest, параметр Helm chart чи Kustomize patch. Review показує, що саме потрапить до середовища, а Git history відповідає на питання, хто, коли і чому змінив бажаний стан.

Встановлення Argo CD у Kubernetes

Для лабораторного кластера потрібні робочий `kubectl`, доступ із правами на створення CRD і окремий namespace. Спочатку перевіримо контекст, щоб випадково не встановити компоненти в інший кластер:

bash
kubectl config current-context
kubectl cluster-info
kubectl get nodes

Офіційний quick start використовує manifest зі стабільної гілки:

bash
kubectl create namespace argocd

kubectl apply \
  --namespace argocd \
  --server-side \
  --force-conflicts \
  --filename https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

Server-side apply потрібен через розмір CRD. Параметр `--force-conflicts` у цій команді стосується конфліктів керування полями під час інсталяції, але його не слід без аналізу переносити на manifests робочих застосунків.

Для production не варто назавжди залежати від рухомої гілки `stable`. Зафіксуйте протестовану версію Argo CD, збережіть спосіб інсталяції в окремому platform repository та прочитайте upgrade notes перед оновленням. Також сплануйте HA-режим, резервне копіювання конфігурації, metrics, alerts і контроль PodDisruptionBudget.

Перевіримо запуск компонентів:

bash
kubectl -n argocd get pods
kubectl -n argocd get deployments,statefulsets
kubectl -n argocd wait \
  --for=condition=Available \
  deployment/argocd-server \
  --timeout=180s

За замовчуванням API server не відкритий назовні. Для локальної перевірки без створення public LoadBalancer використаємо port-forward:

bash
kubectl -n argocd port-forward service/argocd-server 8080:443

Початковий пароль адміністратора можна прочитати з тимчасового Secret:

bash
kubectl -n argocd get secret argocd-initial-admin-secret \
  --output=jsonpath='{.data.password}' | base64 --decode
echo

Після першого входу змініть пароль, налаштуйте SSO та вимкніть вбудованого admin, коли альтернативний адміністративний доступ перевірено. Початковий Secret потрібно видалити після ротації credentials. Постійний публічний доступ до UI організовуйте через Ingress із TLS, корпоративною автентифікацією та мережевими обмеженнями, а не через відкритий NodePort.

Репозиторій конфігурації та ресурс Application

Відокремлення application code від deployment configuration спрощує права доступу й аудит. Розробник може змінювати код, але production image tag потрапить до конфігураційного репозиторію лише після review. Мінімальна структура з Kustomize може виглядати так:

text
platform-config/
└── apps/
    └── demo-web/
        ├── base/
        │   ├── deployment.yaml
        │   ├── service.yaml
        │   └── kustomization.yaml
        └── overlays/
            ├── stage/
            │   └── kustomization.yaml
            └── prod/
                └── kustomization.yaml

У base зберігається спільна конфігурація, а overlays задають namespace, replicas, ingress host, resources та image digest для конкретного середовища. Не використовуйте mutable tag `latest`: один і той самий Git commit може розгортати різний код у різний час. Краще вказувати незмінний digest:

yaml
images:
  - name: registry.example.com/demo-web
    newName: registry.example.com/demo-web
    digest: sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef

Argo CD Application також описується декларативно. Створимо `applications/demo-web-stage.yaml`:

yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: demo-web-stage
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: demo-platform

  source:
    repoURL: https://git.example.com/platform/config.git
    targetRevision: main
    path: apps/demo-web/overlays/stage

  destination:
    server: https://kubernetes.default.svc
    namespace: demo-stage

  syncPolicy:
    syncOptions:
      - CreateNamespace=true

Застосуємо ресурс і подивимося статус:

bash
kubectl apply -f applications/demo-web-stage.yaml
kubectl -n argocd get applications
kubectl -n argocd describe application demo-web-stage

Поле `source` визначає репозиторій, revision і каталог. `destination` указує кластер та namespace. URL `https://kubernetes.default.svc` означає той самий кластер, де працює Argo CD. Finalizer забезпечує каскадне видалення керованих ресурсів разом з Application, тому видалення такого об'єкта потрібно розглядати як потенційно руйнівну операцію.

Для приватного repository Argo CD потрібні credentials. Додавайте deploy key або token через захищений механізм, обмежуйте його read-only доступом лише до потрібного репозиторію та плануйте ротацію. Не комітьте repository Secret разом із незашифрованим паролем.

Синхронізація, self-heal і контрольований rollback

На першому етапі корисно залишити ручний sync. Інженер бачить diff, перевіряє ресурси й запускає застосування лише після review. Через CLI це виглядає так:

bash
argocd app diff demo-web-stage
argocd app sync demo-web-stage
argocd app wait demo-web-stage \
  --sync \
  --health \
  --timeout 300

Коли команда впевнена в manifests і перевірках, stage можна перевести на автоматичну синхронізацію:

yaml
spec:
  syncPolicy:
    automated:
      enabled: true
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
      - CreateNamespace=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

Кожен параметр має окремий наслідок:

- `enabled` дозволяє auto-sync для стану `OutOfSync`; - `prune` видаляє об'єкти, яких більше немає в Git; - `selfHeal` повертає вручну змінений live-ресурс до бажаного стану; - `allowEmpty: false` захищає від автоматичного видалення всіх ресурсів через порожнє джерело; - `retry` повторює тимчасово невдалу синхронізацію з backoff.

`prune` і `selfHeal` не слід увімкнути всюди одним масовим commit. Спочатку перевірте, які ресурси Argo CD вважає своїми, як поводяться operators і mutating webhooks, чи немає generated fields у diff. Для баз даних, PersistentVolumeClaim, CRD та namespace потрібні окремі правила захисту. Опції `Force=true` і `Replace=true` здатні пересоздати ресурс та спричинити перерву, тому їх застосовують лише до конкретного об'єкта після аналізу.

GitOps-rollback — це не натискання кнопки, яке приховує історію. Надійніший шлях — revert проблемного commit і нова синхронізація:

bash
git revert <problem-commit-sha>
git push origin main

argocd app get demo-web-prod
argocd app wait demo-web-prod --health --timeout 300

Так Git знову відповідає фактичному бажаному стану. Але повернення старого image не відкочує автоматично схему бази або дані. Міграції мають бути backward-compatible, а процедура відновлення — перевіреною окремо.

Зміна `replicas` вручну для аварійного масштабування також стане drift і може бути скасована self-heal. Якщо оперативна зміна правильна, якнайшвидше зафіксуйте її в Git або тимчасово призупиніть auto-sync за погодженою процедурою.

Розмежування середовищ, доступу та секретів

Кожен Application належить до AppProject. Вбудований `default` project дуже широкий: він може дозволяти будь-які source repositories, destinations і типи ресурсів. Для реальної платформи створіть окремий AppProject з allowlist:

yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: demo-platform
  namespace: argocd
spec:
  sourceRepos:
    - https://git.example.com/platform/config.git

  destinations:
    - server: https://kubernetes.default.svc
      namespace: demo-stage
    - server: https://kubernetes.default.svc
      namespace: demo-prod

  clusterResourceWhitelist:
    - group: ""
      kind: Namespace

  namespaceResourceBlacklist:
    - group: ""
      kind: ResourceQuota

Project обмежує, звідки дозволено читати manifests, куди їх можна розгортати та якими kinds керувати. Додатково налаштуйте Argo CD RBAC через SSO groups: розробникам може бути достатньо перегляду stage і запуску його sync, тоді як зміна production Application та Projects залишається у platform-команди. Не видавайте всім роль `admin`.

Для різних середовищ використовуйте окремі Applications і каталоги. У production краще посилатися на tag або commit SHA конфігураційного репозиторію чи просувати перевірену зміну через merge request. Один Application, який перемикається між stage і prod параметром, погіршує аудит та збільшує blast radius.

Base64 у Kubernetes Secret — це кодування, а не шифрування. Звичайний Secret за замовчуванням може зберігатися в etcd без encryption at rest. Для GitOps застосовуйте External Secrets Operator із зовнішнім secret manager, Sealed Secrets або SOPS із KMS/age. У Git має залишатися зашифрований payload або лише посилання на зовнішній секрет.

Перевіряйте також доступ Argo CD до цільових кластерів. Контролер має достатні права для керованих ресурсів, тому компрометація його namespace або repository credentials має серйозні наслідки. Використовуйте NetworkPolicy, Pod Security, шифрування etcd, короткоживучі credentials, audit logs та мінімальні Kubernetes RBAC-права.

Підхід особливо корисний для кластерів із відтворюваною базовою конфігурацією. Підготовку control plane, networking і відмовостійкості можна окремо звірити з матеріалом Побудова HA Kubernetes кластера, а Argo CD вже керуватиме workloads поверх готової платформи.

Типові помилки

- Зберігати всі застосунки, secrets і cluster-admin credentials в одному відкритому репозиторії. Розділяйте доступ і не покладайтеся на base64. - Використовувати `latest` замість immutable digest. Git перестає точно визначати версію, а rollback стає непередбачуваним. - Увімкнути `prune` до перевірки ownership. Argo CD може видалити ресурс, прибраний із Git випадково або згенерований іншим контролером. - Редагувати Deployment через `kubectl edit` і не переносити зміну до Git. Self-heal її скасує, а без self-heal залишиться постійний drift. - Залишити всі Applications у permissive `default` project. Обмежуйте repositories, clusters, namespaces і resource kinds через AppProject. - Вважати `Synced` синонімом працездатності. Завжди перевіряйте health, rollout, probes, metrics та реальний endpoint. - Дозволити CI виконувати `kubectl apply` паралельно з Argo CD. Два незалежні writers створюють гонку та незрозуміле джерело істини. - Оновлювати Argo CD без зафіксованої версії, backup і читання upgrade notes. Platform controller потребує такого самого контрольованого release process, як workloads.

Висновок

Argo CD переносить доставку Kubernetes-конфігурації з набору імперативних команд у постійний reconciliation process. Git зберігає бажаний стан, review контролює зміни, Application пов'язує репозиторій із кластером, а sync status і health показують результат. CI при цьому не потребує прямого права розгортати ресурси: він збирає artifact і пропонує зміну конфігурації.

Починайте з одного stage-застосунку та ручної синхронізації. Зафіксуйте версію Argo CD, використовуйте immutable image digest, створіть вузький AppProject, налаштуйте SSO/RBAC і винесіть secrets у захищене сховище. Лише після стабільних diff та перевіреного rollback поступово додавайте auto-sync, `selfHeal` і `prune`.

GitOps не усуває потребу в операційних правилах. Він робить їх видимими: кожна зміна має commit, кожен rollback — revert, кожне середовище — визначене джерело конфігурації. Надійність з'являється тоді, коли Git history, Kubernetes RBAC, secret management, monitoring і процедура відновлення працюють як єдина система.

📞ArgoCD та GitOps — автоматизація Kubernetes-деплоїв | ITheal