- Published on
GitOps 실전 가이드: ArgoCD vs FluxCD 아키텍처 비교와 프로덕션 배포 전략
- Authors
- Name
- 들어가며
- ArgoCD 아키텍처 심층 분석
- FluxCD 아키텍처 심층 분석
- ArgoCD vs FluxCD 비교표
- 배포 전략
- 시크릿 관리
- CI/CD 통합 패턴
- 장애 사례와 복구 절차
- 프로덕션 배포 체크리스트
- 마치며
- 참고자료

들어가며
현대 클라우드 네이티브 환경에서 Kubernetes 배포를 관리하는 방법은 크게 Push 기반과 Pull 기반으로 나뉜다. 전통적인 CI/CD 파이프라인은 Push 방식으로 빌드 완료 후 kubectl apply나 helm upgrade를 실행하여 클러스터에 변경을 밀어넣는다. 이 방식은 CI 서버에 클러스터 접근 권한을 부여해야 하고, 수동 변경(kubectl edit, 콘솔 수정)으로 인한 드리프트를 감지하기 어렵다는 근본적인 한계가 있다.
GitOps는 이 문제를 해결하는 운영 패러다임으로, Git 저장소를 **단일 진실 소스(Single Source of Truth)**로 삼고, 클러스터 내부의 에이전트가 Git의 원하는 상태와 실제 상태를 지속적으로 비교하여 **자동으로 동기화(Reconciliation)**한다. OpenGitOps 프로젝트가 정의한 핵심 원칙은 다음과 같다.
- 선언적(Declarative): 시스템의 원하는 상태를 선언적으로 기술한다
- 버전 관리(Versioned and Immutable): Git에 모든 변경 이력이 남아 감사 추적이 가능하다
- 자동 적용(Pulled Automatically): 에이전트가 자동으로 원하는 상태를 클러스터에 적용한다
- 지속적 조정(Continuously Reconciled): 드리프트를 자동으로 감지하고 수정한다
이 글에서는 GitOps를 구현하는 대표적인 두 가지 도구인 ArgoCD와 FluxCD의 아키텍처를 심층 비교하고, 실제 프로덕션 환경에서의 배포 전략, 시크릿 관리, CI/CD 통합, 장애 사례와 복구, 운영 체크리스트까지 종합적으로 다룬다.
ArgoCD 아키텍처 심층 분석
핵심 구성 요소
ArgoCD는 CNCF Graduated 프로젝트로, Kubernetes 전용 GitOps 지속적 배포 도구이다. 기본적으로 웹 UI, CLI, gRPC/REST API를 제공한다.
# ArgoCD Application 리소스 예시
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/k8s-manifests.git
targetRevision: main
path: apps/my-app/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
ArgoCD의 주요 컴포넌트는 다음과 같다.
- API Server: 웹 UI, CLI, CI/CD 시스템이 사용하는 gRPC/REST API를 제공한다
- Repo Server: Git 저장소를 클론하고 매니페스트를 생성한다. Helm, Kustomize, Jsonnet 등을 지원한다
- Application Controller: Application 리소스를 감시하고 조정 루프를 실행한다
- Redis: UI 세션과 캐시를 저장한다
- Dex: SSO(Single Sign-On) 인증을 담당한다
- Notifications Controller: Slack, 이메일 등으로 동기화 상태 알림을 전송한다
App of Apps 패턴
대규모 환경에서는 App of Apps 패턴을 사용하여 여러 Application을 하나의 부모 Application으로 관리한다.
# 부모 Application (root-app)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/k8s-manifests.git
targetRevision: main
path: apps
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
apps 디렉토리 안에 각 서비스의 Application YAML 파일을 배치하면, root-app이 이를 자동으로 생성하고 관리한다.
ApplicationSet 컨트롤러
ApplicationSet은 하나의 템플릿으로 여러 Application을 동적으로 생성한다. 멀티 클러스터, 멀티 테넌트, 모노레포 환경에서 매우 유용하다.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: cluster-apps
namespace: argocd
spec:
generators:
- clusters:
selector:
matchLabels:
environment: production
template:
metadata:
name: 'app-{{name}}'
spec:
project: default
source:
repoURL: https://github.com/org/k8s-manifests.git
targetRevision: main
path: 'apps/{{metadata.labels.region}}/production'
destination:
server: '{{server}}'
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
이 ApplicationSet은 environment: production 레이블이 있는 모든 클러스터에 대해 자동으로 Application을 생성한다.
Sync Waves와 Hooks
ArgoCD의 Sync Wave는 리소스 배포 순서를 정밀하게 제어한다.
# Wave 0: Namespace와 RBAC 먼저 생성
apiVersion: v1
kind: Namespace
metadata:
name: production
annotations:
argocd.argoproj.io/sync-wave: '0'
---
# Wave 1: ConfigMap과 Secret
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
annotations:
argocd.argoproj.io/sync-wave: '1'
data:
DATABASE_HOST: 'postgres.production.svc'
LOG_LEVEL: 'info'
---
# Wave 2: Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
namespace: production
annotations:
argocd.argoproj.io/sync-wave: '2'
spec:
replicas: 3
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api-server
image: myregistry/api-server:v2.1.0
ports:
- containerPort: 8080
---
# PreSync Hook: DB 마이그레이션
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
namespace: production
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
containers:
- name: migrate
image: myregistry/db-migrate:v2.1.0
command: ['./migrate', 'up']
restartPolicy: Never
FluxCD 아키텍처 심층 분석
핵심 구성 요소
FluxCD는 CNCF Graduated 프로젝트로, Kubernetes 네이티브 GitOps 툴킷이다. 여러 개의 독립적인 컨트롤러로 구성되어 있다.
- Source Controller: Git 저장소, Helm 저장소, OCI 아티팩트, S3 버킷 등 소스를 관리한다
- Kustomize Controller: Kustomization 리소스를 감시하고 클러스터에 매니페스트를 적용한다
- Helm Controller: HelmRelease 리소스를 감시하고 Helm 차트를 관리한다
- Notification Controller: 이벤트 알림 및 외부 웹훅 수신을 처리한다
- Image Reflector/Automation Controllers: 컨테이너 레지스트리의 새 이미지를 감지하고 자동으로 Git을 업데이트한다
GitRepository와 Kustomization
FluxCD의 기본 워크플로우는 GitRepository로 소스를 정의하고 Kustomization으로 적용 방법을 기술하는 것이다.
# 소스 정의
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: k8s-manifests
namespace: flux-system
spec:
interval: 1m
url: https://github.com/org/k8s-manifests.git
ref:
branch: main
secretRef:
name: git-credentials
ignore: |
# 불필요한 파일 제외
docs/
README.md
---
# 적용 정의
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: production-apps
namespace: flux-system
spec:
interval: 5m
retryInterval: 2m
timeout: 3m
sourceRef:
kind: GitRepository
name: k8s-manifests
path: ./apps/production
prune: true
force: false
targetNamespace: production
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: api-server
namespace: production
postBuild:
substituteFrom:
- kind: ConfigMap
name: cluster-vars
HelmRelease 컨트롤러
FluxCD의 Helm Controller는 HelmRelease CRD를 통해 Helm 차트의 라이프사이클을 선언적으로 관리한다.
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: bitnami
namespace: flux-system
spec:
interval: 30m
url: https://charts.bitnami.com/bitnami
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: postgresql
namespace: production
spec:
interval: 10m
chart:
spec:
chart: postgresql
version: '15.x'
sourceRef:
kind: HelmRepository
name: bitnami
namespace: flux-system
values:
primary:
persistence:
size: 50Gi
resources:
requests:
memory: 512Mi
cpu: 250m
metrics:
enabled: true
upgrade:
remediation:
retries: 3
remediateLastFailure: true
rollback:
cleanupOnFail: true
timeout: 5m
FluxCD 의존성 관리
Kustomization 리소스 간 의존성을 선언하여 배포 순서를 제어할 수 있다.
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: infrastructure
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: GitRepository
name: k8s-manifests
path: ./infrastructure
prune: true
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: applications
namespace: flux-system
spec:
interval: 10m
dependsOn:
- name: infrastructure
sourceRef:
kind: GitRepository
name: k8s-manifests
path: ./applications
prune: true
infrastructure Kustomization이 성공적으로 적용된 후에만 applications가 적용된다.
ArgoCD vs FluxCD 비교표
| 항목 | ArgoCD | FluxCD |
|---|---|---|
| CNCF 등급 | Graduated | Graduated |
| 웹 UI | O (내장, 풍부한 기능) | X (별도 Weave GitOps UI 사용) |
| CLI | argocd CLI | flux CLI |
| 아키텍처 | 모놀리식 (여러 컴포넌트가 하나로) | 마이크로서비스 (독립 컨트롤러) |
| CRD | Application, ApplicationSet, AppProject | GitRepository, Kustomization, HelmRelease 등 |
| 멀티 클러스터 | 허브-스포크 모델 | 클러스터별 Flux 설치 |
| Helm 지원 | O (소스로 직접 참조) | O (HelmRelease CRD) |
| Kustomize | O | O (네이티브) |
| RBAC | 자체 RBAC + SSO 통합 | Kubernetes 네이티브 RBAC |
| 알림 | Notification Controller | Notification Controller |
| 이미지 자동 업데이트 | Image Updater (별도 설치) | Image Automation Controller |
| 드리프트 감지 | O (실시간 UI 표시) | O (이벤트 기반) |
| Sync Wave/순서 | Sync Wave + Hook | dependsOn 의존성 |
| SSO 지원 | O (Dex 내장) | X (Kubernetes RBAC 위임) |
| 멀티 테넌시 | AppProject 기반 | 네임스페이스 기반 |
| Git 지원 | GitHub, GitLab, Bitbucket 등 | GitHub, GitLab, Bitbucket, S3, OCI 등 |
| 학습 곡선 | 중간 (UI가 도움) | 높음 (CLI 중심) |
| 리소스 사용량 | 상대적으로 높음 | 상대적으로 낮음 |
배포 전략
Blue-Green 배포 (Argo Rollouts)
ArgoCD와 함께 Argo Rollouts를 사용하면 Blue-Green 배포를 선언적으로 구현할 수 있다.
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: api-server
namespace: production
spec:
replicas: 5
strategy:
blueGreen:
activeService: api-server-active
previewService: api-server-preview
autoPromotionEnabled: false
prePromotionAnalysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: api-server-preview
scaleDownDelaySeconds: 30
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api-server
image: myregistry/api-server:v2.2.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
namespace: production
spec:
args:
- name: service-name
metrics:
- name: success-rate
interval: 30s
count: 5
successCondition: result[0] >= 0.95
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(rate(http_requests_total{service="{{args.service-name}}",status=~"2.."}[5m])) /
sum(rate(http_requests_total{service="{{args.service-name}}"}[5m]))
Canary 배포 (Flagger + FluxCD)
FluxCD 환경에서는 Flagger를 사용하여 Canary 배포를 자동화한다.
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: api-server
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
service:
port: 8080
targetPort: 8080
analysis:
interval: 1m
threshold: 5
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m
webhooks:
- name: load-test
type: rollout
url: http://flagger-loadtester.production/
metadata:
cmd: 'hey -z 1m -q 10 -c 2 http://api-server-canary.production:8080/'
시크릿 관리
SOPS (Secrets OPerationS)
SOPS는 파일 레벨에서 시크릿을 암호화하여 Git에 안전하게 저장하는 도구이다. FluxCD는 SOPS를 네이티브로 지원한다.
# .sops.yaml (저장소 루트에 배치)
creation_rules:
- path_regex: .*.enc.yaml
encrypted_regex: '^(data|stringData)$'
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 시크릿 암호화
sops --encrypt --in-place secrets/production/db-credentials.enc.yaml
# FluxCD Kustomization에서 SOPS 복호화 활성화
flux create kustomization production-secrets \
--source=GitRepository/k8s-manifests \
--path="./secrets/production" \
--prune=true \
--interval=10m \
--decryption-provider=sops \
--decryption-secret=sops-age
Sealed Secrets
Sealed Secrets는 비대칭 암호화를 사용하여 시크릿을 Git에 안전하게 저장한다.
# SealedSecret 생성
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=supersecret \
--dry-run=client -o yaml | \
kubeseal --controller-name=sealed-secrets \
--controller-namespace=kube-system \
--format=yaml > sealed-db-credentials.yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: production
spec:
encryptedData:
username: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq...
password: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq...
template:
type: Opaque
metadata:
name: db-credentials
namespace: production
Vault 통합 (ArgoCD Vault Plugin)
HashiCorp Vault와 ArgoCD를 통합하여 시크릿을 동적으로 주입한다.
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
annotations:
avp.kubernetes.io/path: 'secret/data/production/database'
type: Opaque
stringData:
username: <username>
password: <password>
위 매니페스트에서 <username>과 <password>는 ArgoCD Vault Plugin이 Vault에서 값을 조회하여 실제 시크릿 값으로 대체한다.
CI/CD 통합 패턴
GitHub Actions + ArgoCD
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and Push Image
run: |
docker build -t myregistry/api-server:$GITHUB_SHA .
docker push myregistry/api-server:$GITHUB_SHA
- name: Update Kubernetes Manifests
run: |
git clone https://github.com/org/k8s-manifests.git
cd k8s-manifests
kustomize edit set image myregistry/api-server=myregistry/api-server:$GITHUB_SHA
git add .
git commit -m "chore: update api-server image to $GITHUB_SHA"
git push
이 패턴에서 CI(GitHub Actions)는 이미지 빌드와 매니페스트 저장소 업데이트만 담당하고, 실제 배포는 ArgoCD가 Git 변경을 감지하여 자동으로 수행한다.
장애 사례와 복구 절차
사례 1: Sync Loop (무한 동기화 루프)
상황: ArgoCD Application이 Sync 완료 후 즉시 OutOfSync 상태로 전환되어 무한 동기화 루프에 빠졌다. 원인은 Admission Webhook이 리소스에 기본값을 주입하면서 Git의 매니페스트와 차이가 발생한 것이었다.
증상:
- Application 상태가 Synced와 OutOfSync를 반복
- ArgoCD API Server CPU 사용량 급증
- Repo Server 메모리 사용량 증가
복구 절차:
# 1. 자동 동기화 일시 중단
argocd app set my-app --sync-policy none
# 2. 드리프트 원인 분석
argocd app diff my-app --local ./manifests/
# 3. ignoreDifferences 설정으로 Webhook 주입 필드 무시
argocd app set my-app --ignore-differences \
'group=apps, kind=Deployment, jsonPointers=[/spec/template/metadata/annotations]'
# 4. 매니페스트에 Webhook이 주입하는 기본값을 명시적으로 추가
# 5. 자동 동기화 재활성화
argocd app set my-app --sync-policy automated
사례 2: 드리프트 감지 실패
상황: FluxCD의 Kustomization이 성공 상태를 보고하지만, 실제 클러스터에서 수동으로 변경된 리소스가 복원되지 않았다. force 옵션이 비활성화되어 있었고, 수동 변경이 FluxCD가 관리하지 않는 필드에서 발생했다.
증상:
- Kustomization 상태는 Ready
- 실제 리소스의 설정이 Git과 다름
- kubectl diff로 차이 확인 가능
복구 절차:
# 1. FluxCD 강제 조정 트리거
flux reconcile kustomization production-apps --with-source
# 2. force 옵션 활성화 (server-side apply 사용)
flux create kustomization production-apps \
--source=GitRepository/k8s-manifests \
--path="./apps/production" \
--prune=true \
--force=true \
--interval=5m
# 3. 수동 변경 방지를 위한 OPA 정책 추가
# (kubectl edit, kubectl patch 등을 제한)
사례 3: Helm Release 롤백 실패
상황: FluxCD HelmRelease의 업그레이드가 실패했으나 자동 롤백이 동작하지 않았다. remediation 설정에서 remediateLastFailure가 false로 되어 있었다.
증상:
- HelmRelease 상태가 Failed
- 이전 버전의 릴리스도 손상됨
- Helm history에 failed 릴리스가 누적
복구 절차:
# 1. HelmRelease 상태 확인
flux get helmrelease -n production
# 2. Helm 릴리스 히스토리 확인
helm history postgresql -n production
# 3. 수동 롤백
helm rollback postgresql 3 -n production
# 4. HelmRelease 설정에 자동 복구 추가
flux create helmrelease postgresql \
--source=HelmRepository/bitnami \
--chart=postgresql \
--chart-version="15.x" \
--target-namespace=production \
--values=values/postgresql.yaml \
--remediation-retries=3
프로덕션 배포 체크리스트
사전 준비
- Git 저장소 구조 설계 (모노레포 vs 폴리레포 결정)
- 브랜치 전략 수립 (main -> staging -> production)
- RBAC 정책 설계 (어떤 팀이 어떤 Application/Kustomization을 관리할지)
- 시크릿 관리 방법 결정 (SOPS, Sealed Secrets, Vault 중 선택)
ArgoCD 설정
- HA(고가용성) 모드로 배포 (최소 3개 레플리카)
- Redis Sentinel 또는 Redis Cluster 구성
- Dex 또는 OIDC를 통한 SSO 설정
- AppProject별 소스 저장소와 대상 클러스터 제한
- Resource Exclusions/Inclusions 설정으로 관리 범위 명확화
FluxCD 설정
- 각 클러스터에 독립적인 FluxCD 설치
- Source Controller의 Git 폴링 간격 최적화 (기본 1분)
- Kustomization의 헬스체크 대상 리소스 지정
- Notification Controller로 Slack/Teams 알림 구성
운영 주의사항
- Git 저장소의 write 권한을 최소화 (PR 기반 변경)
- 프로덕션 배포는 반드시 PR 리뷰 후 머지
- 동기화 간격과 타임아웃을 워크로드 특성에 맞게 조정
- 대규모 매니페스트 변경 시 Sync Wave나 dependsOn으로 순서 보장
- Helm 차트 버전을 범위가 아닌 고정 버전으로 지정
- 롤백을 위한 Git revert 프로세스 문서화
모니터링
- ArgoCD: Application 동기화 상태 메트릭 수집 (
argocd_app_info) - FluxCD: Kustomization/HelmRelease 상태 메트릭 수집
- 동기화 실패 시 자동 알림 설정 (PagerDuty, OpsGenie 연동)
- 정기적인 드리프트 감사 리포트 생성
마치며
GitOps는 Kubernetes 배포 관리의 표준으로 자리 잡았다. ArgoCD와 FluxCD 모두 CNCF Graduated 프로젝트로서 성숙한 생태계를 갖추고 있으며, 어떤 도구를 선택하든 GitOps의 핵심 원칙을 충실히 구현할 수 있다.
ArgoCD는 풍부한 웹 UI와 ApplicationSet을 통한 멀티 클러스터 관리, Argo Rollouts와의 통합을 강점으로 한다. FluxCD는 Kubernetes 네이티브한 마이크로서비스 아키텍처, 가벼운 리소스 사용량, SOPS 네이티브 지원을 강점으로 한다.
중요한 것은 도구의 선택보다 GitOps 문화의 정착이다. 모든 변경이 Git을 통해 이루어지도록 프로세스를 수립하고, 수동 변경을 방지하는 가드레일을 구축하며, 드리프트를 지속적으로 감지하고 수정하는 자동화 체계를 갖추는 것이 진정한 GitOps의 완성이다.