Published on

Argo CD Sync 실패 comparisonError 원인·해결

Authors

서로 다른 환경에서 Argo CD를 운영하다 보면 Sync failed의 원인이 실제 Apply 실패가 아니라, 그 이전 단계인 비교(Comparison/Diff) 단계에서 터지는 경우가 많습니다. 이때 UI에는 흔히 comparisonError로 뭉뚱그려 표시되는데, 메시지가 짧거나 “failed to load live state” 같은 형태로만 보여서 원인 파악이 어렵습니다.

이 글에서는 comparisonError가 무엇인지(어느 단계에서 발생하는지), 로그/CLI로 원인을 좁히는 방법, 그리고 현장에서 자주 만나는 원인별 해결책을 정리합니다.

comparisonError란 무엇인가

Argo CD의 기본 흐름을 단순화하면 다음과 같습니다.

  1. Git에서 Desired 상태(매니페스트)를 가져옴
  2. 매니페스트를 렌더링(Helm/Kustomize/플러그인)
  3. 클러스터에서 Live 상태를 조회(K8s API)
  4. Desired vs Live를 비교(Diff)
  5. Sync 시 Apply

comparisonError는 대체로 2~4 단계(렌더링/라이브 조회/비교)에서 문제가 생겼다는 의미입니다. 즉, “적용하다가 실패”라기보다 “적용하기 전에 비교할 수 없어서 실패”에 가깝습니다.

특히 다음 상황에서 자주 발생합니다.

  • Helm/Kustomize 렌더링 실패(차트 의존성, values, 플러그인)
  • 클러스터 Live 리소스 조회 실패(RBAC, API 오류)
  • CRD/스키마 불일치로 Diff가 실패
  • Admission Webhook이 Live 조회/서버 사이드 디프 과정에 간섭
  • Argo CD repo-server / application-controller 네트워크 문제

가장 먼저 확인할 것: UI 이벤트 vs 컨트롤러 로그

UI의 애플리케이션 상세 화면에서 Conditions 또는 EventsComparisonError가 찍힙니다. 하지만 핵심 원인은 application-controller 또는 repo-server 로그에 있습니다.

  • 렌더링/리포지토리 접근/플러그인 문제: argocd-repo-server 로그
  • 라이브 상태 조회/클러스터 접근/RBAC 문제: argocd-application-controller 로그

아래처럼 네임스페이스를 확인한 뒤 로그를 봅니다.

# Argo CD가 설치된 네임스페이스 예: argocd
kubectl -n argocd get pods

# repo-server 로그
kubectl -n argocd logs deploy/argocd-repo-server -f --tail=200

# application-controller 로그
kubectl -n argocd logs deploy/argocd-application-controller -f --tail=200

이때 로그에 자주 보이는 패턴은 다음과 같습니다.

  • rpc error: code = Unknown desc = ... (repo-server gRPC/렌더링)
  • failed to load live state (클러스터 조회/RBAC)
  • the server could not find the requested resource (CRD 미설치/버전 불일치)
  • x509: certificate signed by unknown authority (TLS/프록시)

원인 1) Helm/Kustomize 렌더링 실패

증상

  • UI에는 comparisonError
  • repo-server 로그에 Helm 템플릿 에러가 찍힘

예시 로그/메시지:

  • Error: failed to download ... chart (의존성 다운로드 실패)
  • YAML parse error / template: ... (템플릿 문법)
  • cannot load values (values 파일 경로)

해결 체크리스트

  1. 차트 의존성(Chart.lock/Chart.yaml)과 repo-server 네트워크
  • Argo CD Helm 렌더링은 repo-server에서 수행됩니다.
  • 사내망/프록시 환경이면 repo-server가 외부 Helm repo에 접근하지 못해 비교 단계부터 실패합니다.
  1. Helm dependency를 Git에 vendor
  • 외부 접근이 불안정하면 charts/ 디렉터리에 의존성을 포함하거나, OCI 레지스트리 사용을 검토합니다.
  1. Kustomize remote base / Helm chart remote 참조 제한
  • 보안 설정에 따라 remote base를 막아두면 렌더링이 실패할 수 있습니다.

빠른 재현(로컬에서 동일 렌더링)

# Helm 렌더링 재현
helm template myapp ./chart -f values.yaml --namespace myns

# Kustomize 렌더링 재현
kustomize build overlays/prod

로컬에서는 되는데 Argo CD에서만 실패하면, repo-server 환경(네트워크/DNS/CA/프록시/플러그인 바이너리) 차이를 의심해야 합니다.

원인 2) 클러스터 Live 상태 조회 실패(RBAC)

증상

  • application-controller 로그에 forbidden 또는 cannot list resource
  • UI에는 failed to load live state

Argo CD는 비교를 위해 대상 네임스페이스의 리소스를 get/list/watch 해야 합니다. RBAC이 부족하면 Apply 이전에 비교 단계에서 막힙니다.

확인 방법

Argo CD가 사용하는 ServiceAccount(설치 방식에 따라 다름)가 무엇인지 확인합니다.

kubectl -n argocd get sa
kubectl -n argocd describe deploy argocd-application-controller | sed -n '/Service Account/,$p'

그리고 해당 SA가 대상 네임스페이스 리소스를 읽을 수 있는지 kubectl auth can-i로 확인합니다.

SA="system:serviceaccount:argocd:argocd-application-controller"

kubectl auth can-i list deployments.apps -n myns --as="$SA"
kubectl auth can-i list secrets -n myns --as="$SA"
kubectl auth can-i get configmaps -n myns --as="$SA"

해결

  • 최소 권한 원칙을 유지하되, Argo CD가 관리하는 리소스 종류에 대해 get/list/watch 권한을 부여합니다.
  • 클러스터 스코프 리소스(CRD, ClusterRole, Namespace 등)를 관리한다면 clusterrole 권한이 필요합니다.

원인 3) CRD 미설치/버전 불일치로 인한 비교 실패

증상

  • the server could not find the requested resource
  • 특정 CR(Custom Resource)만 비교에서 실패

예: Git에는 Foo CR이 있는데, 클러스터에는 Foo의 CRD가 없거나 apiVersion이 다릅니다.

진단

# CRD 존재 여부
kubectl get crd | grep -i foo

# 특정 리소스 조회
kubectl api-resources | grep -i foo

해결

  • CRD를 먼저 설치하도록 앱을 분리하거나(예: crds 앱 → workloads 앱), sync wave를 사용해 CRD가 먼저 적용되게 합니다.
  • Helm 차트의 crds/ 디렉터리를 사용하는 경우, Argo CD에서 CRD 적용 순서가 기대와 다를 수 있으니 운영 정책에 맞게 분리하는 편이 안전합니다.

원인 4) Admission Webhook/OPA/Gatekeeper/Kyverno로 인한 부작용

증상

  • Apply가 아니라 비교 단계에서 에러가 나는데, 로그에 웹훅 타임아웃/거부가 섞여 있음
  • context deadline exceeded 또는 failed calling webhook 형태

일부 웹훅은 특정 리소스 조회/패치/서버사이드 적용 경로에서 예상치 못한 영향을 줄 수 있습니다. 특히 웹훅 엔드포인트 장애나 네트워크 정책으로 접근이 막히면, K8s API가 웹훅 호출에서 지연되어 Argo CD의 비교가 실패할 수 있습니다.

해결

  • 웹훅 서비스 엔드포인트/헬스 확인
  • failurePolicy: Ignore 적용 여부 검토(보안 정책에 따라 신중)
  • 타임아웃/네트워크 정책/보안그룹 점검

클러스터 네트워크 이슈가 의심될 때는 egress 관점 점검도 도움이 됩니다. 특히 EKS 환경에서 “Pod는 정상인데 외부 통신만 막힘” 같은 케이스는 비교 단계에서 repo-server가 의존성 다운로드를 못 하거나, 컨트롤러가 API/웹훅과 통신이 꼬이면서 comparisonError로 보이기도 합니다. 관련 점검 흐름은 EKS에서 Pod는 정상인데 egress만 막힐 때 점검을 참고하세요.

원인 5) repo-server 플러그인/커스텀 툴 체인 문제

증상

  • CMP(Config Management Plugin) 사용 시 plugin sidecar/exec 실패
  • permission denied, no such file or directory, exec format error

플러그인은 repo-server에서 실행되므로 다음이 중요합니다.

  • 바이너리 아키텍처(amd64/arm64) 일치
  • 실행 권한/파일 경로
  • 컨테이너 보안 컨텍스트(readOnlyRootFilesystem 등)
  • 플러그인이 호출하는 외부 도구(예: yq, helm, kustomize) 존재 여부

해결

  • repo-server 이미지에 필요한 바이너리를 bake 하거나, sidecar에 넣고 경로를 명확히 지정
  • securityContext 때문에 실행이 막히는지 확인
kubectl -n argocd describe pod -l app.kubernetes.io/name=argocd-repo-server | sed -n '/Containers:/,$p'

원인 6) 리소스가 너무 커서 Diff/조회가 실패(Timeout/메모리)

대규모 CR(거대한 ConfigMap, 큰 CRD 스펙, 수천 개 리소스)에서 비교 단계가 느려지거나 repo-server/application-controller가 리소스 부족으로 불안정해질 수 있습니다.

증상

  • context deadline exceeded
  • 컨트롤러/리포서버 재시작

이 경우에는 OOM이나 리소스 누수로 이어지기도 하므로, 컨테이너 메모리/CPU 제한과 실제 사용량을 확인하세요. OOM 진단 흐름은 Kubernetes OOMKilled 진단과 메모리 누수 추적 실전이 도움이 됩니다.

실전 트러블슈팅 절차(빠르게 원인 좁히기)

1) 어떤 컴포넌트 문제인지 분리

  • repo-server 로그에 에러가 있다 → 렌더링/레포 접근/플러그인
  • application-controller 로그에 forbidden/live state → RBAC/클러스터 접근

2) Argo CD CLI로 상태/메시지 확인

argocd app get myapp
argocd app diff myapp
argocd app logs myapp --since 10m  # 환경에 따라 지원/권한 필요

app diff가 실패하면 비교 단계 문제 확률이 매우 높습니다.

3) “특정 리소스만” 문제인지 확인

  • 한 리소스 타입(CR)에서만 터지면 CRD/웹훅/스키마
  • 특정 네임스페이스만 터지면 RBAC/네임스페이스 정책
  • 특정 환경(prod만)에서 터지면 네트워크/프록시/인증서

자주 쓰는 해결 패턴 모음

패턴 A: CRD 먼저, 워크로드 나중

  • argocd app을 2개로 분리
    • platform-crds
    • platform-workloads
  • 또는 sync wave/depends-on을 활용

패턴 B: RBAC을 “읽기” 중심으로 먼저 맞추기

비교 단계에서 필요한 건 기본적으로 get/list/watch입니다. Apply 권한보다 먼저 읽기 권한이 맞는지 확인하세요.

패턴 C: repo-server 네트워크/CA/프록시 표준화

  • 사내 CA를 repo-server에 주입
  • outbound 정책(네트워크 정책/보안그룹/NAT) 확인
  • Helm repo/OCI registry 접근 경로를 고정

패턴 D: 웹훅 장애가 전체 배포를 막지 않게 설계

  • 웹훅 timeoutSeconds 조정
  • 장애 시 영향 범위를 줄이기 위한 failurePolicy 검토
  • 웹훅 서비스의 HPA/리소스/네트워크 정책 점검

비교 오류를 줄이는 운영 팁

  • App-of-Apps 구조에서는 CRD/클러스터 공통 리소스를 별도 앱으로 분리
  • 큰 리소스(대형 ConfigMap/CR)는 분할하거나 생성 방식을 재검토
  • repo-server/application-controller에 적절한 리소스 요청/제한을 설정
  • Argo CD 업그레이드 시 diff/health 관련 변경사항 릴리즈 노트 확인

마무리

comparisonError는 “Sync가 실패했다”는 결과만 보여주지만, 실제로는 렌더링(repo-server) 또는 라이브 상태 조회(application-controller) 단계에서 실패한 경우가 대부분입니다. 따라서 문제를 빠르게 푸는 핵심은 (1) 에러가 어느 컴포넌트에서 났는지 분리하고, (2) RBAC/CRD/웹훅/네트워크/플러그인 중 어디가 병목인지 로그로 좁히는 것입니다.

운영 환경이 EKS이고 네트워크 이슈 가능성이 있다면 egress 경로부터 점검해보는 것도 시간을 크게 줄여줍니다. 또한 비교 단계에서의 타임아웃/재시작이 보이면 리소스 부족(OOM)까지 함께 의심해보세요.