Skip to content

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

✨ Learn with Quiz
|

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

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의 완성이다.

참고자료

GitOps in Practice: ArgoCD vs FluxCD Architecture Comparison and Production Deployment Strategies

GitOps ArgoCD vs FluxCD

Introduction

In modern cloud-native environments, methods for managing Kubernetes deployments broadly fall into push-based and pull-based approaches. Traditional CI/CD pipelines use the push approach, running kubectl apply or helm upgrade to push changes into the cluster after builds complete. This approach has fundamental limitations: it requires granting cluster access credentials to CI servers, and it is difficult to detect drift caused by manual changes (kubectl edit, console modifications).

GitOps is an operational paradigm that addresses these problems by treating the Git repository as the Single Source of Truth, where an agent inside the cluster continuously compares the desired state in Git with the actual state and automatically synchronizes (Reconciliation) them. The core principles defined by the OpenGitOps project are:

  1. Declarative: The desired state of the system is described declaratively
  2. Versioned and Immutable: All change history remains in Git, enabling audit trails
  3. Pulled Automatically: Agents automatically apply the desired state to the cluster
  4. Continuously Reconciled: Drift is automatically detected and corrected

This article provides an in-depth comparison of ArgoCD and FluxCD architectures, the two primary tools for implementing GitOps, along with production deployment strategies, secret management, CI/CD integration, failure cases and recovery, and operational checklists.

ArgoCD Architecture Deep Dive

Core Components

ArgoCD is a CNCF Graduated project and a Kubernetes-native GitOps continuous deployment tool. It provides a web UI, CLI, and gRPC/REST API by default.

# ArgoCD Application resource example
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

The major components of ArgoCD are:

  • API Server: Provides gRPC/REST APIs used by the web UI, CLI, and CI/CD systems
  • Repo Server: Clones Git repositories and generates manifests. Supports Helm, Kustomize, Jsonnet, and more
  • Application Controller: Watches Application resources and runs reconciliation loops
  • Redis: Stores UI sessions and cache
  • Dex: Handles SSO (Single Sign-On) authentication
  • Notifications Controller: Sends sync status notifications via Slack, email, etc.

App of Apps Pattern

In large-scale environments, the App of Apps pattern manages multiple Applications through a single parent Application.

# Parent 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

By placing Application YAML files for each service inside the apps directory, root-app automatically creates and manages them.

ApplicationSet Controller

ApplicationSet dynamically creates multiple Applications from a single template. It is extremely useful in multi-cluster, multi-tenant, and monorepo environments.

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

This ApplicationSet automatically creates Applications for all clusters with the environment: production label.

Sync Waves and Hooks

ArgoCD's Sync Waves provide precise control over resource deployment order.

# Wave 0: Create Namespace and RBAC first
apiVersion: v1
kind: Namespace
metadata:
  name: production
  annotations:
    argocd.argoproj.io/sync-wave: '0'

---
# Wave 1: ConfigMap and 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 Migration
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 Architecture Deep Dive

Core Components

FluxCD is a CNCF Graduated project and a Kubernetes-native GitOps toolkit. It consists of several independent controllers.

  • Source Controller: Manages sources including Git repositories, Helm repositories, OCI artifacts, and S3 buckets
  • Kustomize Controller: Watches Kustomization resources and applies manifests to the cluster
  • Helm Controller: Watches HelmRelease resources and manages Helm charts
  • Notification Controller: Handles event notifications and external webhook reception
  • Image Reflector/Automation Controllers: Detect new images in container registries and automatically update Git

GitRepository and Kustomization

FluxCD's basic workflow defines sources via GitRepository and describes how to apply them via Kustomization.

# Source definition
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: |
    # Exclude unnecessary files
    docs/
    README.md

---
# Application definition
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 Controller

FluxCD's Helm Controller declaratively manages the lifecycle of Helm charts through the HelmRelease CRD.

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 Dependency Management

Dependencies between Kustomization resources can be declared to control deployment order.

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

The applications Kustomization is only applied after the infrastructure Kustomization has been successfully applied.

ArgoCD vs FluxCD Comparison

FeatureArgoCDFluxCD
CNCF StatusGraduatedGraduated
Web UIYes (built-in, feature-rich)No (separate Weave GitOps UI available)
CLIargocd CLIflux CLI
ArchitectureMonolithic (multiple components as one)Microservices (independent controllers)
CRDsApplication, ApplicationSet, AppProjectGitRepository, Kustomization, HelmRelease, etc.
Multi-clusterHub-spoke modelFlux installed per cluster
Helm SupportYes (direct source reference)Yes (HelmRelease CRD)
KustomizeYesYes (native)
RBACCustom RBAC + SSO integrationKubernetes native RBAC
NotificationsNotification ControllerNotification Controller
Image Auto-updateImage Updater (separate install)Image Automation Controller
Drift DetectionYes (real-time UI display)Yes (event-based)
Sync OrderSync Wave + HookdependsOn dependencies
SSO SupportYes (Dex built-in)No (delegates to Kubernetes RBAC)
Multi-tenancyAppProject-basedNamespace-based
Git SupportGitHub, GitLab, Bitbucket, etc.GitHub, GitLab, Bitbucket, S3, OCI, etc.
Learning CurveMedium (UI helps)Higher (CLI-centric)
Resource UsageRelatively higherRelatively lower

Deployment Strategies

Blue-Green Deployment (Argo Rollouts)

Using Argo Rollouts with ArgoCD enables declarative Blue-Green deployments.

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 Deployment (Flagger + FluxCD)

In FluxCD environments, Flagger automates canary deployments.

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/'

Secret Management

SOPS (Secrets OPerationS)

SOPS is a tool that encrypts secrets at the file level for safe storage in Git. FluxCD natively supports SOPS.

# .sops.yaml (placed at repository root)
creation_rules:
  - path_regex: .*.enc.yaml
    encrypted_regex: '^(data|stringData)$'
    age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Encrypt secrets
sops --encrypt --in-place secrets/production/db-credentials.enc.yaml

# Enable SOPS decryption in FluxCD Kustomization
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 uses asymmetric encryption to safely store secrets in Git.

# Create 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 Integration (ArgoCD Vault Plugin)

Integrate HashiCorp Vault with ArgoCD for dynamic secret injection.

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>

In the manifest above, <username> and <password> are placeholders that the ArgoCD Vault Plugin replaces with actual secret values retrieved from Vault.

CI/CD Integration Patterns

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

In this pattern, CI (GitHub Actions) is only responsible for building images and updating the manifest repository; the actual deployment is performed automatically by ArgoCD when it detects the Git change.

Failure Cases and Recovery Procedures

Case 1: Sync Loop (Infinite Synchronization Loop)

Situation: An ArgoCD Application transitioned to OutOfSync immediately after Sync completion, entering an infinite sync loop. The root cause was an Admission Webhook injecting default values into resources, creating differences from the Git manifests.

Symptoms:

  • Application status alternating between Synced and OutOfSync
  • ArgoCD API Server CPU usage spike
  • Repo Server memory usage increase

Recovery Procedure:

# 1. Pause auto-sync
argocd app set my-app --sync-policy none

# 2. Analyze drift cause
argocd app diff my-app --local ./manifests/

# 3. Set ignoreDifferences to ignore webhook-injected fields
argocd app set my-app --ignore-differences \
  'group=apps, kind=Deployment, jsonPointers=[/spec/template/metadata/annotations]'

# 4. Explicitly add webhook-injected defaults to manifests

# 5. Re-enable auto-sync
argocd app set my-app --sync-policy automated

Case 2: Drift Detection Failure

Situation: FluxCD Kustomization reported a success state, but manually changed resources in the actual cluster were not restored. The force option was disabled, and manual changes occurred in fields not managed by FluxCD.

Symptoms:

  • Kustomization status is Ready
  • Actual resource configuration differs from Git
  • Differences visible via kubectl diff

Recovery Procedure:

# 1. Trigger forced FluxCD reconciliation
flux reconcile kustomization production-apps --with-source

# 2. Enable force option (use server-side apply)
flux create kustomization production-apps \
  --source=GitRepository/k8s-manifests \
  --path="./apps/production" \
  --prune=true \
  --force=true \
  --interval=5m

# 3. Add OPA policies to prevent manual changes
# (restrict kubectl edit, kubectl patch, etc.)

Case 3: Helm Release Rollback Failure

Situation: A FluxCD HelmRelease upgrade failed but auto-rollback did not trigger. The remediateLastFailure option was set to false in the remediation configuration.

Symptoms:

  • HelmRelease status is Failed
  • Previous release version is also corrupted
  • Failed releases accumulating in Helm history

Recovery Procedure:

# 1. Check HelmRelease status
flux get helmrelease -n production

# 2. Check Helm release history
helm history postgresql -n production

# 3. Manual rollback
helm rollback postgresql 3 -n production

# 4. Add auto-remediation to HelmRelease configuration
flux create helmrelease postgresql \
  --source=HelmRepository/bitnami \
  --chart=postgresql \
  --chart-version="15.x" \
  --target-namespace=production \
  --values=values/postgresql.yaml \
  --remediation-retries=3

Production Deployment Checklist

Pre-Deployment

  • Design Git repository structure (monorepo vs polyrepo decision)
  • Establish branch strategy (main -> staging -> production)
  • Design RBAC policies (which teams manage which Applications/Kustomizations)
  • Choose secret management method (SOPS, Sealed Secrets, or Vault)

ArgoCD Configuration

  • Deploy in HA (High Availability) mode (minimum 3 replicas)
  • Configure Redis Sentinel or Redis Cluster
  • Set up SSO via Dex or OIDC
  • Restrict source repositories and target clusters per AppProject
  • Clarify management scope with Resource Exclusions/Inclusions

FluxCD Configuration

  • Install independent FluxCD on each cluster
  • Optimize Source Controller Git polling interval (default 1 minute)
  • Specify health check target resources in Kustomizations
  • Configure Slack/Teams notifications via Notification Controller

Operational Notes

  • Minimize write access to Git repositories (PR-based changes)
  • Require PR review before production deployments
  • Tune sync intervals and timeouts to match workload characteristics
  • Ensure ordering with Sync Waves or dependsOn for large manifest changes
  • Pin Helm chart versions instead of using version ranges
  • Document Git revert processes for rollbacks

Monitoring

  • ArgoCD: Collect Application sync status metrics (argocd_app_info)
  • FluxCD: Collect Kustomization/HelmRelease status metrics
  • Configure automatic alerts on sync failures (PagerDuty, OpsGenie integration)
  • Generate periodic drift audit reports

Conclusion

GitOps has become the standard for Kubernetes deployment management. Both ArgoCD and FluxCD, as CNCF Graduated projects, have mature ecosystems, and either tool can faithfully implement the core principles of GitOps.

ArgoCD excels with its rich web UI, multi-cluster management through ApplicationSet, and integration with Argo Rollouts. FluxCD excels with its Kubernetes-native microservice architecture, lightweight resource usage, and native SOPS support.

What matters more than the choice of tool is the establishment of a GitOps culture. Building processes where all changes go through Git, constructing guardrails to prevent manual changes, and establishing automation systems that continuously detect and correct drift are what truly complete GitOps.

References