Деплой приложений через Argo CD: Application, sync и App-of-Apps

Рейтинг: 80.6% · 16 голосов
Kubernetes для разработчиков: поды, деплойменты, сервисы, ingress, конфиги и отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
anton_k8s
Сообщения: 55
Зарегистрирован: 12 май 2026, 03:23

Деплой приложений через Argo CD: Application, sync и App-of-Apps

Сообщение anton_k8s »

Оглавление курса (48)
  1. Зачем нужен Kubernetes и из чего состоит кластер
  2. Поднимаем локальный кластер: minikube и kind
  3. Поды: базовая единица запуска
  4. Deployment и ReplicaSet: управляем репликами
  5. Service: сетевой доступ к подам
  6. ConfigMap и Secret: выносим конфигурацию
  7. Ingress: пускаем трафик снаружи
  8. Хранилище: Volumes и PersistentVolumeClaim
  9. Namespaces, requests и limits
  10. Health checks: liveness и readiness пробы
  11. Отладка: почему под не стартует
  12. Helm: пакетный менеджер для Kubernetes
  13. Базовая безопасность: RBAC и доступы
  14. Job и CronJob: разовые и периодические задачи
  15. StatefulSet и DaemonSet: stateful-нагрузки и системные агенты
  16. Стратегии обновления и планирование: rollout и rollback, graceful shutdown, nodeSelector, affinity, taints
  17. Автомасштабирование: HPA по метрикам, обзор VPA и Cluster Autoscaler
  18. Наблюдаемость: логи, метрики, events, обзор Prometheus и Grafana
  19. Безопасность глубже: securityContext, Pod Security Standards, NetworkPolicy, шифрование секретов
  20. Архитектура control plane: api-server, etcd, scheduler
  21. Узел кластера: kubelet, kube-proxy и container runtime
  22. Декларативная модель: reconciliation, контроллеры, CRD
  23. kubectl профессионально: get, describe, explain, jsonpath
  24. Под глубже: init, sidecar, lifecycle hooks и QoS
  25. Метки, селекторы и организация ресурсов
  26. Сервисы и kube-proxy глубже: типы, IPVS, EndpointSlices
  27. Ingress, ingress-контроллеры и Gateway API
  28. Секреты в кластере: шифрование, External Secrets, Vault
  29. Хранилище глубже: PV, StorageClass, CSI и StatefulSet
  30. Сеть кластера: CNI, NetworkPolicy и CoreDNS
  31. Планировщик: affinity, taints, topology spread
  32. Ресурсы, QoS и вытеснение: requests, limits, eviction
  33. Helm глубже: шаблоны, хуки, зависимости, OCI
  34. Kustomize и управление конфигурацией без шаблонов
  35. RBAC и аутентификация глубже
  36. Admission и Pod Security: контроль на входе
  37. Policy as code: Kyverno и OPA Gatekeeper
  38. GitOps: Argo CD и Flux
  39. Операторы и CRD: расширяем Kubernetes
  40. Сервис-меш: Istio, Linkerd и когда он нужен
  41. Эксплуатация кластера: апгрейд, узлы, бэкап etcd
  42. Где запускать кластер: managed, self-hosted, k3s, Deckhouse
  43. Траблшутинг кластера: Pending, CrashLoop, узлы NotReady
  44. Стоимость и эффективность кластера: FinOps
  45. CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы
  46. Лучшие практики и антипаттерны Kubernetes
  47. Сквозной проект и путь дальше: от манифеста до прода
  48. Деплой приложений через Argo CD: Application, sync и App-of-Apps (вы здесь)
Боль, которую лечит GitOps

Представь типичный пайплайн CI: код собрался, тесты прошли, а в конце джоба прячется заветная строчка

Код: Выделить всё

kubectl apply -f manifests/
или

Код: Выделить всё

helm upgrade
. Чтобы это сработало, твой раннер в GitLab или GitHub Actions должен иметь kubeconfig с правами на прод-кластер. То есть креды от продакшена лежат в переменных CI, любой, кто получит доступ к раннеру, получает root над кластером. Это push-модель: CI толкает изменения снаружи внутрь.

Дальше начинается дрейф. Кто-то ночью пофиксил инцидент через

Код: Выделить всё

kubectl edit deployment
, кто-то поднял replicas руками, кто-то в спешке поменял лимиты. В git об этом ни строчки. Через месяц никто не знает, что реально крутится в кластере, а откат "к предыдущей версии" превращается в археологию. Знакомо? Вот тут и появляется gitops kubernetes как дисциплина, а argocd (он же argo cd) - как самый популярный её инструмент.

Идея проста до неприличия: git - единственный источник правды. То, что описано в репозитории, и есть желаемое состояние кластера. А специальный контроллер внутри кластера сам тянет (pull) манифесты из git и приводит кластер в соответствие. CI больше не ходит в кластер - он только собирает образ и пишет новый тег в git. Креды от прода остаются внутри прода. Это и есть pull-модель, и в этом её главное преимущество перед push из CI.

Изображение

Установка и первый вход в Argo CD

Argo CD ставится в отдельный namespace. Самый прямой способ - официальный манифест.

Код: Выделить всё

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
Это поднимет несколько компонентов, и важно понимать, кто за что отвечает, иначе диагностика превратится в гадание:
  • argocd-application-controller - сердце системы. Это тот самый reconcile-loop, который сравнивает git с кластером и применяет diff.
  • argocd-repo-server - клонирует git-репозитории, рендерит Helm/Kustomize в плоский YAML и кеширует результат.
  • argocd-server - API и веб-UI, сюда же ходит CLI.
  • argocd-applicationset-controller - генерирует Application'ы из шаблонов (о нём ниже).
  • argocd-redis и argocd-dex - кеш и SSO-прокси.
В проде так не ставят - там helm-чарт с пинованной версией и настроенным HA, но для понимания механики манифест нагляднее:

Код: Выделить всё

helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd -n argocd --create-namespace
Дальше нужен CLI. Ставим , пробрасываем порт и логинимся. Начальный пароль admin лежит в секрете - его генерит сам Argo при первой установке:

Код: Выделить всё

kubectl -n argocd port-forward svc/argocd-server 8080:443
argocd admin initial-password -n argocd
argocd login localhost:8080 --username admin --insecure
Первое, что сделай после входа в веб-UI, - смени пароль и удали секрет

Код: Выделить всё

argocd-initial-admin-secret
, он не пересоздаётся и больше не нужен. На бой Argo обычно выставляют через Ingress или Gateway API с TLS, а admin-аккаунт вообще отключают в пользу SSO - но это тема эксплуатации, к ней вернёмся.

Application: главный объект и его анатомия

Всё в Argo CD крутится вокруг CRD argocd application. Это и есть единица деплоя: "вот этот путь в этом репозитории должен быть раскатан в этот namespace этого кластера". Минимальный, но реальный манифест для деплоя приложения kubernetes выглядит так:

Код: Выделить всё

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: shop-api
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: https://gitlab.cyberlake.ru/team/shop-deploy.git
    targetRevision: main
    path: apps/shop-api/overlays/prod
  destination:
    server: https://kubernetes.default.svc
    namespace: shop
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true
Разберём по полям, потому что дьявол тут в деталях.

source описывает желаемое состояние. и - откуда брать манифесты.

Код: Выделить всё

targetRevision
- это git-ревизия: ветка, тег или конкретный SHA. Огромная грабля новичков - оставить

Код: Выделить всё

targetRevision: HEAD
или на проде. Тогда любой коммит в ветку автоматически едет на бой. На проде пинуй тег или хотя бы держи отдельную ветку. Если в лежит Helm-чарт, source расширяется блоком с или

Код: Выделить всё

valueFiles
; для Kustomize Argo находит

Код: Выделить всё

kustomization.yaml
сам.

destination - куда катим. вида

Код: Выделить всё

https://kubernetes.default.svc
означает "тот же кластер, где живёт Argo". Для мульти-кластера тут будет URL внешнего API-сервера, заранее зарегистрированного через

Код: Выделить всё

argocd cluster add
.

Код: Выделить всё

namespace
- целевой неймспейс.

finalizers с

Код: Выделить всё

resources-finalizer.argocd.argoproj.io
- важная штука. Без него удаление Application оставит ресурсы в кластере висеть сиротами. С ним Argo при удалении Application выполнит каскадное удаление всех порождённых ресурсов. Это называется cascading deletion, и забыть про финалайзер - значит однажды собирать мусор руками.

Sync, status и детект дрейфа

Теперь самое мясо - как именно идёт argo cd деплой. У Application два независимых измерения состояния, и их постоянно путают.

Sync status отвечает на вопрос "совпадает ли кластер с git": или

Код: Выделить всё

OutOfSync
. Health status отвечает на вопрос "а приложению-то хорошо": ,

Код: Выделить всё

Progressing
,

Код: Выделить всё

Degraded
, . Это ортогональные вещи. Можно быть Synced и Degraded одновременно - git раскатан полностью, но поды падают в CrashLoopBackOff. И наоборот, OutOfSync и Healthy - в git появился новый коммит, а в кластере ещё крутится старое работающее.

Контроллер каждые несколько минут (плюс по вебхуку из git) делает reconcile: рендерит манифесты из repo-server и сравнивает с живыми объектами через three-way diff (git, live-состояние, last-applied). Расхождение - это и есть drift. Посмотреть его руками:

Код: Выделить всё

$ argocd app diff shop-api
===== apps/Deployment shop/shop-api ======
2c2
<       image: registry.cyberlake.ru/shop-api:1.4.2
---
>       image: registry.cyberlake.ru/shop-api:1.4.1
Стрелка - желаемое из git, - живое в кластере. Тут видно: в git образ уже 1.4.2, а в кластере ещё 1.4.1, значит app в статусе OutOfSync.

Ручной sync запускается командой:

Код: Выделить всё

$ argocd app sync shop-api
TIMESTAMP  GROUP  KIND         NAMESPACE  NAME      STATUS    HEALTH        HOOK
2026-...    apps   Deployment   shop       shop-api  Synced    Progressing
2026-...           Service      shop       shop-api  Synced    Healthy
А вот automated sync из манифеста выше делает это сам, и тут два флага, которые надо понять железно:
  • prune: true - если ресурс убрали из git, Argo удалит его из кластера. Без prune удалённый из репозитория объект останется висеть в кластере навсегда. С prune git становится по-настоящему декларативным: нет в git - нет в кластере.
  • selfHeal: true - если кто-то поправил объект руками (тот самый

    Код: Выделить всё

    kubectl edit
    ночью), Argo откатит правку обратно к git-состоянию. Это убивает дрейф на корню.
Запомни связку: prune защищает от "забытых" ресурсов, selfHeal - от "ручных" правок. Вместе они делают git единственной правдой.

App-of-Apps и ApplicationSet: масштаб

Один Application на одно приложение - норм, пока их пять. Когда их пятьдесят, ты не хочешь создавать каждый руками. Тут два паттерна.

App-of-Apps - родительский Application, чей source указывает не на манифесты приложения, а на каталог с манифестами других Application'ов. Argo раскатывает родителя, тот создаёт детей, дети раскатывают свои приложения. Получается дерево, и весь кластер бутстрапится из одного корневого Application. Удобно для платформенного слоя: один root-app поднимает мониторинг, ingress-контроллер, cert-manager и так далее.

ApplicationSet - более мощная штука. Это отдельный CRD, который по шаблону генерит много Application'ов. Сердце его - генераторы:
  • list - явный список значений, по одному Application на элемент.
  • cluster - по одному Application на каждый зарегистрированный кластер. Тот самый мульти-кластерный деплой одной строкой.
  • git - сканирует директории или файлы в репозитории и плодит Application'ы по находкам.
Один шаблон - десятки окружений. Пример с cluster-генератором, который катит мониторинг во все кластеры флота:

Код: Выделить всё

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: monitoring-fleet
  namespace: argocd
spec:
  generators:
    - clusters: {}
  template:
    metadata:
      name: 'monitoring-{{name}}'
    spec:
      project: platform
      source:
        repoURL: https://gitlab.cyberlake.ru/team/platform.git
        targetRevision: main
        path: monitoring
      destination:
        server: '{{server}}'
        namespace: monitoring
      syncPolicy:
        automated: { prune: true, selfHeal: true }
Добавил новый кластер через

Код: Выделить всё

argocd cluster add
- и мониторинг приехал туда сам, без единого нового манифеста.

Порядок раската: sync waves и hooks

Реальные приложения требуют порядка. CRD должен появиться раньше ресурса, который его использует. Миграция БД должна пройти до запуска новых подов. За это отвечают две механики.

Sync waves - аннотация

Код: Выделить всё

argocd.argoproj.io/sync-wave
с числом. Argo сортирует ресурсы по волнам и катит по возрастанию: сначала -1, потом 0 (дефолт), потом 1. Внутри волны Argo сам сортирует по типу (namespace раньше всего, что в нём живёт). Следующая волна не стартует, пока ресурсы предыдущей не стали Healthy.

Resource hooks - аннотация

Код: Выделить всё

argocd.argoproj.io/hook
с фазой. Это обычно Job, который встраивается в процесс sync:
  • PreSync - до основного раската. Классика - миграция БД.
  • Sync - вместе с основными ресурсами.
  • PostSync - после того как всё стало Healthy. Прогрев кеша, smoke-тесты, нотификация.
  • SyncFail - если sync упал, для отката или алерта.
Миграция БД перед деплоем выглядит так:

Код: Выделить всё

apiVersion: batch/v1
kind: Job
metadata:
  name: db-migrate
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: migrate
          image: registry.cyberlake.ru/shop-api:1.4.2
          command: ["./migrate", "up"]

Код: Выделить всё

hook-delete-policy: HookSucceeded
удалит Job после успеха, чтобы не копить мусор. Если PreSync-хук упадёт, основной sync не начнётся - и это правильно: накатывать код на непромигрированную схему опаснее, чем не накатить вовсе.

Откат, история и AppProject

GitOps-откат идеологически правильный - это revert коммита в git. Но когда горит, есть быстрый путь через историю:

Код: Выделить всё

$ argocd app history shop-api
ID  DATE                 REVISION
3   2026-06-14 22:10:00  1.4.2 (a1b2c3d)
2   2026-06-14 18:00:00  1.4.1 (f4e5d6c)

$ argocd app rollback shop-api 2
Учти: при включённом selfHeal rollback через CLI - временный. Контроллер увидит расхождение с git и снова накатит то, что в git. Поэтому настоящий откат на проде - это всё-таки коммит-revert, а rollback хорош как аварийная мера на минуты, пока готовишь revert.

AppProject - механизм изоляции и RBAC. Дефолтный project разрешает всё со всех репозиториев во все кластеры - на бою это дыра. Project ограничивает, какие repoURL, кластеры и namespace разрешены для его Application'ов:

Код: Выделить всё

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: shop
  namespace: argocd
spec:
  sourceRepos:
    - https://gitlab.cyberlake.ru/team/shop-deploy.git
  destinations:
    - server: https://kubernetes.default.svc
      namespace: shop
  clusterResourceWhitelist: []
Теперь Application проекта shop физически не сможет катить из чужого репозитория или в чужой namespace. Это первая линия обороны от "разработчик случайно задеплоил в kube-system".

Секреты в GitOps и интеграция с Rollouts

Главное правило: секреты в git открытым текстом не кладём никогда. Репозиторий часто читаем половиной компании, а git хранит историю вечно - удалить из истории сложно. Варианты:
  • Sealed Secrets - шифруешь секрет публичным ключом контроллера, в git лежит зашифрованный SealedSecret, расшифровать может только контроллер в кластере.
  • SOPS (часто с age/KMS) - шифрование значений в YAML, Argo расшифровывает через плагин.
  • External Secrets Operator - в git только ссылка на секрет, реальное значение тянется из Vault, Yandex Lockbox или другого хранилища.
Для прогрессивной доставки Argo CD дружит с Argo Rollouts. Вместо обычного Deployment используешь CRD Rollout с canary или blue-green стратегией: новая версия получает 10% трафика, метрики проверяются, при норме раскатка едет дальше, при деградации - автоматический откат. Argo CD при этом остаётся системой доставки, а Rollouts управляет стратегией выкатки внутри.

Грабли из практики
  • Вечный OutOfSync из-за mutating-вебхуков. Service mesh или прочие admission-вебхуки добавляют в объект поля (sidecar, аннотации), которых нет в git. Argo видит расхождение и считает app OutOfSync навсегда. Лечится переходом на

    Код: Выделить всё

    ServerSideApply=true
    и при необходимости

    Код: Выделить всё

    ignoreDifferences
    по конкретным полям.
  • selfHeal воюет с тобой. Дебажишь под через

    Код: Выделить всё

    kubectl edit
    , а Argo через секунду откатывает правку. Это не баг - это фича. На время дебага временно отключи auto-sync или используй

    Код: Выделить всё

    kubectl debug
    на отдельной копии.
  • Секрет в git. Закоммитил Secret в base64 (это НЕ шифрование, а кодирование) - считай, что утёк. base64 раскручивается за секунду.
  • Слишком широкий AppProject. Все приложения в default project - значит любой Application может катить куда угодно. Делай project на команду или на окружение.
  • targetRevision: HEAD на проде. Любой merge в ветку едет на бой без ревью раската. Пинуй теги.
Мини-лаба: сквозной деплой

Повтори руками, это полчаса:
  • Подними k3s или kind, поставь Argo CD манифестом, пробрось порт, залогинься CLI, смени пароль.
  • Создай git-репозиторий с простым Deployment + Service (nginx сойдёт), положи в

    Код: Выделить всё

    apps/web
    .
  • Создай Application через

    Код: Выделить всё

    argocd app create web --repo <url> --path apps/web --dest-server https://kubernetes.default.svc --dest-namespace demo --sync-policy automated --auto-prune --self-heal
    .
  • Проверь

    Код: Выделить всё

    argocd app get web
    - увидишь Synced/Healthy.
  • Поправь руками:

    Код: Выделить всё

    kubectl scale deploy web -n demo --replicas=5
    . Подожди и посмотри, как selfHeal вернёт реплики к значению из git.
  • Поменяй образ в git, закоммить, пушни. Через минуту

    Код: Выделить всё

    argocd app get web
    покажет, как Argo сам выкатил новую версию.
Контрольные вопросы
  • Чем отличаются sync status и health status, и может ли app быть Synced и Degraded одновременно?
  • Что произойдёт с ресурсом, если убрать его из git при

    Код: Выделить всё

    prune: true
    и при

    Код: Выделить всё

    prune: false
    ?
  • Зачем нужен PreSync-хук и что будет с основным sync, если этот хук упадёт?
  • Почему pull-модель Argo CD безопаснее push из CI, и что именно остаётся внутри кластера, а что снаружи?
Итог

Argo CD превращает git в пульт управления кластером. Application описывает желаемое состояние, контроллер постоянно сводит реальность к нему через reconcile, prune и selfHeal убивают дрейф, sync waves и hooks дают порядок раската, App-of-Apps и ApplicationSet масштабируют это на десятки приложений и кластеров, а AppProject и внешние секрет-менеджеры закрывают безопасность. CI теперь только собирает образ и коммитит тег - а выкатывает уже git. Это и есть gitops kubernetes в продакшене.
👍4 ❤️1 🔥1 😄 🤔1
Аватара пользователя
burnednullptr
Сообщения: 1
Зарегистрирован: 13 май 2026, 18:56

Re: Деплой приложений через Argo CD: Application, sync и App-of-Apps

Сообщение burnednullptr »

Наконец дошло, почему app у меня был вечно OutOfSync - linkerd впрыскивал sidecar, ServerSideApply=true починил. Спасибо за раздел про вебхуки.
👍1 ❤️ 🔥 😄 🤔1
Аватара пользователя
chuffy
Сообщения: 1
Зарегистрирован: 05 июн 2026, 05:43

Re: Деплой приложений через Argo CD: Application, sync и App-of-Apps

Сообщение chuffy »

Вопрос про selfHeal и rollback: то есть на проде аварийный откат это всегда revert коммита, а argocd app rollback держится только пока контроллер не передёрнет? правильно понял?
👍 ❤️ 🔥3 😄 🤔
Ответить
← Предыдущая глава
Сквозной проект и путь дальше: от манифеста до прода

Все главы курса «Kubernetes: оркестрация контейнеров от основ до продакшена»

Поделиться темой: ✈ Telegram VK
Похожие запросы: argocd и gitops как настроить деплой в kubernetesnginx в Docker

Вернуться в «Kubernetes на практике»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость