본문으로 건너뛰기
Argo CD ApplicationSet을 App-of-Apps로 안전하게 전환하기

Argo CD ApplicationSet을 App-of-Apps로 안전하게 전환하기

2026년 4월 8일

list generator 기반 Argo CD ApplicationSet을 파일 단위 Application(App-of-Apps)으로 안전하게 전환하는 일반 절차.

언제?

  • ApplicationSet 템플릿으로 생성되던 자식 Application들을 개별 git 파일로 관리하고 싶을 때
  • StatefulSet/PVC 등 데이터 보존이 필요한 워크로드를 무중단으로 옮겨야 할 때

원칙

Caution

StatefulSet/PVC를 쓰는 차트는 잘못 전환하면 cascade prune으로 볼륨까지 삭제될 수 있다. 아래 3가지를 반드시 지킬 것.

  1. spec 일치: 새 Application의 name, namespace, source.path, helm.releaseName, destination, valueFiles가 기존 ApplicationSet 렌더 결과와 동일해야 Argo CD가 기존 helm release를 adopt (diff 0)
  2. orphan 선행: ApplicationSet이 element 삭제 시 자식 Application을 cascade 삭제하지 못하도록 ownerReferences를 먼저 제거
  3. 원자적 커밋: ApplicationSet elements 제거 + 새 Application yaml 추가를 같은 커밋으로 push (분리하면 reconcile 레이스 발생)

템플릿

원본 ApplicationSet template.spec을 그대로 복사해서 kind: ApplicationSetkind: Application으로 바꾸고 제너레이터 변수({{.app}}, {{.namespace}} 등)를 실제 값으로 치환한다.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: <generated-name>          # ApplicationSet 템플릿이 렌더했던 이름과 동일
  namespace: argocd
  labels: { ... }                 # ApplicationSet template.metadata에 있던 것 복사
  annotations: { ... }            # notifications 등 포함해서 그대로 복사
spec:
  project: <project>
  source:
    repoURL: <repo-url>
    targetRevision: <rev>
    path: <chart-path>            # 템플릿 렌더 결과와 동일
    helm:
      releaseName: <release>      # 템플릿 렌더 결과와 동일
      valueFiles: [ ... ]         # 템플릿 렌더 결과와 동일 (중복 항목도 그대로)
  destination: { ... }
  syncPolicy: { ... }

Tip

변환 전에 기존 자식 Application의 live spec을 덤프해서 그대로 yaml로 쓰는 게 가장 안전하다.

kubectl -n argocd get application <name> -o yaml \
  | yq 'del(.status, .metadata.creationTimestamp, .metadata.generation, .metadata.managedFields, .metadata.resourceVersion, .metadata.uid, .metadata.ownerReferences)' \
  > apps/.../<name>.yaml

전환 절차 (차트 1개 기준 — 반복 적용)

1. 대상 Application의 현재 상태 확인

kubectl -n argocd get application <name>
kubectl -n argocd get application <name> -o jsonpath='{.metadata.ownerReferences}{"\n"}'

Synced/Healthy인지, ownerReferences가 ApplicationSet을 가리키는지 확인.

2. orphan 패치 — ApplicationSet 소유권 끊기

kubectl -n argocd patch application <name> --type=json \
  -p='[{"op":"remove","path":"/metadata/ownerReferences"}]'

Note

이미 비어있으면 The request is invalid 에러가 나오지만 무시해도 된다.

# 확인
kubectl -n argocd get application <name> -o jsonpath='{.metadata.ownerReferences}{"\n"}'

3. git 변경 (같은 커밋)

  • ApplicationSet yaml의 generators.list.elements에서 대상 블록 제거
  • <name>.yaml 파일 추가 (위 템플릿)
git add <appset-file> <new-app-file>
git commit -m "chore: migrate <name> from ApplicationSet to Application"
git push

Warning

따로 push하면 ApplicationSet reconcile 타이밍에 따라 ownerReferences가 재부착되거나 cascade 삭제가 트리거될 수 있다.

4. sync 순서

root (app-of-apps) → 자식 순.

  1. root Application sync → ApplicationSet 업데이트 + 새 자식 Application 리소스 생성
  2. 새 자식 Application이 automated sync로 알아서 돌거나, 수동이면 그다음에 sync

5. 검증

argocd app get <name> | grep -E 'Health Status|Sync Status'
kubectl -n <ns> get sts,pvc      # StatefulSet/PVC 건재 확인

Synced/Healthy + PVC 보존되면 성공.

전부 옮긴 후 ApplicationSet 제거

모든 element를 옮겨 ApplicationSet이 비었을 때:

# git
rm <appset-file>
git add <appset-file>
git commit -m "chore: remove empty ApplicationSet"
git push

# 클러스터 (cascade=orphan 필수)
kubectl -n argocd delete applicationset <appset-name> --cascade=orphan

Caution

--cascade=orphan을 빼면 자식 Application들이 같이 삭제되고 → prune: true 설정에 따라 워크로드/PVC가 소실될 수 있다.

롤백

  1. <name>.yaml 삭제
  2. ApplicationSet의 elements에 해당 블록 복원
  3. push

Note

ownerReferences가 비어있어도 이름이 일치하면 ApplicationSet 컨트롤러가 재흡수한다.

자주 하는 실수

실수 결과
orphan 없이 ApplicationSet element 제거 cascade 삭제 가능성
element 제거와 새 파일 push를 다른 커밋으로 분리 reconcile 레이스
releaseName / namespace / path 중 하나라도 기존과 다름 adoption 실패 → 새 helm release 생성 → 충돌
valueFiles 중복 나열 정리를 동시에 진행 spec diff 발생 → 재싱크 1회 (adoption 후 별도 커밋에서 정리)
ApplicationSet 삭제 시 --cascade=orphan 누락 자식 Application + 워크로드 소실