IL-0058 Argo CD users · 2026-07-08 (게시일)kubernetesgcp
이 사례는 Argo CD v3.3.9 를 GKE 1.30.x 에서 쓰는 사용자가 2026-07-08 에 올린 GitHub 이슈다. application-controller 는 샤딩을 켠 Deployment 로 배포돼 복제본 여러 개가 돌고 있었다. application-controller 를 롤링 재시작한 뒤 여러 앱이 함께 쓰는 네임스페이스가 지워져 Terminating 상태가 됐고, 앱 약 97개가 Missing/OutOfSync 에서 빠져나오지 못했다.
보고자는 처음에 원인을 재시작 직후의 캐시 문제로 봤다. 컨트롤러가 클러스터 캐시의 EnsureSynced() 초기 LIST 를 끝내기 전에 동기화를 처리했고, 불완전한 캐시 때문에 공유 네임스페이스가 어느 앱의 것인지 잘못 판단했다는 설명이었다. 삭제 경로로는 두 가지를 들었다. 하나는 prune: true 인 자동 동기화가 네임스페이스를 고아로 보고 지운 경로다. 다른 하나는 UI 에서 force: true 로 일괄 수동 동기화를 하면서 tracking-ID 충돌이 난 네임스페이스에 kubectl replace --force 를 실행한 경로다.
다른 사용자 ivomarino 가 컨트롤러와 gitops-engine 코드를 읽고 다른 해석을 내놓았다. autoSync 는 앱이 이미 OutOfSync 일 때만 동작하고, 라이브 상태를 읽지 못하면 CompareAppState 가 앱을 OutOfSync 가 아니라 Unknown 으로 둔다. EnsureSynced() 는 캐시를 비우고 다시 채우는 동안 내내 c.lock 을 잡고 있어서, 읽는 쪽이 반쯤 채워진 캐시를 볼 수 없다. 따라서 첫 번째 경로는 막혀 있다. 수동 동기화에는 OutOfSync 검사가 없으므로, force 동기화와 tracking-ID 충돌이 겹치면 Argo CD 는 대상 앱의 Prune=false 와 상관없이 리소스를 강제로 교체한다. 그는 이 문제가 재시작과 상관없이 언제든 재현될 수 있다고 봤고, 직접 재현해 보지는 않았다고 밝혔다.
보고자는 이 분석이 맞다고 인정했다. 두 앱이 같은 리소스를 선언하고 그 리소스를 소유하지 않은 쪽을 force 동기화하면 언제든 재현된다는 결론이었다. 보고자는 이 이슈를 닫고 원인을 정확히 적은 #29739 를 새로 열었다.
application-controller 를 롤링 재시작한 뒤, 여러 앱이 함께 쓰는 네임스페이스가 Terminating 상태가 됐다. 이 네임스페이스를 쓰는 앱 약 97개가 Missing/OutOfSync 상태에서 빠져나오지 못했다.
컨트롤러 로그에는 msg="Detected changes to resource Namespace/foo which is currently being deleted" 경고가 남았다. 재시작 약 3분 뒤에는 ReplicaSet.apps 목록 조회에서 "Failed to list *unstructured.Unstructured using watch: very old resource version: ... continue parameter too old" 경고가 나왔다.
보고자는 처음에 클러스터 캐시의 초기 LIST 가 끝나기 전에 동기화가 돌았다고 봤고, continue parameter too old 경고를 그 증거로 들었다. 보고자는 삭제 경로로 두 가지를 의심했다. 하나는 prune: true 인 자동 동기화가 재시작 직후 다시 돌면서 네임스페이스를 고아로 보고 지운 경로다. 다른 하나는 UI 에서 force: true 로 일괄 수동 동기화를 하면서 tracking-ID 충돌이 난 네임스페이스에 kubectl replace --force 를 실행한 경로다.
argocd.argoproj.io/sync-options: Prune=false 설정이 있었지만 두 번째 경로를 막지 못했다. 이 설정은 prune 경로만 보호하고 force replace 는 막지 않는다. 컨트롤러 로그의 initiatedBy 필드로 수동 동기화가 삭제를 일으켰다는 것을 확인했다.
증상 ? 리소스 삭제
–
–
argocd.argoproj.io/sync-options: Prune=false 는 prune 경로만 막고, force replace 로 일어나는 삭제는 막지 않는다. 수동 동기화는 자동 동기화와 달리 OutOfSync 검사를 거치지 않으므로 언제든 실행된다. 여러 앱이 같은 클러스터 범위 리소스를 선언하면 tracking-ID 충돌이 생기고, 소유하지 않은 앱을 force 동기화하면 그 리소스가 지워졌다가 다시 만들어질 수 있다. 처음 세운 재시작과 캐시 가설은 코드를 읽어 보니 맞지 않았다.
같은 원인 · 공통 Argo CD