- Published on
Argo CD Sync 실패? 드리프트·헬스체크 7단계
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
운영 중인 쿠버네티스 클러스터에서 Argo CD Sync가 실패하면 증상은 단순해 보이지만 원인은 다양합니다. OutOfSync 가 계속 남거나, SyncFailed 로 끝나거나, Sync는 되었는데 Degraded 가 풀리지 않는 식입니다. 이때 중요한 관점은 두 가지입니다.
- 드리프트(Drift): Git에 선언한 상태와 클러스터 실제 상태가 왜/어디서 달라졌는가
- 헬스체크(Health): 리소스가 실제로 준비되었는가(Ready), 혹은 Argo CD가 준비되지 않았다고 판단하는가
아래 7단계는 “빨리 좁히기”에 초점을 둔 체크리스트입니다. 각 단계는 독립적으로도 유효하지만, 보통은 1번부터 순서대로 진행하면 가장 빠릅니다.
1단계: 실패 지점을 argocd app 으로 고정하기
UI에서 Sync failed 만 보고 들어가면 이벤트가 흩어져 보입니다. 먼저 CLI로 어떤 리소스에서 어떤 메시지로 실패했는지를 텍스트로 고정하세요.
# 앱 상태 요약
argocd app get myapp
# 리소스별 상태와 메시지(중요)
argocd app get myapp --output tree
# 최근 동기화/헬스 이벤트
argocd app history myapp
argocd app logs myapp --since 10m
여기서 확인할 포인트는 다음입니다.
- 특정 리소스만
OutOfSync인가, 다수 리소스가 연쇄적으로 무너졌는가 SyncFailed의 이유가ValidationError,Forbidden,Immutable field,timeout중 무엇인가Degraded가Deployment의ProgressDeadlineExceeded인지,Ingress/HPA/ExternalSecret같은 주변 리소스인지
이 단계에서 “실패 리소스 1~2개”로 범위를 줄이는 것이 목표입니다.
2단계: 드리프트인지, 단순한 적용 실패인지 분리하기
Argo CD 문제를 해결할 때 가장 흔한 함정은 “Sync 실패”를 무조건 권한/네트워크로만 보는 것입니다. 실제로는 드리프트 때문에 계속 재적용이 일어나거나, 반대로 Git 선언이 이미 잘못되어 적용 자체가 안 되는 경우가 많습니다.
- 드리프트 의심 신호
- Sync 후 잠시
Synced였다가 다시OutOfSync로 돌아옴 - 특정 필드가 계속 되돌아감(예:
replicas,resources,annotations) - 운영자가
kubectl edit나 핫픽스로 바꾼 뒤부터 증상 시작
- Sync 후 잠시
드리프트를 빠르게 확인하려면 Argo CD의 diff를 봅니다.
# 실제 클러스터 상태와 Git 선언의 차이
argocd app diff myapp
# 특정 리소스만 보고 싶다면(예: Deployment)
argocd app diff myapp --resource apps:Deployment:default:api
diff에서 자주 보이는 패턴:
- HPA가
replicas를 덮어써서 Deployment 스펙과 충돌 - Admission Webhook이 특정 annotation/label을 강제로 추가/수정
- Mutating webhook이 이미지 태그를 바꾸거나, securityContext를 덧씌움
이 경우 “왜 바뀌는지”를 찾는 게 핵심이며, 다음 단계에서 원인을 파고듭니다.
3단계: 서버 사이드에서 실제 거부/변형을 확인하기
Git 선언이 맞는지 틀린지를 가르는 가장 빠른 방법은 서버 사이드 드라이런입니다. Argo CD가 실패하는 것과 동일하게, API 서버가 받아들이는지를 확인합니다.
# Kustomize/Helm 등으로 렌더링된 결과를 적용한다고 가정
# (예시는 manifest.yaml 이 이미 렌더링된 상태라고 가정)
kubectl apply -f manifest.yaml --dry-run=server
여기서 에러가 나면 Argo CD 문제가 아니라 쿠버네티스 검증/권한/리소스 스펙 문제입니다.
또한 “적용은 되는데 값이 바뀐다”면, 실제로 무엇이 변형했는지 추적해야 합니다.
# 적용 후 필드가 어떻게 저장되는지 확인
kubectl get deploy api -n default -o yaml
# 누가 바꿨는지 이벤트/감사 로그로 추적(클러스터 설정에 따라)
kubectl describe deploy api -n default
kubectl get events -n default --sort-by=.metadata.creationTimestamp
Mutating/Validating webhook이 원인인 경우가 많습니다. 해당 리소스에 붙는 webhook이 있는지 확인하세요.
kubectl get mutatingwebhookconfigurations
kubectl get validatingwebhookconfigurations
4단계: Argo CD가 관리하지 말아야 할 필드를 정리하기(드리프트 차단)
드리프트가 “정상적인 컨트롤러 동작” 때문에 발생하는 경우가 있습니다. 대표적으로 HPA가 replicas 를 바꾸는 것은 정상입니다. 이때는 Argo CD가 해당 필드를 굳이 되돌리려 하지 않도록 설정해야 합니다.
가장 흔한 해결은 ignoreDifferences 입니다.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
spec:
ignoreDifferences:
- group: apps
kind: Deployment
name: api
namespace: default
jsonPointers:
- /spec/replicas
또 다른 자주 쓰는 옵션은 RespectIgnoreDifferences 입니다. (버전에 따라 동작이 다를 수 있으니 사용 중인 Argo CD 버전 릴리즈 노트를 확인하세요.)
이 단계의 목표는 “계속 되돌아가는 필드”를 제거해 OutOfSync 루프를 끊는 것입니다.
5단계: 헬스체크가 틀린지, 리소스가 진짜 아픈지 구분하기
Synced 인데 Degraded 인 경우는 특히 헷갈립니다. 이때는 두 갈래로 나눠야 합니다.
- 리소스가 진짜로 준비되지 않음:
ReadinessProbe실패, 이미지 풀 실패, 크래시 루프 - Argo CD 헬스 판단이 보수적/부정확: CRD/커스텀 리소스의 health script 미정의, 혹은 상태 필드가 기대와 다름
먼저 “진짜로 아픈지”부터 확인하세요.
# 파드 상태
kubectl get pods -n default -l app=api
kubectl describe pod -n default -l app=api
# 최근 로그
kubectl logs -n default -l app=api --tail 200
# 디플로이 상태(ProgressDeadlineExceeded 등)
kubectl describe deploy api -n default
이미지 풀 문제라면 Argo CD 이전에 워크로드 자체가 뜨지 않습니다. EKS 환경에서는 ECR 인증/IRSA가 얽히는 경우가 많아, 아래 글의 체크리스트가 그대로 도움이 됩니다.
또한 ExternalSecret 같은 외부 의존 리소스가 준비되지 않아 앱 전체가 Degraded 로 보이는 경우도 많습니다.
반대로 리소스는 정상인데 Argo CD만 Degraded 라면, 해당 CRD의 상태 필드와 Argo CD의 health 로직이 맞지 않을 수 있습니다. 이 경우는 argocd 설정(ConfigMap)에서 리소스별 health customization을 고려합니다.
6단계: Sync 옵션과 적용 순서를 점검하기(훅/웨이브/CRD)
Sync 실패가 “순서 문제”인 경우가 생각보다 많습니다. 대표 패턴은 다음과 같습니다.
- CRD가 먼저 적용되지 않아 CR(커스텀 리소스) 생성이 실패
- Namespace/ServiceAccount/Role이 먼저 없어서 뒤 리소스가
Forbidden - Job 훅이 실패해서 전체 Sync가 실패 처리
Argo CD는 sync-wave 로 순서를 제어할 수 있습니다. 부등호 기호 대신 문자열로 예시를 들면, wave는 작은 값부터 먼저 적용됩니다.
apiVersion: v1
kind: Namespace
metadata:
name: platform
annotations:
argocd.argoproj.io/sync-wave: "0"
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: widgets.example.com
annotations:
argocd.argoproj.io/sync-wave: "1"
---
apiVersion: example.com/v1
kind: Widget
metadata:
name: my-widget
namespace: platform
annotations:
argocd.argoproj.io/sync-wave: "2"
또한 Sync Options 에서 자주 쓰는 것들:
CreateNamespace=truePruneLast=trueApplyOutOfSyncOnly=trueServerSideApply=true
특히 Server Side Apply는 필드 소유권(field manager) 충돌을 줄이기도 하지만, 기존 리소스가 다른 매니저에 의해 관리 중이면 오히려 충돌 메시지가 명확히 드러납니다. “원인을 숨기지 않고 드러내는” 방향으로 옵션을 선택하는 것이 디버깅에 유리합니다.
7단계: 권한(RBAC)과 프로젝트 경계를 확인하기
마지막으로, Sync 실패의 고전 원인인 권한 문제를 정리합니다. Argo CD는 크게 두 층의 권한이 얽힙니다.
- Argo CD 내부의
Project(AppProject)정책(허용된 클러스터/네임스페이스/리소스 종류) - 실제 클러스터 RBAC(Argo CD가 사용하는 ServiceAccount가 해당 리소스를 만들 권한이 있는지)
우선 AppProject 제한부터 확인합니다.
kubectl get appproject -n argocd
kubectl describe appproject default -n argocd
다음으로 클러스터 RBAC을 확인합니다. Argo CD가 어떤 SA로 동작하는지(클러스터 등록 방식에 따라 다름)를 확인한 뒤, can-i 로 검증합니다.
# 예: argocd-application-controller 가 사용하는 SA 기준으로 확인(환경에 맞게 수정)
kubectl auth can-i create deployments -n default --as=system:serviceaccount:argocd:argocd-application-controller
kubectl auth can-i patch services -n default --as=system:serviceaccount:argocd:argocd-application-controller
kubectl auth can-i create customresourcedefinitions --as=system:serviceaccount:argocd:argocd-application-controller
Forbidden 이라면 Sync는 당연히 실패합니다. 특히 CRD/ClusterRole/IngressClass 같은 클러스터 스코프 리소스는 네임스페이스 권한만으로는 불가능합니다.
자주 만나는 케이스 3가지와 빠른 결론
현장에서 반복되는 패턴을 결론 형태로 정리하면 다음과 같습니다.
- OutOfSync가 계속 돌아온다: 대부분 드리프트이며, HPA/웹훅/컨트롤러가 바꾸는 필드를
ignoreDifferences로 정리해야 합니다. - Synced인데 Degraded다: 파드/디플로이가 진짜 아픈지부터
describe,events,logs로 확인하고, 정상인데도 Degraded면 CRD health customization을 검토합니다. - SyncFailed가 특정 리소스에서 터진다:
kubectl apply --dry-run=server로 API 서버가 거부하는지 먼저 확인하고, 그 다음 순서(wave)와 RBAC을 봅니다.
마무리: “원인”을 찾는 순서가 곧 복구 시간이다
Argo CD Sync 실패는 단일 해결책이 아니라, 드리프트와 헬스라는 두 축에서 “어디가 다르고(드리프트), 무엇이 준비되지 않았는지(헬스)”를 빠르게 분리하는 게임에 가깝습니다. 위 7단계를 그대로 따라가면, UI에서 헤매지 않고 텍스트 기반으로 실패 지점을 고정한 뒤, 서버 검증과 diff로 원인을 좁혀 재현 가능한 형태로 해결할 수 있습니다.
운영 환경이 EKS라면 이미지 풀/IRSA, ExternalSecret 같은 외부 의존 리소스가 Argo CD 실패의 출발점이 되는 경우가 많으니, 앞서 링크한 체크리스트도 함께 점검해 보세요.