ArgoCD Synced/OutOfSync 반복 - 고아 리소스 충돌
App 이름 변경 후 고아 cert-controller가 동일한 ValidatingWebhookConfiguration에 경쟁적으로 CA를 주입하며 ArgoCD가 Synced/OutOfSync를 반복했다. 고아 Controller를 삭제하여 해결했다.
환경: ArgoCD 2.x, EKS, external-secrets Helm chart 날짜: 2026-03-02
상황
external-secrets ArgoCD App 이름에 서비스별 prefix를 추가하는 작업을 하고 있었다(external-secrets → svc-a-external-secrets). 이름을 바꾸고 Sync를 완료했는데, App이 ArgoCD UI에서 0.5초마다 Synced/OutOfSync를 반복하는 깜박임이 발생했다.
처음엔 단순한 Sync 오류라고 생각했지만, 같은 작업을 한 svc-b-external-secrets는 정상이었다. 같은 방식으로 작업했는데 한쪽만 문제라는 건, 작업 자체가 아니라 클러스터 상태에 차이가 있다는 뜻이었다.
관찰한 사실
Synced/OutOfSync를 반복한다는 건, ArgoCD가 Git의 desired state와 클러스터의 actual state를 비교할 때마다 차이가 생겼다 사라졌다를 반복한다는 뜻이다. 무엇이 OutOfSync인지 먼저 특정해야 했다.
kubectl get application svc-a-external-secrets -o json \
| jq '.status.resources[] | select(.status != "Synced")'ValidatingWebhookConfiguration/secretstore-validate가 OutOfSync였다. 이 리소스는 cert-controller가 CA 인증서를 주입해서 관리하는 webhook 설정이다. 이 값이 깜박인다는 건, CA 인증서 값이 계속 바뀌고 있다는 뜻이었다.
가설과 검증 과정
가설: cert-controller가 하나 이상 실행되고 있다
CA 값이 계속 바뀌려면, 누군가가 반복적으로 값을 덮어쓰고 있어야 한다. cert-controller가 하나뿐이라면 CA를 한 번 설정하고 안정될 텐데, 두 개 이상이라면 서로 다른 CA를 번갈아 주입하면서 무한 루프가 생길 수 있다고 생각했다.
클러스터의 Deployment를 확인해보니 의심이 맞았다.
kubectl -n external-secrets get deploy두 세트가 공존하고 있었다: svc-a-external-secrets-*(새 App이 만든 것)와 external-secrets-*(기존 고아). App 이름 변경 전의 리소스가 삭제되지 않고 그대로 남아 있었던 것이다.
cert-controller 로그를 확인하니 결정적인 증거가 나왔다.
kubectl logs svc-a-external-secrets-cert-controller --tail=10같은 초 내에 secretstore-validate를 여러 번 업데이트하는 로그가 찍히고 있었고, 주입되는 CA 값이 매번 달랐다. 두 controller가 경쟁하고 있다는 직접 증거였다.
결과: 채택. 두 cert-controller가 동일한 ValidatingWebhookConfiguration에 서로 다른 CA를 경쟁적으로 주입하면서 무한 루프가 발생한 것이다.
sequenceDiagram participant A as svc-a-cert-controller participant W as secretstore-validate participant B as cert-controller (고아) A->>W: CA-A 주입 B->>W: CA 다름 감지 → CA-B 주입 A->>W: CA 다름 감지 → CA-A 주입 Note over A,B: 무한 반복 → ArgoCD가 변경 감지 → Synced/OutOfSync 깜박임
왜 svc-b에서는 문제가 없었는가?
같은 작업을 한 클러스터 B에서는 깜박임이 없었다. 확인해보니 클러스터 B에는 기존 external-secrets-* Deployment가 없었다. 클러스터 A에서만 기존 리소스가 49일째 운영 중이었고, 새 App 배포 시 이 리소스가 삭제되지 않아 두 세트가 공존하게 된 것이다.
이 비교를 통해 “App 이름 변경”이라는 동작이 기존 리소스를 자동으로 정리하지 않는다는 점이 확정됐다.
근본 원인
ArgoCD App 이름을 변경하면 기존 리소스가 자동 삭제되지 않는다. App 이름에 prefix를 추가하자 ArgoCD는 새 App과 새 리소스(svc-a-external-secrets-*)를 생성했지만, 기존 리소스(external-secrets-*)는 관리 주체를 잃고 고아로 남았다. 두 cert-controller가 공존하면서 동일한 WebhookConfiguration에 서로 다른 CA를 경쟁적으로 주입하는 무한 루프가 만들어졌다.
해결 방법
고아 리소스(기존 external-secrets-*)를 삭제하면 경쟁이 해소될 거라고 판단했다.
# 고아 Deployment 삭제
kubectl -n external-secrets delete deploy \
external-secrets \
external-secrets-cert-controller \
external-secrets-webhook
# 연관 리소스 정리 (SA, Service, Secret, ClusterRole, ClusterRoleBinding)
kubectl -n external-secrets delete sa \
external-secrets external-secrets-cert-controller external-secrets-webhook
kubectl -n external-secrets delete svc external-secrets-webhook
kubectl -n external-secrets delete secret external-secrets-webhook
kubectl delete clusterrole \
external-secrets-cert-controller external-secrets-controller \
external-secrets-edit external-secrets-servicebindings external-secrets-view
kubectl delete clusterrolebinding \
external-secrets-cert-controller external-secrets-controller삭제 후 App을 Sync하니 깜박임이 즉시 사라졌다.
NAME READY STATUS
svc-a-external-secrets-86bd84957d-5frdx 1/1 Running
svc-a-external-secrets-cert-controller-77b9bf84cf-wm6kd 1/1 Running
svc-a-external-secrets-webhook-6d887f5bfc-6f5kw 1/1 Running
Sync: Synced, Health: Healthy
교훈
- ArgoCD App 이름 변경 시에는 기존 리소스를 먼저 정리해라. App 이름을 바꾸면 ArgoCD는 새 App을 만들 뿐, 기존 리소스는 관리 주체를 잃고 고아가 된다. 이름 변경 전에 기존 App을 Non-Cascading으로 삭제하고, 새 이름으로 재생성해야 한다.
- 같은 리소스를 관리하는 Controller가 2개 이상이면 반드시 경쟁이 발생한다. 특히 cert-controller처럼 주기적으로 리소스를 업데이트하는 Controller는 고아로 남는 즉시 무한 루프를 만든다.
- Synced/OutOfSync 깜박임은 외부에서 리소스가 지속적으로 변경되고 있다는 신호다.
kubectl get application -o json으로 OutOfSync 리소스를 먼저 특정하고, 그 리소스를 누가 변경하고 있는지 추적해라.