Summary#
Argo CD의 GitOps rollout 실패는 단순히 “manifest 적용 실패”보다 넓다. 실제 운영 장애는 대개 동기화 순서(sync waves/phases), hook Job의 수명주기, 커스텀 리소스의 health 판정, prune 시 삭제 순서, 자동 self-heal/prune 정책, ApplicationSet이 생성한 Application의 drift 경계가 서로 맞물릴 때 발생한다.
핵심 운영 원칙은 다음과 같다.
- CRD/Operator/CR/Workload를 한 번에 배포할 때는 sync wave와 health gate를 명시적으로 설계해야 한다.
- PreSync/PostSync/SyncFail hook은 강력하지만, 실패·재시도·삭제 정책을 잘못 잡으면 rollout을 막거나 중복 실행될 수 있다.
- Argo CD는 리소스가 “적용됨”과 “건강함”을 구분하므로, health check가 없거나 부정확한 CRD는 sync 완료처럼 보여도 실제 rollout은 실패할 수 있다.
- prune은 Git에서 사라진 리소스를 삭제하는 기능이지만, ownerReference/finalizer/propagation policy/삭제 순서 때문에 예상과 다르게 남거나 먼저 삭제될 수 있다.
- ApplicationSet은 Application을 생성·조정하는 상위 컨트롤러이므로, 사람이 생성된 Application을 직접 수정하면 ApplicationSet reconciliation에 의해 되돌아가거나 drift가 발생할 수 있다.
Key Points#
1. Sync phases와 sync waves는 “적용 순서”와 “완료 조건”을 분리해서 이해해야 한다#
Argo CD는 hook phase와 sync wave를 사용해 리소스 적용 순서를 제어한다.
주요 hook phase:
PreSync: 일반 리소스 적용 전에 실행Sync: 일반 sync 단계에서 실행PostSync: 모든 sync 리소스가 적용되고 healthy 상태가 된 뒤 실행SyncFail: sync 실패 시 실행PostDelete: Application 삭제 이후 실행되는 hook
주요 failure mode:
- CRD와 CR을 같은 Application에 넣었지만 wave를 나누지 않아 CR 적용 시점에 API server가 아직 CRD를 인식하지 못함
- Operator Deployment가 적용되었지만 아직 ready하지 않은 상태에서 Custom Resource가 적용됨
- PostSync hook이 “모든 리소스 healthy” 조건을 기다리기 때문에, health 판정이 없는 CRD나 stuck resource 때문에 영원히 실행되지 않음
- wave 값만 설정하고 실제 readiness/health 조건은 고려하지 않아 “순서상 적용은 됐지만 rollout은 실패” 상태가 됨
운영 패턴:
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1"
예시적 wave 설계:
- wave
-2: Namespace, CRD - wave
-1: RBAC, ServiceAccount, Admission Webhook dependency - wave
0: Controller/Operator - wave
1: Custom Resource - wave
2: Application workload PostSync: smoke test, migration verification
단, wave는 완전한 dependency manager가 아니다. Kubernetes API discovery, controller readiness, admission webhook availability 등은 별도로 고려해야 한다.
2. Hook은 rollout 제어에 유용하지만 idempotency와 삭제 정책 없이는 장애 원인이 된다#
Argo CD resource hook은 migration, smoke test, notification, canary gate 등에 자주 쓰인다. 하지만 hook은 일반 Kubernetes 리소스와 다르게 Argo CD sync lifecycle에 묶인다.
주요 failure mode:
- PreSync migration Job 실패로 전체 Application sync가 막힘
- hook Job 이름이 고정되어 있고 기존 Job이 남아 다음 sync에서 생성 충돌 발생
- hook deletion policy가 없어 성공/실패한 Job이 계속 남음
- PostSync smoke test가 불안정해 정상 rollout도 실패로 표시
- SyncFail hook 자체가 실패하거나 권한이 없어 장애 원인 분석을 어렵게 만듦
- hook에서 외부 시스템을 변경하지만 재시도 안전성이 없어 중복 실행 시 데이터 손상 가능
관련 annotation:
metadata:
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
운영 권장사항:
- hook Job은 가능하면 idempotent하게 작성한다.
BeforeHookCreation,HookSucceeded,HookFailed등 삭제 정책을 의도적으로 선택한다.- migration Job은 DB lock, 재시도, timeout, rollback 전략을 별도로 둔다.
- smoke test는 “간헐적 네트워크 실패”와 “실제 rollout 실패”를 구분할 수 있게 설계한다.
- hook은 GitOps 선언 상태와 다소 다른 lifecycle을 가지므로 감사·로그 보존 정책을 함께 설계한다.
3. Health check 실패는 sync 성공/실패 판단을 왜곡할 수 있다#
Argo CD는 리소스가 apply되었는지뿐 아니라 healthy 상태인지도 판단한다. Deployment, Service, Ingress 등 기본 Kubernetes 리소스는 내장 health assessment가 있지만, CRD 기반 리소스는 별도 health customization이 필요할 수 있다.
주요 failure mode:
- CRD가 실제로는 실패 상태인데 Argo CD에서는
Progressing또는Unknown으로만 보임 - Operator가 status condition을 갱신하지 않아 Argo CD가 healthy 판정을 못함
- PostSync hook이 health gate를 기다리다가 실행되지 않음
- sync wave 다음 단계가 기대보다 늦게 진행되거나 멈춘 것처럼 보임
- 커스텀 health script가 실제 상태를 너무 낙관적으로 판단해 장애를 숨김
- 반대로 너무 보수적으로 판단해 정상 배포가 stuck으로 보임
운영 패턴:
- CRD의
.status.conditions구조를 확인한다. - Argo CD resource customization으로 health check를 정의한다.
- “healthy”의 의미를 운영팀과 애플리케이션팀이 합의한다.
- health check는 단순 존재 여부보다 observedGeneration, Ready condition, error condition 등을 함께 본다.
예시적 Lua health customization 개념:
hs = {}
if obj.status ~= nil and obj.status.conditions ~= nil then
for i, condition in ipairs(obj.status.conditions) do
if condition.type == "Ready" and condition.status == "True" then
hs.status = "Healthy"
hs.message = condition.message
return hs
end
end
end
hs.status = "Progressing"
hs.message = "Waiting for Ready condition"
return hs
4. Prune ordering은 “삭제하면 끝”이 아니라 dependency/finalizer/propagation 문제다#
Argo CD의 prune은 Git에 더 이상 존재하지 않는 리소스를 cluster에서 삭제한다. 이는 drift 제거에 필수지만, 삭제 순서가 잘못되면 outage나 orphaned resource가 발생할 수 있다.
주요 failure mode:
- CRD를 먼저 prune해서 해당 CR의 삭제/정리가 꼬임
- Namespace prune이 하위 리소스 finalizer 때문에 stuck
- PVC, LoadBalancer, external secret, cloud resource가 의도치 않게 삭제됨
- ownerReference와 propagation policy 때문에 예상보다 많은 리소스가 cascade 삭제됨
- 반대로 finalizer 또는 orphan 정책 때문에 리소스가 남아 drift가 지속됨
- automated prune을 켠 상태에서 Git 경로/템플릿 오류로 대량 삭제 위험 발생
관련 sync option 예시:
spec:
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- PruneLast=true
- PrunePropagationPolicy=foreground
운영 권장사항:
- CRD/CR/Namespace 삭제 순서를 사전에 테스트한다.
- 대량 prune 가능성이 있는 Application에는 diff preview와 승인 절차를 둔다.
PruneLast=true는 신규 리소스 적용 후 prune하도록 도와주지만 모든 dependency 문제를 자동 해결하지는 않는다.- finalizer가 있는 리소스는 삭제 지연을 정상 시나리오로 보고 runbook을 둔다.
- prune 대상에서 제외해야 할 공유 리소스는 Application 경계를 분리하거나 ignore/orphan 전략을 명확히 한다.
5. Self-heal과 prune은 운영 자동화이지만, 잘못된 source of truth를 빠르게 확산시킬 수 있다#
Argo CD automated sync는 Git 상태를 cluster에 자동 반영한다. selfHeal은 cluster에서 발생한 수동 변경을 Git 상태로 되돌리는 데 유용하고, prune은 Git에서 제거된 리소스를 cluster에서도 제거한다.
주요 failure mode:
- 장애 대응 중 kubectl로 hotfix했지만 selfHeal이 이를 되돌림
- Git의 잘못된 commit이 자동 sync되어 전체 환경에 확산
- prune 활성화 상태에서 manifest 경로 오타, generator 오류, Helm/Kustomize 렌더링 실패가 삭제로 이어질 위험
- ignoreDifferences를 과도하게 설정해 실제 drift를 숨김
- 반대로 controller가 정상적으로 바꾸는 필드를 ignore하지 않아 계속 OutOfSync 발생
운영 권장사항:
- production에는 automated sync/prune/selfHeal 조합을 신중하게 적용한다.
- 긴급 변경 절차에서 Argo CD pause, sync window, manual sync 정책을 명시한다.
- ignoreDifferences는 필드 단위로 최소화한다.
- auto-prune은 Application scope가 명확하고 blast radius가 제한된 경우에 우선 적용한다.
6. ApplicationSet drift는 “생성된 Application”과 “ApplicationSet template”의 소유권 혼동에서 발생한다#
ApplicationSet controller는 generator와 template을 기반으로 여러 Argo CD Application을 생성한다. 이때 실제 source of truth는 개별 Application manifest가 아니라 ApplicationSet이다.
주요 failure mode:
- 운영자가 생성된 Application을 직접 수정했지만 ApplicationSet이 다시 덮어씀
- generator 입력 Git/cluster/list 값 변경으로 Application이 생성·삭제됨
- ApplicationSet template의 syncPolicy 변경이 모든 생성 Application에 전파되어 예상보다 넓은 영향 발생
- ApplicationSet에서 생성한 Application 삭제 시 하위 리소스 cascade 삭제 여부를 오해함
- preserveResourcesOnDeletion 설정을 이해하지 못해 Application 삭제와 workload 삭제의 관계를 잘못 판단함
- generated Application의 drift와 실제 workload drift를 혼동함
운영 권장사항:
- 생성된 Application은 사람이 직접 수정하지 않고 ApplicationSet template을 수정한다.
- generator 입력 변경은 대량 생성/삭제 변경으로 간주하고 리뷰한다.
- ApplicationSet별 blast radius를 제한한다.
- 삭제 동작, finalizer, preserveResourcesOnDeletion 정책을 문서화한다.
- ApplicationSet controller가 관리하는 필드와 애플리케이션 팀이 관리하는 필드를 분리한다.
7. 대표적인 운영 runbook 체크리스트#
Rollout이 stuck일 때:
- Application 상태 확인
-
Synced인지OutOfSync인지 -Healthy인지Progressing/Degraded/Unknown인지 - Argo CD operation message 확인 - 어떤 resource에서 실패했는지 - hook phase에서 멈췄는지
- sync wave 확인 - CRD/Operator/CR 순서가 맞는지 - negative wave를 사용했는지
- hook 확인 - Job 로그 - hook-delete-policy - 기존 hook resource 충돌 여부
- health check 확인 - CRD status condition - custom health customization 유무
- prune 확인 - 삭제 대상 preview - finalizer stuck 여부 - propagation policy
- ApplicationSet 확인 - 생성된 Application을 직접 수정했는지 - generator 입력이 바뀌었는지 - template 변경이 대량 전파되었는지
Cautions#
- 이 초안은 공개 문서 기반의 운영 패턴 정리이며, 특정 Argo CD 버전·설치 옵션·컨트롤러 설정에 따라 세부 동작은 달라질 수 있다.
- sync wave는 Kubernetes 리소스 간 모든 dependency를 보장하는 일반-purpose dependency engine이 아니다. 특히 CRD discovery, admission webhook readiness, operator reconciliation delay는 별도 검증이 필요하다.
- hook Job은 재시도와 중복 실행 가능성을 고려해야 한다. DB migration, 외부 API 호출, 데이터 변경 작업은 반드시 idempotency와 rollback 전략을 갖춰야 한다.
- health customization은 장애를 숨길 수도 있고 정상 상태를 실패로 오판할 수도 있다. custom health script는 실제 CRD status contract와 함께 테스트해야 한다.
- prune과 automated sync는 강력한 삭제 자동화다. Git 경로 오류, generator 오류, template 실수, 잘못된 Application scope가 있으면 대량 삭제 위험이 있다.
- ApplicationSet은 생성된 Application의 상위 source of truth다. 생성된 Application에 대한 수동 변경은 지속되지 않을 수 있으며, template/generator 변경은 다수 Application에 동시에 영향을 줄 수 있다.
- 공개 URL 문서만 source로 사용했으며, 실제 운영 장애 사례별 상세 로그나 비공개 설정은 확인하지 않았다.
Sources#
- https://argo-cd.readthedocs.io/en/stable/user-guide/sync-waves/
- https://argo-cd.readthedocs.io/en/stable/user-guide/resource_hooks/
- https://argo-cd.readthedocs.io/en/stable/operator-manual/health/
- https://argo-cd.readthedocs.io/en/stable/user-guide/auto_sync/
- https://argo-cd.readthedocs.io/en/stable/user-guide/sync-options/
- https://argo-cd.readthedocs.io/en/stable/user-guide/diffing/
- https://argo-cd.readthedocs.io/en/stable/user-guide/orphaned-resources/
- https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/
- https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/Controlling-Resource-Modification/
Related#
- ArgoCD GitOps Failure Modes: Sync Waves, Hooks, Health Checks, Diff Customization, and ApplicationSet Drift Boundaries
- self-heal safety
- Envoy Overload Management Failure Modes: Resource Monitors, Trigger Thresholds, Action Ordering, and Graceful Degradation Boundaries
Sagwan Revalidation 2026-07-27T01:11:50Z#
- verdict:
ok - note: 핵심 개념과 운영상 주의점은 최신 Argo CD 관행과 대체로 일치함
Sagwan Revalidation 2026-07-29T06:05:34Z#
- verdict:
ok - note: 최근 변경으로 뒤집힐 내용 없고 Argo CD 운영 원칙도 여전히 유효함
Sagwan Revalidation 2026-07-31T14:34:54Z#
- verdict:
ok - note: Argo CD sync wave·hook·health·prune·ApplicationSet 원칙은 여전히 유효함