Разворачиваем сервис через ArgoCD

Меня зовут Андрей Бирюков. Я — независимый эксперт в области ИТ и ИБ, преподаю в учебных центрах и пишу статьи и книги.
В классической модели CI/CD (Jenkins, GitLab CI и др.) конвейер запускает kubectl apply или helm upgrade в конце пайплайна. Но на этом шаге возникает фундаментальная проблема, заключающаяся в ответе на вопрос, кто отвечает за то, чтобы состояние кластера всегда соответствовало конфигурации после завершения пайплайна?
Если кто‑то вручную изменил Deployment или HPA отмасштабировал реплики, конвейер CI/CD об этом не узнает.
В результате дрейф конфигурации будет накапливаться, и кластер перестает отражать то, что описано в Git.
GitOps решает эту проблему, перенося управление инфраструктурой в систему контроля версий Git и используя pull‑based модель.
Суть этой модели состоит в том, что ArgoCD работает внутри кластера, непрерывно сравнивая желаемое состояние (Git) с реальным (кластер) и автоматически приводя их в соответствие.
Git становится единственным источником истины, а ArgoCD — агентом непрерывной сверки (reconciliation).

Использование GitOps в ArgoCD позволяет получить декларативность, когда желаемое состояние описывается в YAML, Helm или Kustomize, а также версионирование, при котором каждое изменение имеет аудит‑след в Git.
Также ArgoCD постоянно отслеживает источник и синхронизирует изменения, обнаруживая и исправляя отклонения от желаемого состояния.
Развертывание сервиса payments‑api
Для лучшего понимания давайте рассмотрим реальный сценарий, в котором команда разрабатывает сервис payments‑api, размещаемый в пространстве имен payments‑stage и payments‑prod.
Рекомендуемая организация репозитория представляет собой разделение приложения и окружений через overlay‑подход (Kustomize) или values‑файлы (Helm):
manifests/
apps/
payments-api/
base/
deployment.yaml
service.yaml
configmap.yaml
overlays/
stage/
kustomization.yaml
patch-replicas.yaml
prod/
kustomization.yaml
patch-replicas.yamlПродуктивная и стейджинговая среды будут отличаться только параметрами: количеством реплик, ресурсами, тегами образов.
Такой подход позволяет CI обновлять только нужный слой после сборки образа, а production‑изменения проходят через pull request и ревью владельцев сервиса.
В свою очередь, Application — это CRD, который связывает Git‑источник с целевым кластером и namespace.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: payments-api-stage
namespace: argocd
spec:
project: default
source:
repoURL: https://git.example.com/platform/manifests.git
targetRevision: main
path: apps/payments-api/overlays/stage
destination:
server: https://kubernetes.default.svc
namespace: payments-stage
syncPolicy:
syncOptions:
- CreateNamespace=true
automated:
selfHeal: true
prune: trueЗдесь ключевыми являются следующие поля:
repoURL— репозиторий с желаемым состоянием;targetRevision— ветка, тег или commit (рекомендуется pin к ветке/тегу, не HEAD;)path— директория с манифестами для конкретного окружения;destination.server— https://kubernetes.default.svc для локального кластера, или URL зарегистрированного внешнего кластера/
Sync policy: автоматизация и безопасность
В манифесте присутствует параметр automated: {}, который включает автосинхронизацию при изменении Git.
Параметр selfHeal: true откатывает ручные изменения в кластере и подходит везде, где Git является источником истины, а prune: true удаляет ресурсы, убранные из Git, но в production применять его следует осторожно.
CreateNamespace=true автоматически создает namespace и удобен для тестовых окружений.
Здесь необходимо сделать важное предостережение: не включайте одновременно
prune: trueиselfHeal: trueдля нагрузок с длительным запуском (30–60 секунд).ArgoCD может запустить self‑heal до того, как у подов будет статус Healthy, что приведет к повреждению частично запущенных ресурсов.
Для production‑окружений часто используют режим manual sync, при котором ArgoCD видит изменение в Git, но требует подтверждения оператора перед его применением.
Для того, чтобы представленный манифест был запущен, выполним следующую команду:
kubectl apply -n argocd -f payments-api-stage-application.yaml
После применения проверьте статус:

Типовые проблемы и методы их решения
Даже при корректной конфигурации ArgoCD может показывать OutOfSync или Degraded. Здесь наиболее вероятны три категории проблем, и мы разберем каждую из них.
Начнем с рассмотрения дрейфа, при котором приложение возвращается в OutOfSync после синхронизации.
То есть, приложение вроде бы синхронизировалось, но через несколько секунд снова показывает OutOfSync.
Для начала можно выполнить команду:
argocd app diff payments-api-stage
Она покажет различия между Git и живым кластером, сравнивая каждое поле вывода с исходной конфигурацией. Наиболее частым источником дрейфа является управление полем /spec/replicas в Deployment с помощью HPA.
Также, часто admission webhook может мутировать ресурс, добавляя caBundle или annotations, контроллеры могут дописывать поля в status и annotations.
Помимо этого, не стоит забывать о ручных изменения через kubectl, которые тоже могут затрагивать произвольные поля.

Здесь очень важно понимать, что без RespectIgnoreDifferences=true sync все равно перезапишет игнорируемые поля.
Классическая нехватка прав
Еще одна типовая проблема — это SyncFailed с ошибкой forbidden, при этом возможна ситуация, когда часть ресурсов успешно создалась, а часть нет.
Для диагностики здесь необходимо проверить права service account ArgoCD через использование механизма impersonation, с помощью которого аутентифицированный пользователь (или сервис) может действовать от имени другого пользователя, группы или сервисного аккаунта при выполнении API‑запросов.
Вот пример:
kubectl auth can-i create deployments \
--as=system:serviceaccount:argocd:argocd-application-controller \
-n payments-stage
Если ответ no, то проблема, скорее всего, кроется в RBAC, так как ArgoCD использует свой service account, который должен иметь права на
create/update/deleteвсех ресурсов, описанных в манифестах.Также проверьте AppProject restrictions: ArgoCD может ограничивать allowed kinds и destinations на уровне проекта.
И еще одна проблемная ситуация может возникнуть, когда манифест вроде бы применен, но ресурсы не работают. Здесь симптомами является наличие сообщений
Sync status = Synced, но при этомHealth = DegradedилиProgressing.
Это проблема выполнения, а не GitOps, то есть ваши манифесты корректны, но приложение все равно не запускается.
Для диагностики прежде всего смотрим состояние подов:
kubectl get pods -n payments-stage

Как видно, есть проблема с загрузкой образов.
Далее смотрим подробный вывод по интересующим подам.
Ниже приведен фрагмент вывода с описанием событий:
kubectl describe pod <pod-name> -n payments-stage

Посмотрим логи:
kubectl logs <pod-name> -n payments-stage

Типичные причины данных проблем является то, что ImagePullBackOff указывает на неверный тег образа или отсутствие прав на registry.
Сообщение CrashLoopBackOff, в свою очередь, указывает на ошибку в конфигурации приложения, а ProgressDeadlineExceeded - на то, что deployment не достигает готовности в отведенное время.
Если хотите проверить, насколько уверенно ориентируетесь в DevOps на практике, пройдите короткий бесплатный вступительный тест. Он поможет оценить текущий уровень и найти темы, которые стоит подтянуть.
Управление синхронизацией: стратегии и best practices
ArgoCD предоставляет четыре комбинации режимов prune/selfHeal. Так режим Auto sync only (prune: false, selfHeal: false) применяет изменения из Git, но не удаляет ресурсы и не откатывает ручные изменения.
Auto + prune (p
rune: true, selfHeal: false) дополнительно удаляет из кластера ресурсы, убранные из Git.Auto + selfHeal (
prune: false, selfHeal: true) откатывает ручные изменения, но не удаляет ресурсы.Full auto (
prune: true, selfHeal: true) обеспечивает полный GitOps: кластер всегда соответствует Git.
Здесь для использования в продуктивной среде можно рекомендовать manual sync с обязательным ревью всех изменений перед применением. А режим Auto‑sync с prune+selfHeal подходит для stage/dev, где скорость важнее контроля.
Если приложению нужны зависимости (Namespace, ConfigMap, Secret) до создания Deployment, используйте аннотации sync waves:
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1" # Namespace создается первымРесурсы применяются в порядке возрастания wave, что позволяет избежать ошибок типа namespace not found.
Еще одна возможная ситуация — это когда Kubernetes API server и mutating webhooks добавляют поля, которых нет в Git (terminationGracePeriodSeconds, dnsPolicy и др.), вызывая «фантомные» изменения.
Для решения этих проблем есть два решения.
Первое — это использование параметра ignoreDifferences, когда мы точечно игнорируем конкретные поля.
Второе — ServerSideDiff=true, выполнять dry‑run apply для каждого ресурса и сравнивать результат. Но это увеличивает нагрузку на API server, поэтому тестируйте на малом количестве приложений.
Выводы
ArgoCD превращает Kubernetes в систему, где состояние кластера всегда определяется Git. Но «магия» GitOps требует понимания механики работы системы.
Например, как работает reconciliation loop, какие поля могут вызывать дрейф, как RBAC влияет на синхронизацию, и когда автоматизация опасна.
Диагностический подход — начните с argocd app diff, определите категорию проблемы (drift/permissions/runtime), примените точечное решение.
Не подавляйте дрейф широкими ignore‑правилами, так как это маскирует реальные проблемы конфигурации.
GitOps с ArgoCD — это не просто инструмент деплоя. Это операционная модель, в которой Git становится контрактом между командами разработки и платформы, а ArgoCD — гарантом его соблюдения.

Когда деплой уже работает, следующий вопрос — насколько предсказуемо ведёт себя вся инфраструктура: от создания кластера до доставки изменений и контроля его состояния.
Разобраться с этими задачами на практике и собрать отдельные инструменты в цельный процесс можно на бесплатных открытых уроках:
7 октября в 20:00. «GitOps‑практики: развертываем сервис через ArgoCD». Записаться
15 октября в 20:00. «Поднимаем кластер Kubernetes с помощью Terraform и Ansible». Записаться
Полный список открытых уроков на октябрь собрали в дайджесте.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.