GitOps 와 ArgoCD · Application 과 동기화 · 이론
Application — 배포 자체를 오브젝트로 만든다
한 줄 요약
ArgoCD 의 Application 은 배포 스크립트가 아니라 "이 저장소의 이 경로가 저 클러스터의 저 네임스페이스여야 한다"는 선언이고, 그 선언 자체가 클러스터 안의 오브젝트다.
왜 이게 필요했나
배포 파이프라인을 CI 스크립트로 짜면 배포 설정이 파이프라인 정의 안에 갇힌다. 어떤 앱이 어느 네임스페이스로 가는지, 누가 그 앱을 배포할 수 있는지를 알려면 스크립트를 읽어야 하고, 그 스크립트는 클러스터 밖에 있다. 그래서 "이 클러스터에 배포된 앱 목록"을 클러스터에 물어볼 수 없다.
ArgoCD 는 배포 관계 자체를 커스텀 리소스로 만들어 이 문제를 없앤다. kubectl get application -n argocd 한 줄로 이 클러스터가 무엇을, 어디서, 어떤 정책으로 받아 오는지 전부 나온다. 배포가 오브젝트가 되면 RBAC, 감사 로그, 그리고 GitOps 자기 자신(Application 을 정의한 YAML 도 저장소에 커밋한다)까지 자연스럽게 붙는다.
어떻게 동작하나
Application 의 뼈대는 네 덩어리다.
| 필드 | 무엇을 정하나 |
| --- | --- |
| spec.source | 어디서 읽나 — repoURL, path, targetRevision |
| spec.destination | 어디에 넣나 — server(또는 name), namespace |
| spec.project | 어떤 경계 안에서 움직이나 — AppProject 이름 |
| spec.syncPolicy | 어떻게 맞추나 — 자동화·정리·재시도 |
syncPolicy.automated 에는 스위치가 둘 있다. prune 은 "저장소에서 지운 것을 클러스터에서도 지운다"이고, selfHeal 은 "클러스터가 저장소와 달라지면 저장소 쪽으로 되돌린다"이다. 둘 다 꺼 두면 ArgoCD 는 차이를 화면에 보여 주기만 하는 대시보드가 된다.
syncOptions 는 적용 방식의 세부다. CreateNamespace=true 는 대상 네임스페이스를 알아서 만들고, ServerSideApply=true 는 필드 소유권을 서버가 추적하게 해 여러 컨트롤러가 같은 오브젝트를 건드릴 때의 충돌을 줄인다. PruneLast=true 는 다른 리소스를 다 맞춘 뒤 마지막에 삭제하게 해 삭제 사고의 폭을 줄인다.
retry 는 실패를 어떻게 다시 시도할지다. limit, 그리고 backoff 의 duration·factor·maxDuration 으로 지수 백오프를 만든다. duration: 10s, factor: 2 면 10초, 20초, 40초로 벌어지고 maxDuration 에서 멈춘다. 같은 간격으로 무한히 재시도하면 API 서버를 때리는 부하 장치가 된다.
순서 제어 장치는 두 층이다. sync wave 는 argocd.argoproj.io/sync-wave 어노테이션의 숫자로 리소스를 그룹 짓고 작은 값부터 적용하며, 현재 웨이브가 Healthy 가 될 때까지 다음으로 넘어가지 않는다. 같은 웨이브 안에서는 리소스 종류별 기본 순서(Namespace → ConfigMap/Secret → RBAC → CRD → PV/PVC → Service → 워크로드 → Ingress)가 적용된다. 그 위에 훅(hook) 이 단계 자체를 나눈다 — PreSync → Sync → PostSync 이고 실패하면 SyncFail 이 돈다. 훅은 대개 Job 이며 argocd.argoproj.io/hook 어노테이션으로 지정하고, hook-delete-policy 로 뒷정리 시점을 정한다. 기본값은 BeforeHookCreation 이라 훅 리소스는 성공해도 남아 있다가 다음 sync 직전에 지워진다 — 실패한 마이그레이션 Job 의 로그를 사후에 볼 수 있는 이유가 이 기본값 덕분이다.
현장에서 만나는 모습
첫째, AppProject 는 사고 반경을 정하는 담장이다. sourceRepos, destinations, clusterResourceWhitelist, namespaceResourceBlacklist 로 "어느 저장소에서, 어느 네임스페이스로, 어떤 종류까지" 배포할 수 있는지를 못 박는다. sourceRepos: ["*"] 로 두면 프로젝트를 나눈 의미가 사라진다 — 오타 하나로 남의 저장소를 자기 클러스터에 배포하는 일이 가능해진다.
둘째, 무한 동기화. HPA 가 spec.replicas 를 바꾸는데 ArgoCD 가 그것을 드리프트로 보고 되돌리면, HPA 가 다시 바꾸고 ArgoCD 가 다시 되돌리는 루프가 생긴다. ignoreDifferences 로 /spec/replicas 를 비교 대상에서 빼야 멈춘다. 다른 컨트롤러가 소유한 필드는 GitOps 의 소유가 아니다는 원칙을 명시적으로 적어 주는 자리다.
셋째, 웨이브를 잘못 나누면 배포가 그 자리에 선다. 절대 Healthy 가 되지 않는 리소스를 앞 웨이브에 두면 뒤 웨이브는 영원히 오지 않는다. 웨이브는 "먼저 있어야 하는 것"에만 쓰고, 개수는 적게 유지하는 편이 안전하다.
다음 실습에서 할 것
ArgoCD 의 CRD 를 오프라인 번들에서 적용해 Application·AppProject 타입을 클러스터에 등록하고, argocd 네임스페이스에 Application web 을 직접 작성한다. 자동 동기화·정리·자가치유 스위치와 syncOptions·재시도 백오프를 채우고, 저장소 매니페스트에 sync wave 와 PreSync 훅을 붙인다. 그다음 AppProject platform 으로 경계를 그어 앱을 그 안에 소속시키고, 마지막에 클러스터에 실제로 존재하는 Application 목록과 일치하는 구성 보고서를 만든다. 이 환경에는 ArgoCD 컨트롤러가 없으므로 Application 이 스스로 Synced 가 되지는 않는다 — 채점 대상은 선언의 정확성이다.