| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- 리눅스
- 네트워크
- 파이썬
- 부트캠프회고
- 네트워크면접
- 코테
- 프로펙트
- ApplicationSet
- PROPECT
- 리눅스기초
- 네트워크기초
- 위니브
- ArgoCD배포
- 구름프로펙트
- 네트워크정리
- 개발자
- Python
- 코딩테스트
- 컴퓨터네트워크
- 제주ICT
- javascript
- 백준
- Linux
- 네트워크핵심정리
- 제주코딩베이스캠프
- 프로펙트부트캠프
- 더오름
- 코테준비
- 자료구조
- 터미널명령어
- Today
- Total
hyei-devlog
[CICD] ArgoCD 배포 구조 개선: Flat 방식에서 App of Apps 패턴으로 전환, 개별 관리에서 계층적 관리로 본문
[CICD] ArgoCD 배포 구조 개선: Flat 방식에서 App of Apps 패턴으로 전환, 개별 관리에서 계층적 관리로
winter126 2025. 12. 23. 10:21ArgoCD를 도입한 후 가장 먼저 마주하는 질문이 있다.
"수십 개의 마이크로서비스를 어떻게 체계적으로 관리할 것인가?"
이 글에서는 세 가지 주요 배포 패턴을 실무 관점에서 비교하고, 각 패턴이 빛을 발하는 상황을 살펴본다.

1. Flat Application 구조: 단순함의 미학
Flat 구조는 가장 직관적인 방식이다.
각 서비스마다 독립적인 ArgoCD Application YAML 파일을 생성하고, 이를 개별적으로 ArgoCD에 등록하는 방식이다.
Flat 구조는 Root YAML 없이 서비스 하나마다 Application YAML을 둔다.
apps/
├─ service-a.yaml
├─ service-b.yaml
└─ service-c.yaml
이 구조의 핵심은 Application 간에 어떠한 계층 관계도 존재하지 않는다는 점이다.
각 Application은 완전히 독립적으로 동작하며, 상위 Application이 이들을 관리하지 않는다.
Q. "App of Apps와 뭐가 다른가요? Application들이 Git에 모여 있으니까 App of Apps 아닌가요?"
Git 저장소에 여러 Application YAML이 함께 있다고 해서 App of Apps 패턴이 되는 것은 아니다.
App of Apps는 부모 Application이 자식 Application들을 소스로 관리할 때만 성립한다.
Flat 구조는 그저 관리의 편의를 위해 파일들을 같은 디렉토리에 모아둔 것일 뿐이다.
- Git에 같이 있음 ≠ App of Apps
- 부모 Application이 다른 Application을 source로 관리해야 App of Apps
언제 사용해야 하는가?
Flat 구조는 초기 단계나 소규모 프로젝트에 적합하다. 서비스가 5-10개 정도이고, 각 서비스가 고유한 설정을 가지고 있다면 이 방식이 오히려 명확하다. ArgoCD를 처음 도입하는 팀이라면, 복잡한 계층 구조보다 이해하기 쉬운 Flat 구조로 시작하는 것이 현명한 선택이다.
하지만 서비스가 늘어나면서 문제가 드러난다. 새로운 서비스를 추가할 때마다 ArgoCD UI에 접속해서 수동으로 등록해야 하고, 공통 설정을 변경하려면 모든 YAML 파일을 일일이 수정해야 한다. 이 지점에서 우리는 더 나은 패턴을 고민하게 된다.
2. App of Apps 패턴: 계층으로 복잡도 정복하기
App of Apps는 ArgoCD의 자기 참조 특성을 활용한 우아한 패턴이다.
하나의 루트 Application이 여러 하위 Application manifest들을 관리하는 계층 구조를 만드는 것이다.
App of Apps는 하나의 상위 ArgoCD Application이 여러 하위 Application manifest들을 관리(생성/동기화)하는 계층적 구조이다.
root-app (Application)
└─ apps/
├─ service-a.yaml (Application)
├─ service-b.yaml (Application)
└─ service-c.yaml (Application)
이 방식의 핵심은 ArgoCD에 루트 Application 하나만 등록하면, 나머지 Application들은 자동으로 생성된다는 점이다.
루트 Application은 Git 저장소의 apps/ 디렉토리를 바라보며, 이 디렉토리에 새로운 Application YAML이 추가되면 자동으로 감지하여 배포한다.
아키텍처
root-application (ArgoCD App)
↓ (watches)
apps/ directory in Git
├── backend-app.yaml
├── frontend-app.yaml
├── database-app.yaml
└── monitoring-app.yaml
↓ (each deploys)
Actual Kubernetes Resources
- root-app의 source.path → apps/
- ArgoCD는 root-app 하나만 등록
- 나머지 Application들은 root-app이 생성해 줌
- 계층(부모-자식) 관계가 명확함
구현 예시
루트 Application은 다음과 같이 정의된다:
# root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/gitops-repo
targetRevision: main
path: apps # 이 디렉토리의 모든 Application을 관리
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
그리고 각 하위 Application은 표준적인 ArgoCD Application 정의를 따른다:
# apps/backend-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: backend-app
namespace: argocd
spec:
project: default
source:
repoURL: <https://github.com/myorg/backend>
targetRevision: main
path: k8s/overlays/production
destination:
server: <https://kubernetes.default.svc>
namespace: backend
syncPolicy:
automated:
prune: true
selfHeal: true
계층 구조의 실전 활용
실무에서는 인프라, 플랫폼, 서비스라는 세 가지 레이어로 구조화하는 것이 효과적이라고 한다:
apps/
├─ infrastructure/
│ ├─ istio.yaml
│ ├─ ingress-nginx.yaml
│ └─ cert-manager.yaml
├─ platform/
│ ├─ monitoring.yaml
│ └─ logging.yaml
└─ services/
├─ backend-api.yaml
└─ frontend-web.yaml
이렇게 구성하면 배포 순서를 제어할 수 있다.
Istio와 Ingress Controller가 먼저 배포되고, 그다음 모니터링 스택이, 마지막으로 실제 서비스들이 배포되도록 순서를 보장할 수 있다. ArgoCD의 Sync Waves 기능과 결합하면 더욱 정교한 제어가 가능하다.
App of Apps의 장단점
이 패턴의 가장 큰 장점은 직관성과 제어의 균형이다. Git에 YAML 파일만 추가하면 자동으로 배포되므로 운영이 편리하고, 동시에 각 Application을 독립적으로 설정할 수 있어 유연성도 확보된다. 문제가 발생했을 때도 해당 Application의 YAML 파일을 직접 확인하면 되므로 디버깅이 쉽다.
하지만 규모가 커지면 한계가 보인다. 20개 이상의 서비스를 관리하게 되면 YAML 파일들이 급증하고, 유사한 설정이 여러 파일에 반복된다. dev, staging, production 환경이 추가되면 상황은 더욱 복잡해진다. 이 지점에서 우리는 ApplicationSet을 고려하게 된다.
3. ApplicationSet 패턴: 자동화의 정점
ApplicationSet은 ArgoCD의 가장 강력한 기능이다. 템플릿과 Generator를 사용하여 수십, 수백 개의 Application을 동적으로 생성한다. 하나의 ApplicationSet 정의만으로 다양한 환경과 클러스터에 일관되게 배포할 수 있다.
ApplicationSet은 템플릿과 Generator를 사용하여 여러 ArgoCD Application을 동적으로 생성하는 CRD이다.
ApplicationSet (single resource)
↓ (generates via template)
Multiple Applications (auto-created)
├── app-dev
├── app-staging
└── app-production
↓ (each deploys)
Kubernetes Resources per environment
Generator의 이해
ApplicationSet의 핵심은 Generator다. Generator는 Application을 생성하기 위한 메타데이터를 제공한다.
가장 많이 사용되는 Generator는 다음과 같다:
- List Generator: 명시적 리스트 기반
- Cluster Generator: 등록된 클러스터 기반
- Git Generator: Git 디렉토리/파일 구조 기반
- Matrix Generator: 여러 Generator 조합
List Generator는 명시적인 리스트를 기반으로 Application을 생성한다.
dev, staging, production 환경에 동일한 애플리케이션을 배포할 때 유용하다:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: microservices-appset
namespace: argocd
spec:
generators:
- list:
elements:
- env: dev
cluster:
replicas: "2"
- env: staging
cluster:
replicas: "3"
- env: production
cluster:
replicas: "5"
template:
metadata:
name: 'backend-{{env}}'
spec:
project: default
source:
repoURL: <https://github.com/myorg/backend>
targetRevision: main
path: 'k8s/overlays/{{env}}'
helm:
parameters:
- name: replicaCount
value: '{{replicas}}'
destination:
server: '{{cluster}}'
namespace: 'backend-{{env}}'
syncPolicy:
automated:
prune: true
selfHeal: true
이 하나의 정의로 dev, staging, production 환경에 각각 다른 설정으로 backend Application이 자동 생성된다.
Git Directory Generator는 Git 저장소의 디렉토리 구조를 기반으로 Application을 생성한다.
마이크로서비스 아키텍처에서 특히 강력하다.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: all-microservices
namespace: argocd
spec:
generators:
- git:
repoURL: <https://github.com/myorg/microservices>
revision: main
directories:
- path: services/*
template:
metadata:
name: '{{path.basename}}'
spec:
project: default
source:
repoURL: <https://github.com/myorg/microservices>
targetRevision: main
path: '{{path}}'
destination:
server: <https://kubernetes.default.svc>
namespace: '{{path.basename}}'
syncPolicy:
automated:
prune: true
selfHeal: true
services/ 디렉토리에 새로운 서비스 디렉토리를 추가하기만 하면, 자동으로 해당 서비스를 위한 Application이 생성되고 배포된다.
개발자는 ArgoCD를 전혀 건드리지 않고도 새로운 서비스를 배포할 수 있다.
Cluster Generator는 ArgoCD에 등록된 모든 클러스터를 기반으로 Application을 생성한다. 멀티 클러스터 환경에서 동일한 애플리케이션을 모든 클러스터에 배포할 때 사용한다.
Matrix Generator는 여러 Generator를 조합한다. 예를 들어, Git Directory Generator와 List Generator를 결합하여 "모든 마이크로서비스를 모든 환경에 배포"하는 복잡한 시나리오를 단일 정의로 해결할 수 있다.
ApplicationSet의 강력함과 복잡성
ApplicationSet의 장점은 확장성과 일관성이다. 새로운 환경이나 클러스터가 추가되어도 템플릿을 수정할 필요가 없다. 공통 설정을 변경하려면 템플릿 한 곳만 수정하면 모든 Application에 즉시 반영된다. 이는 수십 개의 마이크로서비스와 여러 환경을 관리하는 대규모 조직에서 엄청난 생산성 향상을 가져온다.
하지만 이러한 강력함에는 대가가 따른다. 학습 곡선이 가파르다. Generator의 개념과 템플릿 문법을 이해해야 하고, 생성된 Application이 의도와 다를 때 원인을 파악하기 어렵다. 간단한 케이스에서는 오히려 불필요한 복잡도를 추가하는 셈이 된다.
ApplicationSet은 다음과 같은 상황에서 진가를 발휘한다:
- 10개 이상의 클러스터나 환경을 관리할 때
- 동일한 패턴의 마이크로서비스가 30개 이상일 때
- 멀티 테넌트 환경에서 테넌트별로 격리된 환경을 제공할 때
- 클러스터가 동적으로 추가되거나 제거될 때
패턴 비교: 무엇을 선택할 것인가?
세 가지 패턴을 비교하면 다음과 같다.
학습 난이도:
Flat 구조는 ArgoCD의 기본 개념만 이해하면 충분하다.
App of Apps는 계층 구조라는 추가 개념이 필요하지만 여전히 직관적이다.
ApplicationSet은 Generator와 템플릿 문법을 학습해야 하므로 진입 장벽이 높다.
초기 설정 시간:
Flat 구조는 몇 분이면 시작할 수 있다.
App of Apps는 루트 Application 설계에 시간이 필요하다.
ApplicationSet은 적절한 Generator를 선택하고 템플릿을 설계하는 데 상당한 시간이 소요된다.
확장성:
Flat 구조는 서비스가 늘어날수록 관리 부담이 선형적으로 증가한다.
App of Apps는 계층 구조로 어느 정도 완화되지만 여전히 수동 작업이 많다.
ApplicationSet은 자동화를 통해 거의 무한대의 확장성을 제공한다.
유지보수:
Flat 구조와 App of Apps는 파일 수가 많아지면 유지보수가 어려워진다.
ApplicationSet은 템플릿만 관리하면 되므로 장기적으로 유지보수 비용이 낮다.
디버깅:
Flat 구조와 App of Apps는 문제가 발생한 Application의 YAML을 직접 확인하면 되므로 디버깅이 쉽다.
ApplicationSet은 템플릿과 Generator의 조합을 이해해야 하므로 디버깅이 복잡하다.
💡goormEats 프로젝트에는 무엇이 맞을까?
현재 프로젝트의 GitOps 레포 구조를 살펴보면 전형적인 Flat 구조로 운영되고 있다.
environments/ 디렉토리에 모든 서비스의 Application YAML이 평평하게 나열되어 있다.
environments/
├─ argocd-infra.yaml
├─ gateway.yaml
├─ order-service.yaml
├─ payment-service.yaml
├─ review-service.yaml
├─ store-service.yaml
├─ user-service.yaml
└─ monitoring/
다음은 이해를 돕기 위한 현재 전체 레포지토리 구조이다.
gitops-repo(k8s-manifests)/
├─ apps/
│ ├─ gateway/
│ ├─ order-service/
│ ├─ payment-service/
│ ├─ review-service/
│ ├─ store-service/
│ └─ user-service/
│
├─ docs/
│ └─ (문서들)
│
├─ environments/ (argo-apps/로 변경 예정)
│ ├─ argocd-infra.yaml
│ ├─ gateway.yaml
│ ├─ order-service.yaml
│ ├─ payment-service.yaml
│ ├─ review-service.yaml
│ ├─ store-service.yaml
│ ├─ user-service.yaml
│ └─ monitoring/
│ └─ (모니터링 관련 Application YAML들)
│
└─ infra/
├─ argo/
│ ├─ argo-cd-ingress.yaml
│ └─ argo-rollouts-ingress.yaml
└─ monitoring/ (모니터링 스택의 manifest들)
디렉토리 네이밍 개선하기
먼저 짚고 넘어갈 점이 있다. environments/라는 이름은 직관적이지 않다.
"environments"는 보통 dev, staging, production 같은 환경 분리를 의미하는데, 지금은 dev 환경만 사용하고 있고 ArgoCD Application 정의들이 들어있다.
많이 사용되는 디렉토리 네이밍:
- argocd-apps/ - 가장 명확하고 헷갈릴 여지없음
- argo-apps/ - 짧고 직관적
- argocd/ - 심플하고 많은 프로젝트에서 사용
이 중 어떤 이름을 선택하든, ArgoCD Application 정의들이 있다는 것을 명확하게 전달할 수 있다.
이 글에서는 앞으로 argo-apps/라는 이름을 사용하겠다.
현재 구조의 문제점
이 구조는 초기 단계에서 빠르게 시작하기에 좋았지만, 서비스가 늘어나면서 다음과 같은 문제가 생긴다:
- ArgoCD UI에서 수동으로 Application을 등록하는 일이 잦다
- 새로운 서비스를 추가할 때마다 여러 단계의 수동 작업이 필요하다
- 공통 설정을 변경하려면 여러 파일을 수정해야 한다
App of Apps로의 전환
App of Apps는 현재 규모에 적합한 선택이다.
단일 클러스터 환경에서 10-20개의 애플리케이션을 관리한다면, App of Apps의 직관성과 제어력이 최적의 균형점이다. GitOps의 핵심 이점인 "Git에 커밋하면 자동 배포"를 실현하면서도, 필요할 때 세밀한 제어가 가능하다.
전환은 점진적으로 진행할 수 있다.
먼저 디렉토리 이름을 변경하고, 루트 Application을 추가하는 방식으로 시작하겠다.
1단계: 디렉토리 이름 변경
git mv environments argo-apps
2단계: 루트 Application 생성
# argo-apps/root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/gitops-repo
targetRevision: main
path: argo-apps
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
3단계: ArgoCD에 루트 Application만 등록
이렇게 하면 기존 Application들이 자동으로 관리 대상이 되고, 앞으로 새로운 서비스는 YAML 파일만 추가하면 자동으로 배포된다. 기존 배포에는 영향을 주지 않는다.
선택: 더 체계적인 관리
더 나아가 디렉토리를 재구조화할 수도 있다.
argo-apps/
├─ root-app.yaml
├─ infrastructure/
│ └─ argocd-infra.yaml
├─ platform/
│ └─ monitoring/
└─ services/
├─ gateway.yaml
├─ order-service.yaml
├─ payment-service.yaml
├─ review-service.yaml
├─ store-service.yaml
└─ user-service.yaml
이렇게 하면 배포 순서 제어(Sync Waves)와 관심사 분리가 더 명확해진다.
하지만 이는 필수가 아니다. 현재 구조에 루트 Application만 추가해도 충분히 효과적이다.
ApplicationSet은 아직 필요 없다.
하지만 다음과 같은 변화가 예상된다면 미리 학습해 두는 것이 좋다.
- dev, staging, production 환경을 구축할 계획이 있다
- 클러스터를 3개 이상으로 확장할 예정이다
- 마이크로서비스가 30개 이상으로 증가할 것으로 예상된다
중요한 것은, 이 세 가지 패턴이 상호 배타적이지 않다는 점이다.
같은 클러스터에서 App of Apps와 ApplicationSet을 함께 사용할 수 있다.
예를 들어, 인프라와 플랫폼 컴포넌트는 App of Apps로 관리하고, 수십 개의 마이크로서비스는 ApplicationSet으로 관리하는 하이브리드 접근도 가능하다.
🤔 궁금한 점: Helm/Kustomize와 ArgoCD 패턴은 다른 건가요?
Helm Chart, Kustomize, 그리고 ArgoCD 배포 패턴은 서로 다른 레이어의 개념이다.
두 가지는 독립적인 선택
Manifest 관리 방식 (Helm vs Kustomize vs Plain YAML)
- "어떻게 Kubernetes manifest를 생성하고 관리할 것인가?"
- 템플릿 엔진이나 오버레이 방식으로 manifest를 구성하는 방법
ArgoCD 배포 패턴 (Flat vs App of Apps vs ApplicationSet)
- "ArgoCD Application들을 어떻게 조직화할 것인가?"
- 이 글에서 다룬 내용
실제 goormEats 프로젝트에서는?
apps/user-service/
├─ templates/
├─ Chart.yaml # ← Helm Chart (Manifest 관리)
└─ values.yaml
argo-apps/
├─ user-service.yaml # ← ArgoCD Application (배포 패턴)
└─ gateway.yaml
위 구조에서:
- Manifest 관리: Helm Chart를 사용하여 K8s 리소스를 템플릿화
- 배포 패턴: Flat 구조 (또는 App of Apps로 전환 예정)
ArgoCD Application은 어떤 Manifest 관리 방식을 사용할지 명시한다.
# argo-apps/user-service.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: user-service
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/goormEats/k8s-manifests.git
path: apps/user-service
targetRevision: dev
# Helm을 사용하는 경우
helm:
valueFiles:
- values.yaml
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: false
위 예시에서 helm 필드를 통해 Helm Chart를 사용한다고 명시하고 있다. 만약 Kustomize를 사용한다면:
source:
path: apps/user-service
kustomize:
namePrefix: prod-
이렇게 kustomize 필드를 사용하면 된다.
쉽게 비유하면
- Helm/Kustomize: 재료를 어떻게 준비할 것인가? (조리법)
- ArgoCD 패턴: 식단을 어떻게 구성할 것인가? (식단표 작성법)
좋은 조리법(Helm)을 사용하면서도 나쁜 식단표(배포 패턴)를 쓸 수 있고, 그 반대도 가능하다.
두 가지는 독립적으로 선택할 수 있다.
예를 들어:
- Helm + Flat 패턴
- Helm + App of Apps
- Kustomize + ApplicationSet
- Plain YAML + App of Apps
모두 가능한 조합이다.
프로젝트에 맞는 Manifest 관리 방식을 선택하고, 그와 별개로 적절한 ArgoCD 배포 패턴을 선택하면 된다.
마치며
ArgoCD의 배포 패턴은 팀의 성숙도와 인프라의 규모에 따라 진화한다.
Flat 구조로 시작해서 App of Apps로 성장하고, 필요하다면 ApplicationSet으로 확장하는 것이 자연스러운 여정이다.
패턴 선택의 핵심은 "지금 당장 필요한가?"다. 미래를 위해 과도하게 복잡한 구조를 도입하면 오히려 생산성이 떨어진다.
현재 문제를 해결하는 가장 단순한 패턴을 선택하고, 필요할 때 진화시키는 것이 GitOps의 철학에도 부합한다.
'🚀 Project > goorm x goormEats Project' 카테고리의 다른 글
| [회고] 0부터 서비스 배포까지, 8주간 만든 것들: 구름 PROPECT 프로펙트 클라우드 엔지니어링 과정 (0) | 2025.12.23 |
|---|
