GitOps와 데이터 플랫폼: ArgoCD, FluxCD, Infrastructure as Code
배포 자동화의 다음 단계가 필요한 이유
데이터 플랫폼 팀이 CI/CD 파이프라인을 갖추고 나면 새로운 문제가 생긴다. 파이프라인은 병합 이벤트에 반응해서 배포를 밀어 넣는다(Push). 하지만 누군가 클러스터에 직접 접속해 설정을 바꾸거나, Helm 릴리스를 수동으로 업그레이드하거나, ConfigMap을 손으로 편집하면 그 변경은 Git에 없다. 파이프라인은 모른다. 클러스터 실제 상태와 Git이 선언한 상태가 분리되기 시작한다.
GitOps는 이 드리프트(drift) 문제를 구조적으로 해결한다. Git이 원하는 상태의 유일한 진실이 되고, 자동화된 에이전트가 실제 상태를 지속적으로 Git 쪽으로 당겨온다(Pull). 수동 변경은 다음 조정(reconciliation) 주기에 자동으로 되돌아간다.
GitOps 4대 원칙
CNCF OpenGitOps 워킹 그룹은 GitOps를 네 가지 원칙으로 정의한다.
- 선언적(Declarative): 시스템 전체 상태를 YAML, HCL 등 선언적 형식으로 기술한다.
- 버전 관리되고 변경 불가(Versioned and Immutable): 원하는 상태는 Git에 보관된다. 모든 변경이 커밋으로 추적된다.
- 자동으로 Pull됨(Pulled Automatically): 소프트웨어 에이전트가 Git에서 원하는 상태를 스스로 가져온다. 외부 시스템이 클러스터에 자격증명을 들고 접근하지 않는다.
- 지속적으로 조정됨(Continuously Reconciled): 에이전트가 실제 상태와 원하는 상태를 지속적으로 비교하고 차이를 수정한다.
이 루프가 핵심이다. 외부 시스템(GitHub Actions, Jenkins)이 클러스터 자격증명을 들고 push하는 대신, 클러스터 내부의 컨트롤러가 스스로 Git을 조회해서 필요한 부분만 적용한다. 클러스터 자격증명이 외부로 나가지 않는다.
ArgoCD: 데이터 플랫폼의 관제탑
ArgoCD(v3.4, CNCF Graduated)는 중앙집중형 컨트롤 플레인이다. 데이터 플랫폼 팀이 선호하는 이유는 풍부한 Web UI 때문이다. Airflow Deployment, Kafka StatefulSet, Spark Operator가 모두 하나의 화면에서 상태가 보이고, Sync 버튼 하나로 수동 배포 게이트를 만들 수 있다.
핵심 컴포넌트
| 컴포넌트 | 역할 |
|---|---|
| API Server | gRPC + REST; 웹 UI, CLI, CI 연동 |
| Application Controller | 핵심 조정 루프; Application CRD 관리 |
| Repository Server | Git 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 --pruneselfHeal: true 상태에서 CLI 롤백은 다음 조정 주기에 Git 상태(최신)로 덮어쓰인다. 지속적인 롤백이 필요하면 Git에서 git revert하는 것이 정석이다.
FluxCD: 경량 모듈형 GitOps
FluxCD(v2.8, CNCF Graduated)는 단일체가 아니라 단일 책임 컨트롤러의 집합이다. ArgoCD보다 메모리 풋프린트가 작고, SOPS 시크릿 복호화가 기본 내장되어 있어 멀티 클러스터 운영이나 인프라 순수주의 팀에 잘 맞는다.
GitOps Toolkit 컨트롤러
| 컨트롤러 | CRD | 역할 |
|---|---|---|
| source-controller | GitRepository, HelmRepository | Git/OCI에서 아티팩트 가져오기 |
| kustomize-controller | Kustomization | Kustomize 매니페스트 적용, SOPS 복호화 |
| helm-controller | HelmRelease | Helm v4 릴리스 관리 |
| notification-controller | Alert, Provider | Slack/PagerDuty 알림, GitHub webhook 수신 |
| image-reflector-controller | ImageRepository | 컨테이너 레지스트리에서 새 태그 스캔 |
| image-automation-controller | ImageUpdateAutomation | 새 태그를 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: trueFluxCD 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.4 | FluxCD 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의 plan과 apply를 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: trueDAG 파일을 추가하거나 수정하면 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: OnFailuredbt 버전을 올리려면 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: deletePR에 이 파일을 추가하고 머지하면 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: usernamerefreshInterval: 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/- 브랜치 생성 및 변경
``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 ``
- PR CI 검증:
kubeval로 매니페스트 유효성 검사,argocd app diff로 예상 변경 사항 미리보기
- PR 리뷰: Kafka 버전 변경 위험도, 토픽 파티션 수, 복제 인수 확인
- 머지 → ArgoCD 자동 감지: 3분 내 또는 webhook 즉시. Application 상태
OutOfSync
- Sync (자동 또는 수동 승인 후):
- Strimzi Cluster Operator가 브로커를 롤링 업그레이드 (min.insync.replicas 유지) - Strimzi Entity Operator가 payment-events 토픽 생성 - ArgoCD가 Kafka 헬스체크로 Ready=True 확인
- 감사 추적: 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