LLM WikiAccess-protected knowledge portal
← 스터디 홈
6편 · 약 27분

GitOps와 데이터 플랫폼: ArgoCD, FluxCD, Infrastructure as Code

배포 자동화의 다음 단계가 필요한 이유

데이터 플랫폼 팀이 CI/CD 파이프라인을 갖추고 나면 새로운 문제가 생긴다. 파이프라인은 병합 이벤트에 반응해서 배포를 밀어 넣는다(Push). 하지만 누군가 클러스터에 직접 접속해 설정을 바꾸거나, Helm 릴리스를 수동으로 업그레이드하거나, ConfigMap을 손으로 편집하면 그 변경은 Git에 없다. 파이프라인은 모른다. 클러스터 실제 상태와 Git이 선언한 상태가 분리되기 시작한다.

GitOps는 이 드리프트(drift) 문제를 구조적으로 해결한다. Git이 원하는 상태의 유일한 진실이 되고, 자동화된 에이전트가 실제 상태를 지속적으로 Git 쪽으로 당겨온다(Pull). 수동 변경은 다음 조정(reconciliation) 주기에 자동으로 되돌아간다.


GitOps 4대 원칙

CNCF OpenGitOps 워킹 그룹은 GitOps를 네 가지 원칙으로 정의한다.

  1. 선언적(Declarative): 시스템 전체 상태를 YAML, HCL 등 선언적 형식으로 기술한다.
  2. 버전 관리되고 변경 불가(Versioned and Immutable): 원하는 상태는 Git에 보관된다. 모든 변경이 커밋으로 추적된다.
  3. 자동으로 Pull됨(Pulled Automatically): 소프트웨어 에이전트가 Git에서 원하는 상태를 스스로 가져온다. 외부 시스템이 클러스터에 자격증명을 들고 접근하지 않는다.
  4. 지속적으로 조정됨(Continuously Reconciled): 에이전트가 실제 상태와 원하는 상태를 지속적으로 비교하고 차이를 수정한다.
Git Repository 원하는 상태 (YAML 매니페스트) GitOps 컨트롤러 (ArgoCD / FluxCD) ① Observe Git 커밋 해시 조회 ② Diff Git 상태 vs 클러스터 상태 ③ Act 차이만 적용 (server-side apply) ④ 상태 리포트 → 다음 주기 대기 Kubernetes Cluster (실제 상태) Pull (3분마다 / webhook) Apply (차이만 적용) 수동 변경 selfHeal: true → 다음 주기에 자동 복원 엔지니어 git push
GitOps Pull 모델과 조정 루프

이 루프가 핵심이다. 외부 시스템(GitHub Actions, Jenkins)이 클러스터 자격증명을 들고 push하는 대신, 클러스터 내부의 컨트롤러가 스스로 Git을 조회해서 필요한 부분만 적용한다. 클러스터 자격증명이 외부로 나가지 않는다.


ArgoCD: 데이터 플랫폼의 관제탑

ArgoCD(v3.4, CNCF Graduated)는 중앙집중형 컨트롤 플레인이다. 데이터 플랫폼 팀이 선호하는 이유는 풍부한 Web UI 때문이다. Airflow Deployment, Kafka StatefulSet, Spark Operator가 모두 하나의 화면에서 상태가 보이고, Sync 버튼 하나로 수동 배포 게이트를 만들 수 있다.

핵심 컴포넌트

컴포넌트역할
API ServergRPC + REST; 웹 UI, CLI, CI 연동
Application Controller핵심 조정 루프; Application CRD 관리
Repository ServerGit clone, Helm/Kustomize 렌더링, 매니페스트 캐시
ApplicationSet Controller여러 Application을 자동으로 생성
Redis렌더링 결과 캐시

Application CRD

ArgoCD에서 배포 단위는 Application CRD다.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: data-platform-airflow
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io   # cascading delete 보장
spec:
  project: data-platform                         # AppProject로 RBAC 격리
  source:
    repoURL: https://github.com/org/data-platform-gitops.git
    targetRevision: HEAD
    path: apps/airflow/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: data-platform
  syncPolicy:
    automated:
      prune: true          # Git에서 삭제된 리소스 제거
      selfHeal: true       # 수동 변경 자동 복원
      allowEmpty: false    # 레포가 비어 있으면 전체 삭제 방지
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true   # 대형 매니페스트/CRD에 권장
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas    # HPA가 관리하는 replicas는 무시

selfHeal: true: 누군가 kubectl edit으로 직접 바꿔도 다음 조정 주기(기본 3분)에 Git 상태로 복원된다.

prune: true: Git에서 파일을 삭제하면 클러스터 리소스도 함께 삭제된다. 의도치 않은 삭제를 막으려면 allowEmpty: false를 함께 쓴다.

Sync Wave: 배포 순서 제어

하나의 Application 내에서 리소스 배포 순서를 제어할 때 sync wave를 쓴다.

# 네임스페이스 먼저 (wave -1)
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "-1"

# 데이터베이스 (wave 1)
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "1"

# 애플리케이션 (wave 2, DB 기동 후)
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "2"

App of Apps: 데이터 스택 전체를 하나로

데이터 플랫폼에는 Airflow, Kafka, Spark, dbt CronJob 등 여러 컴포넌트가 있다. "루트 App"이 다른 Application 매니페스트 디렉터리를 가리키고, ArgoCD가 재귀적으로 각 컴포넌트를 관리한다.

# 루트 Application — apps/ 디렉터리 내 다른 Application YAML들을 관리
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: data-platform-root
  namespace: argocd
spec:
  source:
    repoURL: https://github.com/org/gitops-repo.git
    targetRevision: HEAD
    path: apps/                   # Airflow, Kafka, Spark Application YAML들
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

롤백

# 배포 이력 확인
argocd app history data-platform-airflow
# ID  DATE                REVISION
# 5   2026-07-08 14:00   abc123f
# 4   2026-07-07 09:15   def456a  ← 롤백 목표

# 특정 히스토리로 롤백
argocd app rollback data-platform-airflow 4 --prune

selfHeal: true 상태에서 CLI 롤백은 다음 조정 주기에 Git 상태(최신)로 덮어쓰인다. 지속적인 롤백이 필요하면 Git에서 git revert하는 것이 정석이다.


FluxCD: 경량 모듈형 GitOps

FluxCD(v2.8, CNCF Graduated)는 단일체가 아니라 단일 책임 컨트롤러의 집합이다. ArgoCD보다 메모리 풋프린트가 작고, SOPS 시크릿 복호화가 기본 내장되어 있어 멀티 클러스터 운영이나 인프라 순수주의 팀에 잘 맞는다.

GitOps Toolkit 컨트롤러

컨트롤러CRD역할
source-controllerGitRepository, HelmRepositoryGit/OCI에서 아티팩트 가져오기
kustomize-controllerKustomizationKustomize 매니페스트 적용, SOPS 복호화
helm-controllerHelmReleaseHelm v4 릴리스 관리
notification-controllerAlert, ProviderSlack/PagerDuty 알림, GitHub webhook 수신
image-reflector-controllerImageRepository컨테이너 레지스트리에서 새 태그 스캔
image-automation-controllerImageUpdateAutomation새 태그를 Git 매니페스트에 커밋

Kustomization CRD 예시

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: data-platform-airflow
  namespace: flux-system
spec:
  interval: 15m
  path: "./apps/airflow/production"
  prune: true
  sourceRef:
    kind: GitRepository
    name: data-platform
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: airflow-webserver
      namespace: data-platform
  timeout: 5m
  decryption:
    provider: sops        # SOPS 복호화 기본 내장
    secretRef:
      name: sops-age-key
  dependsOn:
    - name: data-platform-infra   # 인프라 Kustomization 완료 후 실행

HelmRelease (FluxCD 2.8, Helm v4)

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: airflow
  namespace: data-platform
spec:
  interval: 15m
  chart:
    spec:
      chart: airflow
      version: "1.15.*"
      sourceRef:
        kind: HelmRepository
        name: apache-airflow
        namespace: flux-system
  upgrade:
    remediation:
      retries: 3
      strategy: rollback        # 업그레이드 실패 시 자동 롤백
      remediateLastFailure: true
  rollback:
    timeout: 5m
    cleanupOnFail: true

FluxCD HelmRelease는 업그레이드 실패 시 strategy: rollback이 자동으로 이전 Helm 릴리스로 되돌아간다. ArgoCD에는 없는 내장 기능이다.

SOPS 시크릿 관리 (FluxCD의 강점)

시크릿을 Git에 평문으로 커밋하는 것은 보안 위반이다. FluxCD는 Age 키를 사용한 SOPS 암호화를 기본 지원한다.

# Age 키 쌍 생성
age-keygen -o age.agekey

# 프라이빗 키를 클러스터 시크릿으로 등록
kubectl create secret generic sops-age-key \
  --namespace=flux-system \
  --from-file=age.agekey=./age.agekey

# 시크릿 파일 암호화 (퍼블릭 키로)
sops --age=age1<PUBLIC_KEY> \
  --encrypt --in-place secret.yaml

# 암호화된 YAML은 Git에 안전하게 커밋 가능
# kustomize-controller가 런타임에 클러스터 시크릿으로 복호화

ArgoCD vs FluxCD: 데이터 플랫폼 관점

항목ArgoCD v3.4FluxCD v2.8
아키텍처중앙집중형 (monolith-ish)모듈형 독립 컨트롤러
Web UI풍부한 토폴로지 뷰, 수동 Sync 게이트기본 수준 (Flux Operator 별도)
시크릿 관리ESO / Sealed Secrets 별도 필요SOPS 기본 내장
Helm 지원Helm 3 (Argo가 렌더링 후 적용)Helm v4 (FluxCD 2.8~), server-side apply
이미지 자동화argocd-image-updater (별도)기본 내장 컨트롤러
멀티 클러스터ApplicationSet + 허브 클러스터 (강함)클러스터별 bootstrap; 중앙 UI 없음
HelmRelease 자동 롤백없음 (CLI 수동)기본 내장 (strategy: rollback)
GitHub stars (2026)~23,100~8,180

데이터 플랫폼에서 ArgoCD를 선택할 때: 팀이 UI를 통한 시각적 확인과 수동 Sync 게이트(프로덕션 배포 전 사람 확인)를 중요하게 여길 때. Airflow, Kafka, Spark 상태를 한눈에 보고 싶을 때.

FluxCD를 선택할 때: 수십 개의 클러스터를 운영하거나, SOPS 기반 시크릿 관리가 필수이거나, Git 커밋만으로 완전 자동화를 원하는 팀.


Terraform + GitOps: 인프라 코드화

Kubernetes 워크로드는 ArgoCD/Flux가 관리하지만, EKS/GKE 클러스터 자체, RDS, MSK(Managed Kafka), S3 버킷 같은 클라우드 인프라는 Terraform이 담당한다.

원격 상태 백엔드

# backend.tf — S3 백엔드 (Terraform 1.10+)
terraform {
  backend "s3" {
    bucket       = "my-org-terraform-state"
    key          = "data-platform/production/terraform.tfstate"
    region       = "us-east-1"
    encrypt      = true
    use_lockfile = true   # Terraform 1.10+: DynamoDB 없이 네이티브 잠금
  }
}

Terraform 1.10부터 S3 네이티브 잠금을 지원한다. DynamoDB 기반 잠금은 deprecated됐다. 환경별(dev, staging, prod)로 state 버킷을 분리하고 S3 버전 관리를 켜두는 것이 기본이다.

Atlantis: PR 기반 인프라 자동화

Atlantis(v0.42.0)는 Terraform/OpenTofu의 planapply를 PR 워크플로에 통합한다. 엔지니어가 .tf 파일을 변경한 PR을 열면 Atlantis가 자동으로 plan을 실행하고 결과를 PR 코멘트로 올린다.

# atlantis.yaml — 프로젝트 설정
version: 3
projects:
  - name: data-platform-eks
    dir: terraform/eks
    workspace: production
    terraform_version: v1.10.3
    autoplan:
      when_modified:
        - "*.tf"
        - "../modules/**/*.tf"
      enabled: true
    apply_requirements:
      - approved       # PR 승인 필수
      - mergeable      # 브랜치 보호 규칙 통과 필수
# PR 코멘트로 실행하는 Atlantis 명령
atlantis plan          # plan 트리거 (PR 오픈 시 자동 실행)
atlantis apply         # 승인 후 apply
atlantis unlock        # 디렉터리 잠금 해제

Atlantis는 디렉터리 잠금을 걸기 때문에, 동일 디렉터리에 대한 두 PR이 동시에 apply될 수 없다. 데이터 플랫폼에서 인프라 충돌 사고를 방지하는 핵심 장치다.


데이터 플랫폼 GitOps 실전

Apache Airflow: Helm + ArgoCD

데이터 플랫폼 팀에서 가장 흔한 패턴은 두 개의 레포를 분리하는 것이다. "설정 레포"는 Helm values.yaml을 관리하고 ArgoCD가 바라본다. "DAG 레포"는 Python DAG 파일을 관리하고 git-sync 사이드카가 실시간으로 동기화한다.

# ArgoCD Application — Apache Airflow Helm chart
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: airflow
  namespace: argocd
spec:
  source:
    repoURL: https://airflow.apache.org/
    chart: airflow
    targetRevision: "1.15.0"
    helm:
      releaseName: airflow
      values: |
        executor: KubernetesExecutor
        dags:
          gitSync:
            enabled: true
            repo: https://github.com/org/airflow-dags.git
            branch: main
            period: 60s    # 60초마다 DAG 레포 동기화
        webserver:
          replicas: 2
        externalDatabase:
          type: postgres
          host: rds.example.com
  destination:
    server: https://kubernetes.default.svc
    namespace: data-platform
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

DAG 파일을 추가하거나 수정하면 git-sync 사이드카가 60초 내로 반영한다. ArgoCD sync가 필요 없다. Airflow 버전 자체를 올리려면 targetRevision을 바꿔 커밋하면 ArgoCD가 업그레이드를 처리한다.

dbt: Kubernetes CronJob으로 관리

dbt는 상주 서비스가 아니라 배치 잡이다. Kubernetes CronJob 정의를 GitOps 레포에 넣어 ArgoCD/Flux가 관리하게 한다.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: dbt-run
  namespace: data-platform
spec:
  schedule: "0 6 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: dbt
              image: registry.example.com/data-platform/dbt:1.8.2
              command: ["dbt", "build", "--profiles-dir", "/dbt/profiles"]
              envFrom:
                - secretRef:
                    name: dbt-database-credentials  # ESO가 관리
          restartPolicy: OnFailure

dbt 버전을 올리려면 image 태그만 변경해 커밋하면 된다. FluxCD를 쓴다면 image-automation-controller가 새 CI 빌드를 자동으로 감지해 태그를 직접 커밋해준다.

Kafka 토픽: Strimzi로 IaC화

Strimzi Operator(v1.1.0)를 쓰면 Kafka 토픽 생성/변경을 KafkaTopic CRD YAML로 선언할 수 있다. kafka-topics.sh 수동 명령은 필요 없다.

# kafka/topics/payment-events.yaml — GitOps 레포에 추가
apiVersion: kafka.strimzi.io/v1
kind: KafkaTopic
metadata:
  name: payment-events
  namespace: kafka
  labels:
    strimzi.io/cluster: data-platform-kafka
spec:
  partitions: 12
  replicas: 3
  config:
    retention.ms: "604800000"   # 7일
    cleanup.policy: delete

PR에 이 파일을 추가하고 머지하면 ArgoCD가 sync하고 Strimzi Entity Operator가 토픽을 생성한다. 모든 토픽 변경 이력이 Git에 남는다.


시크릿 관리: Git에 비밀을 담지 않기

Kubernetes Secret의 data 필드는 base64 인코딩일 뿐 암호화가 아니다. 평문을 Git에 올리는 것은 보안 사고다. 두 가지 선택지가 있다.

Option A: Sealed Secrets (Bitnami)

클러스터 내 컨트롤러가 프라이빗 키를 보유한다. kubeseal CLI가 퍼블릭 키로 암호화한 SealedSecret 리소스를 Git에 커밋한다. 컨트롤러만 복호화할 수 있다.

kubectl create secret generic db-credentials \
  --from-literal=password=supersecret \
  --dry-run=client -o yaml | \
kubeseal --format yaml \
  --controller-name=sealed-secrets-controller \
  --controller-namespace=kube-system \
  > sealed-secret.yaml

git add sealed-secret.yaml   # 안전하게 커밋 가능

주의: 컨트롤러 프라이빗 키를 잃으면 모든 SealedSecret을 재암호화해야 한다. 키 백업이 필수다.

Option B: External Secrets Operator (프로덕션 권장)

Git에는 ExternalSecret CRD 매니페스트(민감 정보 없음)만 커밋한다. ESO 컨트롤러가 런타임에 AWS Secrets Manager, GCP Secret Manager, Vault에서 실제 값을 가져와 Kubernetes Secret을 생성한다.

apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: airflow-db-credentials
  namespace: data-platform
spec:
  refreshInterval: 30m
  secretStoreRef:
    name: aws-secretsmanager
    kind: ClusterSecretStore
  target:
    name: airflow-db-secret
  data:
    - secretKey: password
      remoteRef:
        key: data-platform/airflow/db
        property: password
    - secretKey: username
      remoteRef:
        key: data-platform/airflow/db
        property: username

refreshInterval: 30m마다 Secrets Manager의 최신 값을 반영한다. 자격증명 로테이션이 자동으로 클러스터에 반영된다. ArgoCD가 Secret data 필드를 OutOfSync로 표시하지 않도록 ignoreDifferences를 설정해야 한다.


실전 워크플로: Kafka 업그레이드 + 신규 토픽 추가

data-platform-gitops/
├── apps/                        # ArgoCD App of Apps 매니페스트
│   ├── kafka.yaml
│   └── airflow.yaml
├── kafka/
│   ├── cluster/kafka-cluster.yaml    # Kafka 클러스터 정의
│   └── topics/
│       ├── user-events.yaml
│       └── payment-events.yaml      # 신규 추가
└── terraform/
    └── eks/
  1. 브랜치 생성 및 변경

``bash git checkout -b feat/kafka-upgrade-payment-topic # kafka-cluster.yaml: version 3.8.0 → 3.9.0 # kafka/topics/payment-events.yaml 신규 생성 git commit -m "feat: Kafka 3.9.0 업그레이드 + payment-events 토픽 추가" git push origin feat/kafka-upgrade-payment-topic ``

  1. PR CI 검증: kubeval로 매니페스트 유효성 검사, argocd app diff로 예상 변경 사항 미리보기
  1. PR 리뷰: Kafka 버전 변경 위험도, 토픽 파티션 수, 복제 인수 확인
  1. 머지 → ArgoCD 자동 감지: 3분 내 또는 webhook 즉시. Application 상태 OutOfSync
  1. Sync (자동 또는 수동 승인 후):

- Strimzi Cluster Operator가 브로커를 롤링 업그레이드 (min.insync.replicas 유지) - Strimzi Entity Operator가 payment-events 토픽 생성 - ArgoCD가 Kafka 헬스체크로 Ready=True 확인

  1. 감사 추적: Git 커밋 히스토리 + argocd app history kafka-platform → 누가, 언제, 무엇을 변경했는지 전부 추적 가능

References

  • OpenGitOps Principles: https://opengitops.dev/
  • Argo CD Docs — Automated Sync Policy: https://argo-cd.readthedocs.io/en/stable/user-guide/auto_sync/
  • Argo CD Docs — Sync Waves: https://argo-cd.readthedocs.io/en/stable/user-guide/sync-waves/
  • Argo CD Docs — Secret Management: https://argo-cd.readthedocs.io/en/stable/operator-manual/secret-management/
  • Announcing Argo CD v3: https://blog.argoproj.io/announcing-argo-cd-v3-small-but-mighty-df05c0b39ad6
  • Announcing Flux 2.8 GA: https://fluxcd.io/blog/2026/02/flux-v2.8.0/
  • FluxCD Kustomization CRD: https://fluxcd.io/flux/components/kustomize/kustomizations/
  • FluxCD Manage Helm Releases: https://fluxcd.io/flux/guides/helmreleases/
  • FluxCD Secrets with SOPS: https://fluxcd.io/flux/guides/mozilla-sops/
  • FluxCD Image Automation Controllers: https://fluxcd.io/flux/components/image/
  • ArgoCD vs FluxCD 2026 (dev.to): https://dev.to/mechcloud_academy/the-gitops-standard-in-2026-a-comparative-research-analysis-of-argocd-and-fluxcd-46d8
  • External Secrets Operator Overview: https://external-secrets.io/latest/introduction/overview/
  • Atlantis — Terraform Pull Request Automation: https://www.runatlantis.io/
  • Atlantis Practical Guide 2026 (env0): https://www.env0.com/blog/atlantis-terraform-guide
  • Terraform S3 Backend — native locking (1.10+): https://developer.hashicorp.com/terraform/language/backend/s3
  • Strimzi Operator Docs 1.1.0: https://strimzi.io/docs/operators/latest/overview
  • Airflow on Argo CD GitOps Guide: https://medium.com/ankercloud-engineering/airflow-on-argo-cd-a-step-by-step-gitops-guide-with-git-sync-for-dags-464bd0d7e2f5
  • GitOps Pull-Based vs Push-Based (ITNEXT): https://itnext.io/gitops-pull-based-vs-push-based-959c50feca78