ArgoCD에서 “배포 단위”는 Deployment도 Helm release도 아닙니다. Application이라는 CRD 하나입니다. 이 리소스가 “어떤 Git 경로를 어떤 클러스터·네임스페이스에 어떻게 동기화할지”를 전부 담습니다. 2편에서 본 application-controller가 무한히 감시하는 대상도 결국 이 Application 오브젝트입니다

Application 리소스 구조

가장 단순한 Application 매니페스트는 이렇게 생겼습니다

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-service
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/org/infra-repo
    path: services/my-service
    targetRevision: main
  destination:
    server: https://kubernetes.default.svc
    namespace: prod
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

핵심은 세 블록입니다

블록 역할
source Git 어디서 매니페스트를 가져올지 (repo·path·branch)
destination 어느 클러스터의 어느 네임스페이스에 배포할지
syncPolicy 수동 Sync인지 자동인지, drift 감지 시 어떻게 할지

source는 Helm·Kustomize·plain YAML 무엇이든 올 수 있습니다. repo-server가 알아서 감지해서 렌더링합니다

동기화 모드: Manual vs Automated

Application을 생성하면 가장 먼저 결정해야 하는 건 “누가 Sync를 트리거하느냐”입니다

flowchart LR
    GIT["Git 변경 감지"]
    OOS{"OutOfSync"}
    MANUAL["사용자가 Sync 버튼<br/>또는 argocd app sync"]
    AUTO["자동 Sync 즉시 실행"]
    APPLY["kubectl apply"]

    GIT --> OOS
    OOS -->|"syncPolicy 없음"| MANUAL
    OOS -->|"automated 설정"| AUTO
    MANUAL --> APPLY
    AUTO --> APPLY

    classDef primary fill:#2563eb,stroke:#1e40af,color:#ffffff
    classDef warning fill:#d97706,stroke:#b45309,color:#ffffff
    classDef success fill:#059669,stroke:#047857,color:#ffffff
    classDef neutral fill:#475569,stroke:#334155,color:#ffffff

    class GIT primary
    class OOS warning
    class MANUAL neutral
    class AUTO success
    class APPLY primary
모드 동작 언제 쓰나
Manual OutOfSync 감지만 하고 사람이 Sync 버튼을 눌러야 실제 배포 프로덕션 초기, 승인 절차가 필요한 환경
Automated 차이 감지 즉시 자동 apply Dev·Staging, 신뢰 가능한 PR 리뷰 체계가 있는 팀

Automated 모드에는 중요한 옵션 두 가지가 따라붙습니다

prune: Git에서 삭제된 리소스를 클러스터에서도 지울지

prune: false면 Git 매니페스트에서 Deployment 하나를 삭제해도 클러스터에는 그대로 남습니다. 안전하지만 zombie 리소스가 쌓입니다. prune: true는 Git이 진짜 SSOT(Single Source of Truth)가 되게 강제하지만, 실수로 삭제한 파일 때문에 프로덕션 리소스가 날아갈 수 있습니다

selfHeal: 클러스터에서 직접 바꾼 걸 원복할지

1편에서 소개한 “타노스 리셋” 옵션입니다. 누군가 kubectl scale로 replicas를 임의 변경하면 ArgoCD가 Git 상태로 강제 복구합니다. 운영 안정성을 위해 프로덕션에서는 강하게 권장되지만, 긴급 장애 대응 중에는 일시적으로 꺼두기도 합니다

OutOfSync 판정 로직

“차이가 난다”를 어떻게 판정할지도 생각보다 복잡합니다. 컨트롤러는 kubectl diff와 유사한 로직을 쓰지만, 쿠버네티스 기본 필드들(예: status, metadata.managedFields)은 무시합니다

flowchart TB
    START["Live 리소스 조회"]
    NORM["정규화<br/>(status·managedFields 제거)"]
    IGNORE{"ignoreDifferences<br/>규칙 적용?"}
    DIFF["필드별 비교"]
    RESULT차이 있음?
    IN["Synced"]
    OUT["OutOfSync"]

    START --> NORM
    NORM --> IGNORE
    IGNORE -->|"매칭"| DIFF
    IGNORE -->|"없음"| DIFF
    DIFF --> RESULT
    RESULT -->|"아니오"| IN
    RESULT -->|"예"| OUT

    classDef primary fill:#2563eb,stroke:#1e40af,color:#ffffff
    classDef info fill:#0891b2,stroke:#0e7490,color:#ffffff
    classDef success fill:#059669,stroke:#047857,color:#ffffff
    classDef danger fill:#dc2626,stroke:#991b1b,color:#ffffff

    class START,NORM,DIFF primary
    class IGNORE info
    class IN success
    class OUT danger

HPA가 replicas를 동적으로 조정하는 경우처럼 “차이가 나도 OutOfSync로 보고 싶지 않은” 필드는 ignoreDifferences로 제외합니다

spec:
  ignoreDifferences:
  - group: apps
    kind: Deployment
    jsonPointers:
    - /spec/replicas

이런 예외가 쌓이면 매니페스트가 난잡해지므로, Application 수준이 아니라 Project 수준에서 정의해서 공통 규칙으로 관리하는 게 좋습니다

Sync Wave: 리소스 배포 순서 제어

여러 리소스가 한 번에 apply될 때 순서가 중요한 경우가 있습니다. 예를 들어 Namespace가 먼저 생겨야 그 안의 Deployment가 배포될 수 있고, ConfigMap이 먼저 있어야 Pod가 마운트할 수 있습니다. ArgoCD는 sync-wave annotation으로 이 순서를 제어합니다

metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "-1"
  • 숫자가 작을수록 먼저 apply
  • 같은 wave 내 리소스는 병렬 적용
  • wave 내 모든 리소스가 Healthy 상태가 돼야 다음 wave로 진행
Wave 용도 예시
-2 Namespace, PriorityClass
-1 CRD, ConfigMap, Secret
0 (기본) Deployment, StatefulSet, Service
1 HPA, PDB, ServiceMonitor
2 Ingress, ExternalDNS 레코드

Sync Hook: 배포 전후 작업 실행

DB 마이그레이션처럼 “배포 전에” 실행돼야 하는 작업이 있습니다. 이럴 때는 일반 Job에 hook annotation을 붙입니다

metadata:
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
Hook 타입 실행 시점
PreSync Sync 시작 전 (DB migration 등)
Sync 본 Sync와 함께 (기본값)
PostSync Sync 완료 후 (smoke test, 알림)
SyncFail Sync 실패 시 (정리 작업)

hook-delete-policy로 성공한 Hook 리소스를 자동 정리하면 네임스페이스가 Job 찌꺼기로 어지러워지지 않습니다

실전 조합 예시

프로덕션에서 자주 쓰는 조합입니다

spec:
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true
    - PrunePropagationPolicy=foreground
    - ApplyOutOfSyncOnly=true
    retry:
      limit: 5
      backoff:
        duration: 10s
        factor: 2
        maxDuration: 3m
옵션 효과
CreateNamespace=true 대상 네임스페이스가 없으면 자동 생성
PrunePropagationPolicy=foreground 삭제 시 의존 리소스까지 기다려 정리
ApplyOutOfSyncOnly=true 전체 apply가 아니라 변경분만 apply (성능 개선)
retry apply 실패 시 지수 백오프 재시도

정리

Application 리소스는 단순한 “배포 선언”이 아니라 배포의 모든 정책을 담는 컨테이너입니다. 특히 자동화 모드를 쓸지, self-heal을 켤지, prune을 허용할지는 팀의 신뢰 수준과 장애 허용도에 따라 단계적으로 조정해야 합니다

  • Manual → Automated는 일방통행이 아닙니다. 환경별로 다르게 설정할 수 있습니다
  • self-heal은 강력하지만 위험한 옵션 — drift 원인이 진짜 bug인지 임시 대응인지 구분하는 문화가 먼저입니다
  • Sync Wave·Hook은 복잡한 배포 시나리오를 매니페스트 안에 캡슐화하는 도구입니다. 배포 스크립트를 외부에 둘 필요가 없습니다

다음 글에서는 Application 하나로는 부족한 대규모 환경, 즉 멀티 클러스터·수백 개 Application을 어떻게 선언적으로 관리하는지 ApplicationSet으로 풀어보겠습니다