목차
- 클러스터가 하나일 때는 괜찮았는데...
- ArgoCD 멀티 클러스터란? 개념부터 잡고 가요
- GitOps가 뭔지 먼저 짚고 가면
- 멀티 클러스터 관리, 왜 필요한가요?
- ArgoCD 설치 및 멀티 클러스터 등록 — 실전으로 들어갑니다
- 1단계: 허브 클러스터에 ArgoCD 설치
- 2단계: 대상 클러스터(Spoke) 등록
- Git 저장소 구조 설계 — 이게 진짜 중요합니다
- 모노레포(Monorepo) 방식 — 제가 선호하는 방식
- Kustomize 오버레이 예시
- ArgoCD Application 및 AppProject 설정
- AppProject(앱 프로젝트)로 클러스터 접근 제어
- Application 정의 — 멀티 클러스터 배포의 핵심
- ApplicationSet으로 여러 클러스터에 한 번에 배포
- ⚠️ 실제로 겪은 트러블슈팅 — 이거 꼭 읽으세요
- 문제 1: 클러스터 등록 후 연결 끊김 (Connection Refused)
- 문제 2: prune 옵션으로 인한 의도치 않은 리소스 삭제
- 문제 3: Helm 차트 버전 충돌
- ✅ 배포 검증 및 운영 팁
- 헬스 체크와 동기화 상태 모니터링
- Notification(알림) 설정으로 배포 상태 파악
- RBAC으로 팀별 접근 제어
- 전략 정리 — 이것만 기억하세요
- 마무리하며 — 그래서 뭐가 달라졌냐면
- 자주 묻는 질문 (FAQ)
- Q. ArgoCD 자체가 설치된 허브 클러스터가 다운되면 어떻게 되나요?
- Q. ApplicationSet과 Application을 언제 선택해야 하나요?
- Q. Helm과 Kustomize 중 어떤 걸 쓰는 게 좋나요?
클러스터가 하나일 때는 괜찮았는데...
처음 쿠버네티스를 도입했을 때 저도 클러스터 하나로 시작했어요. 개발(dev), 스테이징(staging), 프로덕션(production) 환경을 네임스페이스(Namespace)로 분리해서 쓰는 방식이었는데, 솔직히 그때는 그게 맞다고 생각했거든요. 근데 조직이 커지고 팀이 늘어나면서 문제가 하나씩 터지기 시작했습니다.
"개발팀이 실수로 프로덕션 네임스페이스에 배포했어요." 이 한 마디에 심장이 철렁 내려앉은 적 있으신가요? 저는 있습니다 ㅎㅎ. 결국 클러스터를 분리하기로 결정했고, 그때부터 ArgoCD 멀티 클러스터 전략을 본격적으로 파고들었습니다.
이 글에서는 ArgoCD를 허브(Hub) 클러스터에 설치하고, 여러 개의 쿠버네티스 클러스터를 GitOps 방식으로 배포 및 관리하는 전략을 단계별로 정리해 드리겠습니다. 저처럼 삽질하지 않도록요.
▲ ArgoCD 허브 클러스터가 여러 대상 클러스터(dev, staging, prod)를 관리하는 전체 아키텍처 구조. Hub-and-Spoke 패턴의 핵심입니다.
ArgoCD 멀티 클러스터란? 개념부터 잡고 가요
GitOps가 뭔지 먼저 짚고 가면
GitOps는 쉽게 말해 "Git 저장소가 인프라와 애플리케이션의 단일 진실 공급원(Single Source of Truth)이 되는 운영 방식"이에요. 배포하고 싶으면 kubectl로 직접 명령을 날리는 게 아니라, Git에 커밋하면 자동으로 클러스터에 반영되는 방식이죠.
ArgoCD는 이 GitOps를 쿠버네티스 위에서 구현해주는 대표적인 오픈소스 도구입니다. Git 저장소를 지속적으로 감시하다가 변경이 감지되면 클러스터에 자동으로 동기화(Sync)해줘요.
멀티 클러스터 관리, 왜 필요한가요?
네임스페이스 분리 방식의 가장 큰 문제는 폭발 반경(Blast Radius)이 너무 넓다는 거예요. 클러스터 레벨의 장애가 모든 환경에 영향을 주고, 보안 격리도 완벽하지 않습니다. 반면 클러스터를 분리하면:
- 환경 간 완전한 격리 — 개발 배포 실수가 프로덕션에 영향 없음
- 클러스터별 독립적인 리소스 할당 및 스케일링
- 규정 준수(Compliance) 요구사항 충족 용이
- 팀별 독립적인 업그레이드 주기 관리
그런데 클러스터가 여러 개가 되면 관리 포인트도 여러 개가 되잖아요. 각 클러스터에 ArgoCD를 따로 설치하는 방법도 있지만, 저는 Hub-and-Spoke 패턴을 선호합니다. 허브 클러스터 하나에 ArgoCD를 설치하고, 나머지 클러스터들을 원격으로 관리하는 방식이에요.
| 방식 | 장점 | 단점 | 추천 상황 |
|---|---|---|---|
| 클러스터별 ArgoCD 설치 | 독립성 높음, 장애 격리 | 관리 포인트 분산, 일관성 유지 어려움 | 팀/조직이 완전히 분리된 경우 |
| Hub-and-Spoke (중앙화) | 단일 관리 포인트, 일관된 정책 적용 | 허브 클러스터 장애 시 배포 불가 | 중앙 플랫폼팀이 관리하는 경우 |
ArgoCD 설치 및 멀티 클러스터 등록 — 실전으로 들어갑니다
1단계: 허브 클러스터에 ArgoCD 설치
저는 허브 클러스터로 별도의 관리 전용 클러스터를 사용합니다. 프로덕션 워크로드가 없는 클러스터에 ArgoCD를 올리는 게 안전하더라고요.
# ArgoCD 네임스페이스 생성
kubectl create namespace argocd
# ArgoCD 공식 매니페스트로 설치
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# 설치 확인
kubectl get pods -n argocd
설치가 완료되면 argocd-server 파드가 Running 상태가 되는데, 처음엔 이미지 풀(Image Pull) 때문에 좀 기다려야 할 수도 있어요. 저도 처음에 "왜 안 되지?" 하고 5분 기다렸더니 그냥 올라왔습니다 ㅎㅎ.
# 초기 admin 비밀번호 확인
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d
# ArgoCD CLI 설치 (macOS 기준)
brew install argocd
# ArgoCD 서버 포트포워딩 (로컬 접근용)
kubectl port-forward svc/argocd-server -n argocd 8080:443
# CLI 로그인
argocd login localhost:8080 --username admin --password <위에서_확인한_비밀번호> --insecure
2단계: 대상 클러스터(Spoke) 등록
이 부분이 멀티 클러스터 설정의 핵심이에요. ArgoCD CLI로 대상 클러스터를 등록하면, ArgoCD가 해당 클러스터에 argocd-manager라는 서비스 어카운트(Service Account)를 만들고 필요한 RBAC 권한을 자동으로 설정해줍니다.
# 현재 kubeconfig에 등록된 컨텍스트 확인
kubectl config get-contexts
# 예시 출력:
# CURRENT NAME CLUSTER AUTHINFO
# * hub-cluster hub-cluster hub-admin
# dev-cluster dev-cluster dev-admin
# staging-cluster staging-cluster staging-admin
# prod-cluster prod-cluster prod-admin
# 개발 클러스터 등록
argocd cluster add dev-cluster --name dev
# 스테이징 클러스터 등록
argocd cluster add staging-cluster --name staging
# 프로덕션 클러스터 등록
argocd cluster add prod-cluster --name prod
# 등록된 클러스터 목록 확인
argocd cluster list
💡 팁: 클러스터 등록 시 --name 옵션으로 별칭을 지정하면 나중에 Application 설정에서 훨씬 읽기 편합니다. URL 대신 이름으로 참조할 수 있거든요.
▲ ArgoCD 웹 UI의 Settings > Clusters 화면. dev, staging, prod 클러스터가 각각 등록되어 연결 상태를 실시간으로 확인할 수 있습니다.
Git 저장소 구조 설계 — 이게 진짜 중요합니다
멀티 클러스터 GitOps에서 Git 저장소 구조를 어떻게 잡느냐가 나중에 관리 편의성을 크게 좌우해요. 제가 여러 방식을 시도해보고 정착한 구조를 공유합니다.
모노레포(Monorepo) 방식 — 제가 선호하는 방식
gitops-repo/
├── apps/ # 애플리케이션 정의
│ ├── base/ # 공통 기본 설정 (Kustomize base)
│ │ ├── my-app/
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ └── kustomization.yaml
│ └── overlays/ # 환경별 오버레이
│ ├── dev/
│ │ └── my-app/
│ │ ├── kustomization.yaml
│ │ └── patch-replicas.yaml
│ ├── staging/
│ │ └── my-app/
│ └── prod/
│ └── my-app/
├── argocd/ # ArgoCD 설정 자체도 Git으로 관리
│ ├── projects/ # ArgoCD Project 정의
│ │ ├── dev-project.yaml
│ │ ├── staging-project.yaml
│ │ └── prod-project.yaml
│ └── applications/ # ArgoCD Application 정의
│ ├── dev/
│ ├── staging/
│ └── prod/
└── clusters/ # 클러스터 레벨 설정
├── dev/
├── staging/
└── prod/
이 구조의 핵심은 Kustomize(커스터마이즈)를 활용해서 base 설정을 공유하면서 환경별로 다른 값(레플리카 수, 리소스 제한, 이미지 태그 등)만 오버레이로 덮어쓰는 방식이에요.
Kustomize 오버레이 예시
# apps/base/my-app/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-registry/my-app:latest
resources:
requests:
cpu: 100m
memory: 128Mi
# apps/overlays/prod/my-app/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../../base/my-app
patches:
- path: patch-replicas.yaml
images:
- name: my-registry/my-app
newTag: v1.2.3 # 프로덕션은 명시적 태그 사용
# apps/overlays/prod/my-app/patch-replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3 # 프로덕션은 3개로
ArgoCD Application 및 AppProject 설정
AppProject(앱 프로젝트)로 클러스터 접근 제어
ArgoCD의 AppProject는 애플리케이션들을 논리적으로 그룹화하고, 어떤 Git 저장소에서 어떤 클러스터로 배포할 수 있는지 제한하는 역할을 합니다. 저는 이걸 환경별로 나눠서 씁니다.
# argocd/projects/prod-project.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: production
namespace: argocd
spec:
description: Production environment project
# 허용된 소스 저장소
sourceRepos:
- 'https://github.com/myorg/gitops-repo.git'
# 배포 가능한 대상 클러스터와 네임스페이스
destinations:
- server: https://prod-cluster-api.example.com
namespace: '*'
# 클러스터 범위 리소스 생성 제한 (선택적)
clusterResourceWhitelist:
- group: ''
kind: Namespace
# RBAC 역할 정의
roles:
- name: prod-deployer
description: Production deployment role
policies:
- p, proj:production:prod-deployer, applications, sync, production/*, allow
groups:
- myorg:platform-team
Application 정의 — 멀티 클러스터 배포의 핵심
# argocd/applications/prod/my-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app-prod
namespace: argocd
# 앱 삭제 시 리소스도 함께 삭제되지 않도록 (중요!)
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: production
source:
repoURL: https://github.com/myorg/gitops-repo.git
targetRevision: main
path: apps/overlays/prod/my-app # 프로덕션 오버레이 경로
destination:
# 등록한 클러스터 이름 또는 API 서버 URL
server: https://prod-cluster-api.example.com
namespace: my-app
syncPolicy:
automated:
prune: true # Git에서 삭제된 리소스는 클러스터에서도 삭제
selfHeal: true # 클러스터 상태가 Git과 다르면 자동 복구
syncOptions:
- CreateNamespace=true # 네임스페이스 없으면 자동 생성
- PrunePropagationPolicy=foreground
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
ApplicationSet으로 여러 클러스터에 한 번에 배포
근데 여기서 더 편한 방법이 있어요. ApplicationSet(애플리케이션셋)을 쓰면 여러 클러스터에 대한 Application을 템플릿 하나로 자동 생성할 수 있거든요. 처음 이걸 알았을 때 "이게 왜 이렇게 편하지?" 싶었습니다.
# argocd/applicationsets/my-app-all-clusters.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: my-app-all-clusters
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: dev
url: https://dev-cluster-api.example.com
env: dev
revision: HEAD
- cluster: staging
url: https://staging-cluster-api.example.com
env: staging
revision: main
- cluster: prod
url: https://prod-cluster-api.example.com
env: prod
revision: main
template:
metadata:
name: 'my-app-{{env}}'
spec:
project: '{{env}}'
source:
repoURL: https://github.com/myorg/gitops-repo.git
targetRevision: '{{revision}}'
path: 'apps/overlays/{{env}}/my-app'
destination:
server: '{{url}}'
namespace: my-app
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
💡 팁: ApplicationSet의 generators에는 List 방식 외에도 클러스터 레이블 기반으로 자동 감지하는 Cluster Generator도 있어요. 클러스터가 많아질수록 이쪽이 훨씬 강력합니다.
⚠️ 실제로 겪은 트러블슈팅 — 이거 꼭 읽으세요
문제 1: 클러스터 등록 후 연결 끊김 (Connection Refused)
클러스터를 등록했는데 ArgoCD UI에서 계속 "Unknown" 상태가 뜨는 경우가 있었어요. 원인을 파고들어보니 허브 클러스터에서 대상 클러스터의 API 서버로 직접 네트워크 연결이 안 되는 거였습니다. VPC 피어링이나 방화벽 규칙을 확인해야 해요.
# 허브 클러스터에서 대상 클러스터 API 서버 연결 테스트
kubectl run curl-test --image=curlimages/curl --rm -it --restart=Never -- \
curl -k https://prod-cluster-api.example.com/healthz
문제 2: prune 옵션으로 인한 의도치 않은 리소스 삭제
이건 진짜 아찔했던 경험인데요. automated.prune: true를 켜놨는데, 실수로 Git에서 파일을 지웠다가 프로덕션 Deployment가 통째로 삭제된 적이 있었어요. 다행히 빠르게 복구했지만...
이후로 저는 프로덕션 환경에는 Sync Windows(동기화 창)를 설정해서 업무 시간 외에는 자동 동기화가 안 되도록 했습니다.
# AppProject에 Sync Window 추가
spec:
syncWindows:
- kind: allow
schedule: '0 9 * * 1-5' # 평일 오전 9시에만
duration: 8h
applications:
- '*'
manualSync: true # 수동 동기화는 항상 허용
문제 3: Helm 차트 버전 충돌
여러 클러스터에 같은 Helm 차트를 다른 버전으로 배포하다 보면 values 파일 구조가 버전마다 달라서 오류가 나는 경우가 있어요. 저는 이걸 해결하기 위해 클러스터별 values 파일을 명확하게 분리해서 관리하고 있습니다.
# Helm 소스 예시 - 클러스터별 values 파일 분리
source:
repoURL: https://charts.example.com
chart: my-chart
targetRevision: 1.2.3
helm:
valueFiles:
- values.yaml
- values-prod.yaml # 환경별 values 파일
▲ ArgoCD 웹 UI의 Applications 화면. dev, staging, prod 클러스터에 배포된 my-app들이 모두 Synced/Healthy 상태로 표시되는 모습입니다.
✅ 배포 검증 및 운영 팁
헬스 체크와 동기화 상태 모니터링
# 전체 Application 상태 확인
argocd app list
# 특정 앱 상세 상태 확인
argocd app get my-app-prod
# 수동으로 동기화 실행
argocd app sync my-app-prod
# 동기화 상태 및 히스토리 확인
argocd app history my-app-prod
# 롤백 (이전 버전으로)
argocd app rollback my-app-prod
Notification(알림) 설정으로 배포 상태 파악
ArgoCD에는 argocd-notifications라는 컴포넌트가 있어서 Slack, 이메일, PagerDuty 등으로 배포 상태를 알림 받을 수 있어요. 저는 Slack 채널에 연동해서 배포 성공/실패를 실시간으로 받고 있는데, 이거 없으면 이제 못 살 것 같습니다 ㅎㅎ.
RBAC으로 팀별 접근 제어
# argocd-rbac-cm ConfigMap 예시
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
data:
policy.csv: |
# 개발팀: dev 프로젝트만 접근 가능
p, role:dev-team, applications, *, dev/*, allow
p, role:dev-team, applications, sync, dev/*, allow
# 플랫폼팀: 전체 접근 가능
p, role:platform-team, applications, *, */*, allow
p, role:platform-team, clusters, *, *, allow
g, myorg:dev-team, role:dev-team
g, myorg:platform-team, role:platform-team
policy.default: role:readonly
전략 정리 — 이것만 기억하세요
▲ ArgoCD 멀티 클러스터 GitOps 전략의 핵심 요소를 한눈에 정리한 요약 다이어그램. Git 저장소 구조, ArgoCD 컴포넌트, 클러스터 관계를 보여줍니다.
| 구성 요소 | 역할 | 핵심 포인트 |
|---|---|---|
| AppProject | 배포 범위 및 권한 제한 | 환경별로 분리, Sync Window 설정 |
| Application | 개별 앱 배포 정의 | destination.server로 대상 클러스터 지정 |
| ApplicationSet | 다중 클러스터 배포 자동화 | 템플릿 하나로 여러 클러스터에 배포 |
| Kustomize Overlay | 환경별 설정 분리 | base 공유, 환경별 차이만 patch |
| RBAC | 팀별 접근 제어 | AppProject role + argocd-rbac-cm |
마무리하며 — 그래서 뭐가 달라졌냐면
ArgoCD 멀티 클러스터 구성을 제대로 잡고 나서 가장 크게 달라진 건, 배포에 대한 불안감이 사라졌다는 거예요. 예전엔 "내가 지금 어떤 클러스터에 붙어있지?"를 항상 확인해야 했는데, 이제는 Git에 PR 올리고 머지하면 끝이거든요.
물론 처음 설계할 때 Git 저장소 구조를 잘 잡는 게 제일 중요합니다. 나중에 구조를 바꾸려면 진짜 손이 많이 가거든요. 저도 한 번 갈아엎었습니다... ㅎㅎ 이 글을 보시는 분들은 처음부터 overlays 구조로 시작하시길 강력 추천합니다.
다음 글에서는 ArgoCD Image Updater를 활용해서 컨테이너 이미지 태그 업데이트까지 완전 자동화하는 방법을 다뤄볼 예정이에요. CI 파이프라인에서 이미지 빌드되면 ArgoCD가 자동으로 Git 커밋하고 배포까지 이어지는 풀 사이클인데, 이거 진짜 편합니다.
혹시 멀티 클러스터 구성하면서 막히는 부분 있으시면 댓글로 남겨주세요. 제가 겪어본 삽질이라면 같이 해결해 드릴 수 있을 것 같습니다 🎉
자주 묻는 질문 (FAQ)
Q. ArgoCD 자체가 설치된 허브 클러스터가 다운되면 어떻게 되나요?
A. 허브 클러스터가 다운되면 새로운 배포는 불가능하지만, 이미 배포된 애플리케이션들은 각 클러스터에서 계속 정상 동작합니다. 이 때문에 허브 클러스터는 고가용성(HA) 구성으로 운영하는 것을 권장합니다.
Q. ApplicationSet과 Application을 언제 선택해야 하나요?
A. 동일한 앱을 여러 클러스터에 배포해야 한다면 ApplicationSet이 훨씬 효율적입니다. 클러스터마다 배포 설정이 크게 다르거나 배포 시점을 완전히 독립적으로 관리해야 한다면 개별 Application을 사용하는 게 맞습니다.
Q. Helm과 Kustomize 중 어떤 걸 쓰는 게 좋나요?
A. 서드파티 차트를 그대로 쓸 때는 Helm, 자체 매니페스트를 환경별로 관리할 때는 Kustomize가 더 직관적입니다. 둘을 혼합해서 쓰는 것도 가능하고, 저는 실제로 두 방식을 같이 씁니다.
'IT > k8s' 카테고리의 다른 글
| [k8s] 쿠버네티스 영구 스토리지: Longhorn vs Rook Ceph 비교 및 선택 가이드 (0) | 2026.04.27 |
|---|---|
| [k8s] Helm Chart 베스트 프랙티스: 프로덕션 배포 및 관리 전략 (0) | 2026.04.27 |
| [k8s] 쿠버네티스 Ingress Controller 비교: Nginx, Traefik, HAProxy 선택 가이드 (0) | 2026.04.20 |
| [K8s] 쿠버네티스 Ingress Controller 비교: Nginx, Traefik, Gateway API 선택 가이드 (0) | 2026.04.18 |
| [k8s] kubectl 플러그인 추천: 생산성을 높이는 필수 도구 활용법 (0) | 2026.04.18 |
| [k8s] Longhorn 쿠버네티스 영구 스토리지 완벽 가이드: 설치부터 PV/PVC까지 (1) | 2026.04.18 |