Skip to content
Published on

GitOps 실전 가이드: ArgoCD vs FluxCD 아키텍처 비교와 프로덕션 배포 전략

Authors
  • Name
    Twitter
GitOps ArgoCD vs FluxCD

들어가며

현대 클라우드 네이티브 환경에서 Kubernetes 배포를 관리하는 방법은 크게 Push 기반Pull 기반으로 나뉜다. 전통적인 CI/CD 파이프라인은 Push 방식으로 빌드 완료 후 kubectl applyhelm upgrade를 실행하여 클러스터에 변경을 밀어넣는다. 이 방식은 CI 서버에 클러스터 접근 권한을 부여해야 하고, 수동 변경(kubectl edit, 콘솔 수정)으로 인한 드리프트를 감지하기 어렵다는 근본적인 한계가 있다.

GitOps는 이 문제를 해결하는 운영 패러다임으로, Git 저장소를 **단일 진실 소스(Single Source of Truth)**로 삼고, 클러스터 내부의 에이전트가 Git의 원하는 상태와 실제 상태를 지속적으로 비교하여 **자동으로 동기화(Reconciliation)**한다. OpenGitOps 프로젝트가 정의한 핵심 원칙은 다음과 같다.

  1. 선언적(Declarative): 시스템의 원하는 상태를 선언적으로 기술한다
  2. 버전 관리(Versioned and Immutable): Git에 모든 변경 이력이 남아 감사 추적이 가능하다
  3. 자동 적용(Pulled Automatically): 에이전트가 자동으로 원하는 상태를 클러스터에 적용한다
  4. 지속적 조정(Continuously Reconciled): 드리프트를 자동으로 감지하고 수정한다

이 글에서는 GitOps를 구현하는 대표적인 두 가지 도구인 ArgoCDFluxCD의 아키텍처를 심층 비교하고, 실제 프로덕션 환경에서의 배포 전략, 시크릿 관리, 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 비교표

항목ArgoCDFluxCD
CNCF 등급GraduatedGraduated
웹 UIO (내장, 풍부한 기능)X (별도 Weave GitOps UI 사용)
CLIargocd CLIflux CLI
아키텍처모놀리식 (여러 컴포넌트가 하나로)마이크로서비스 (독립 컨트롤러)
CRDApplication, ApplicationSet, AppProjectGitRepository, Kustomization, HelmRelease 등
멀티 클러스터허브-스포크 모델클러스터별 Flux 설치
Helm 지원O (소스로 직접 참조)O (HelmRelease CRD)
KustomizeOO (네이티브)
RBAC자체 RBAC + SSO 통합Kubernetes 네이티브 RBAC
알림Notification ControllerNotification Controller
이미지 자동 업데이트Image Updater (별도 설치)Image Automation Controller
드리프트 감지O (실시간 UI 표시)O (이벤트 기반)
Sync Wave/순서Sync Wave + HookdependsOn 의존성
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의 완성이다.

참고자료