K8s 고급 #6 GitOps — ArgoCD / Flux
K8s 고급 시리즈의 마지막 글입니다. #1 CNI부터 #5 옵저버빌리티까지 클러스터의 데이터 플레인,권한,정책,확장,관측을 한 층씩 쌓아 왔습니다. 이번 글은 그 모든 매니페스트가 클러스터에 들어가는 방식 자체를 다룹니다 — GitOps입니다. kubectl apply를 사람이 손으로 돌리는 대신, 매니페스트의 source of truth를 git에 두고 클러스터 안의 컨트롤러가 git을 watch해 자동으로 동기화하는 운영 모델입니다. ArgoCD와 Flux가 이 모델의 두 표준 구현체이고, 시리즈 마지막 글로 두 도구의 모델,운영 패턴,시리즈 회고,다음 트랙까지 한 사이클로 정리하겠습니다.
이번 시리즈는 K8s 고급 6편입니다.
- #1 CNI 깊이 — Calico / Cilium / eBPF
- #2 RBAC / ServiceAccount 깊이 — Aggregated ClusterRole / Impersonation / IRSA / Workload Identity
- #3 Admission Controller — OPA Gatekeeper / Kyverno
- #4 CRD와 Operator 패턴 — controller-runtime
- #5 옵저버빌리티 — Prometheus / Grafana / Loki / OpenTelemetry
- #6 GitOps — ArgoCD / Flux ← 이번 글
Push 모델과 Pull 모델 #
먼저 GitOps가 등장하기 전의 표준이었던 모델과 비교해 보겠습니다. CI/CD 파이프라인이 클러스터에 매니페스트를 적용하는 방식은 크게 두 갈래입니다.
| 모델 | 흐름 |
|---|---|
| Push | CI 파이프라인이 클러스터의 API 서버에 직접 kubectl apply. CI 시스템이 클러스터 자격 증명을 보유. |
| Pull (GitOps) | 클러스터 안의 컨트롤러가 git을 watch. 매니페스트가 변경되면 컨트롤러가 자동으로 동기화. |
전통적인 CD 파이프라인은 push 모델이었습니다. GitHub Actions나 Jenkins가 빌드 후 kubectl apply -f manifests/를 돌리고 끝이었습니다. 이 모델의 문제는 셋입니다.
- CI 시스템이 클러스터의 강한 자격 증명을 들고 있다 — CI 시스템이 침해당하면 클러스터도 침해됩니다.
- Drift가 보이지 않는다 — 누가 클러스터에 직접
kubectl edit을 돌리면 git의 매니페스트와 실제 클러스터가 어긋나는데, 그 어긋남을 감지할 표준 메커니즘이 없습니다. - 여러 클러스터로 확장이 어렵다 — 클러스터 N개에 같은 매니페스트를 적용하려면 N번
kubectl apply를 돌려야 합니다.
GitOps 모델은 이 셋을 한 번에 푼 방식입니다. 클러스터 안의 컨트롤러가 git을 watch하므로 외부에서 클러스터 자격 증명을 들고 있을 필요가 없고, 그 컨트롤러가 동기화 상태를 계속 보고 있으므로 drift를 자동으로 감지하며, 각 클러스터가 자기 git을 watch하는 모델이라 N개 확장이 자연스럽습니다.
GitOps의 네 원칙 #
OpenGitOps 프로젝트가 정리한 GitOps의 네 원칙은 다음과 같습니다.
| 원칙 | 의미 |
|---|---|
| Declarative | 시스템의 desired state가 선언적으로 표현된다 |
| Versioned and Immutable | desired state가 git 같은 변경 불가능한 저장소에 보관된다 |
| Pulled Automatically | 승인된 변경사항이 자동으로 시스템에 적용된다 |
| Continuously Reconciled | 컨트롤러가 desired state와 실제 state의 차이를 계속 좁힌다 |
K8s의 매니페스트는 declarative이고, git은 versioned + immutable입니다. ArgoCD와 Flux가 그 위에 pull + reconciliation을 더해 GitOps를 완성합니다.
ArgoCD — Application CRD 중심의 모델 #
ArgoCD는 Intuit가 만들어 CNCF에 기증한 GitOps 도구입니다. 가장 큰 특징은 풍부한 웹 UI입니다. 클러스터의 모든 Application의 동기화 상태, drift, 매니페스트 변경 내역을 한 화면에서 볼 수 있어서 운영 팀의 진입 장벽이 낮습니다.
Application CRD — ArgoCD의 단위 #
ArgoCD가 git에서 매니페스트를 가져와 클러스터에 동기화하는 단위가 Application CRD입니다.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/manifests.git
targetRevision: main
path: apps/my-app/overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true이 매니페스트가 ArgoCD에 적용되면 다음 일이 자동으로 일어납니다.
- ArgoCD 컨트롤러가
repoURL의path디렉터리를 git에서 가져옴 - Kustomize / Helm / 일반 YAML을 자동 인식해서 매니페스트를 렌더링
- 클러스터의
destination에 동기화 (automated.selfHeal: true이므로 drift를 자동 복구) - git의 매니페스트가 변경되면 자동으로 다시 동기화 (
automated) - git에서 사라진 객체는 클러스터에서도 삭제 (
prune: true)
App of Apps — Application의 묶음 관리 #
여러 Application을 한곳에서 관리하는 패턴이 App of Apps입니다. 한 Application의 source가 다른 Application 매니페스트들이 들어 있는 디렉터리를 가리키는 구조입니다.
manifests/
apps/
root.yaml ← 루트 Application (ArgoCD에 manual 적용)
children/
app-a.yaml ← Application: app-a
app-b.yaml ← Application: app-b
app-c.yaml ← Application: app-c
...루트 Application 한 개만 ArgoCD에 처음 등록하면, 그 안에서 자식 Application들이 차례로 만들어지고, 각 자식 Application이 다시 자기 매니페스트를 동기화합니다. 클러스터에 새 앱을 추가하려면 git에 새 자식 Application 한 장만 추가하면 끝입니다.
Sync Wave — 순서 있는 적용 #
매니페스트 적용에 순서가 필요한 경우가 있습니다 — Namespace를 먼저 만들고, 그 안에 ConfigMap을 만들고, 그다음에 Deployment를 띄우는 순서. ArgoCD는 annotation으로 이 순서를 표현합니다.
metadata:
annotations:
argocd.argoproj.io/sync-wave: "0" # Namespace
---
metadata:
annotations:
argocd.argoproj.io/sync-wave: "1" # ConfigMap, Secret
---
metadata:
annotations:
argocd.argoproj.io/sync-wave: "2" # Deployment, StatefulSet낮은 wave가 먼저 적용되고, 그 wave의 객체가 모두 healthy 상태가 된 뒤 다음 wave가 진행됩니다. CRD를 먼저 만들고 그 CRD의 인스턴스를 적용하는 패턴이 가장 자주 만나는 사용처입니다.
Flux — 작은 컴포넌트의 묶음 #
Flux는 Weaveworks가 만든 GitOps 도구로 ArgoCD와 같은 카테고리의 도구이지만 접근이 다릅니다. Flux v2는 한 큰 컴포넌트가 아니라 여러 작은 컨트롤러의 묶음으로 설계되어 있습니다.
| Flux 컨트롤러 | 역할 |
|---|---|
| source-controller | git / Helm 저장소 / OCI 이미지에서 매니페스트 fetching |
| kustomize-controller | Kustomize 매니페스트 적용 |
| helm-controller | HelmRelease 객체로 Helm 차트 적용 |
| notification-controller | Slack / Teams / GitHub 등에 이벤트 통지 |
| image-automation-controller | 컨테이너 이미지 새 버전을 git에 자동 commit |
각 컨트롤러가 자기 CRD를 갖고, 그 CRD의 매니페스트로 모든 동작이 표현됩니다.
GitRepository + Kustomization — Flux의 기본 묶음 #
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: manifests
namespace: flux-system
spec:
interval: 1m
url: https://github.com/myorg/manifests.git
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-app
namespace: flux-system
spec:
interval: 5m
path: ./apps/my-app/overlays/prod
prune: true
sourceRef:
kind: GitRepository
name: manifests
targetNamespace: my-appGitRepository가 git을 watch하고, Kustomization이 그 git의 한 디렉터리를 클러스터에 적용합니다. 두 객체의 분리 덕에 같은 git을 여러 Kustomization이 다른 path로 가리킬 수 있습니다.
HelmRelease — Helm 차트의 GitOps화 #
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: prometheus
namespace: monitoring
spec:
interval: 10m
chart:
spec:
chart: kube-prometheus-stack
version: "55.x"
sourceRef:
kind: HelmRepository
name: prometheus-community
values:
prometheus:
prometheusSpec:
retention: 30dhelm install을 손으로 돌리는 대신 HelmRelease 매니페스트로 표현하면 Helm 차트의 설치,업그레이드도 GitOps의 흐름 안으로 들어옵니다.
ArgoCD vs Flux — 선택의 결 #
| 차원 | ArgoCD | Flux |
|---|---|---|
| 모델 | 한 큰 컴포넌트 + 풍부한 UI | 작은 컨트롤러의 묶음 + CLI 중심 |
| 진입 장벽 | 낮음 (UI로 시작) | 중간 (CRD 매니페스트로 시작) |
| 멀티테넌시 | AppProject로 표현 | namespace 단위 분리 |
| 멀티클러스터 | 한 ArgoCD가 여러 클러스터 관리 가능 | 각 클러스터에 Flux 한 개 (hub-spoke 가능) |
| Helm 지원 | 1급 | HelmRelease CRD로 1급 |
| 이미지 자동 업데이트 | argocd-image-updater (별도) | image-automation-controller (내장) |
선택 기준은 보통 다음을 따라갑니다.
- 운영 팀이 GUI를 선호하고, 한 화면에서 모든 클러스터를 보고 싶다면 ArgoCD가 자연스럽습니다.
- 모든 운영을 매니페스트로 표현하고 싶고, 작은 컴포넌트의 묶음을 선호한다면 Flux가 잘 맞습니다.
두 도구 모두 CNCF 졸업 프로젝트이고 운영 규모로 굳어 있으니, 한쪽을 잘못 골라 큰 사고가 나는 종류의 결정은 아닙니다.
디렉터리 구조 패턴 #
GitOps repo의 디렉터리 구조는 운영 팀의 스타일에 따라 갈리지만, 자주 보는 패턴 두 가지가 있습니다.
1. env-per-folder — 환경별 분기 #
manifests/
base/
my-app/
deployment.yaml
service.yaml
envs/
dev/
kustomization.yaml ← base + dev patch
staging/
kustomization.yaml
prod/
kustomization.yamlKustomize의 base + overlay 패턴을 그대로 활용한 구조입니다. 환경별 차이(replicas, image tag, resources)가 overlay에 패치로 들어갑니다.
2. app-per-folder + branch-per-env #
manifests/ (main branch = prod)
apps/
my-app/
deployment.yaml
service.yaml
other-app/
...
manifests-dev/ (dev branch)
manifests-staging/ (staging branch)브랜치를 환경 분기로 쓰는 모델입니다. 환경 차이가 commit으로 표현되어 audit이 자연스럽지만, 브랜치 사이의 sync 부담이 큽니다.
운영에서는 env-per-folder가 더 자주 쓰입니다. 변경의 흐름이 한 브랜치(main) 안에서 일어나므로 PR 리뷰가 단순합니다.
Secret을 git에 어떻게 올릴까 #
GitOps의 큰 숙제 중 하나가 비밀을 git에 두는 길입니다. K8s Secret을 평문으로 git에 올릴 수는 없습니다. 표준 도구가 셋 있습니다.
| 도구 | 모델 |
|---|---|
| Sealed Secrets | Bitnami가 만든 도구. 비밀을 SealedSecret으로 암호화해 git에 올림. 클러스터 안의 컨트롤러가 자기 키로만 복호화 가능. |
| External Secrets Operator | git에는 외부 비밀 저장소(AWS Secrets Manager, Vault 등)의 참조만 두고, 컨트롤러가 K8s Secret으로 동기화. |
| SOPS + age/PGP | git에 암호화된 YAML을 직접 올림. ArgoCD / Flux 모두 SOPS 통합 지원. |
External Secrets Operator가 가장 자주 쓰이는 길입니다. 비밀의 source of truth가 외부 저장소에 있고, K8s에는 그 참조만 들어오므로 비밀 회전이 외부 저장소에서 한 번에 끝납니다. #2 IRSA와 결합하면 비밀 저장소 접근 자격 증명조차 클러스터 안에 정적으로 두지 않을 수 있습니다.
운영 시 잡아 둘 원칙 #
1. auto-sync vs manual sync — 환경별 분기 #
syncPolicy.automated를 켜 두면 git의 변경이 즉시 클러스터에 반영됩니다. dev / staging은 자동, prod는 수동(또는 PR 머지 후 자동)으로 갈래를 두는 게 일반적입니다. prod에 자동 sync를 걸 때는 syncOptions: PruneLast=true로 삭제를 마지막에 처리하는 등의 안전장치를 같이 잡습니다.
2. drift detection의 의미 #
GitOps 컨트롤러는 git과 실제 클러스터의 차이를 계속 비교합니다. 누가 kubectl edit으로 직접 수정하면 그 변경은 즉시 drift로 감지되고, selfHeal: true이면 git의 매니페스트로 다시 덮어씁니다. 이게 GitOps의 강점이지만, 동시에 함정이기도 합니다 — 컨트롤러가 자동 생성하는 필드(status, 자동 라벨)는 drift처럼 보이면 안 됩니다. ArgoCD / Flux 모두 ignoreDifferences 설정으로 무시할 필드를 적을 수 있습니다.
3. Helm value 변경의 영향 #
HelmRelease의 values를 바꾸면 그 차트가 만든 모든 객체가 재배포됩니다. 의도치 않은 재배포가 일어나지 않도록 values 변경의 영향 범위를 PR 단계에서 dry-run으로 미리 확인하는 게 좋습니다.
4. 멀티클러스터의 hub-spoke 모델 #
클러스터 N개를 GitOps로 관리할 때 표준 모델 둘이 있습니다.
- 각 클러스터가 자기 GitOps 컨트롤러를 가짐 — 클러스터 자기완결적, 외부 의존성 적음
- 한 hub 클러스터의 GitOps 컨트롤러가 spoke 클러스터들을 관리 — 운영 단순화, hub의 가용성이 critical
ArgoCD는 두 모델 모두 잘 맞고, Flux는 첫 번째 모델이 자연스럽습니다.
시리즈 회고 — K8s 고급 6편으로 손에 들어온 것 #
마지막 글이므로 6편을 한번 짚어 두겠습니다.
- #1 — CNI 깊이. K8s 네트워크 모델의 4조건, CNI 인터페이스, iptables / IPVS / eBPF의 데이터 플레인, Calico와 Cilium의 비교.
- #2 — RBAC / ServiceAccount 깊이. Aggregated ClusterRole, Impersonation, projected token, IRSA / Workload Identity로 K8s ServiceAccount를 클라우드 IAM과 연결.
- #3 — Admission Controller. API 서버의 5단계 흐름, mutating / validating webhook, OPA Gatekeeper와 Kyverno의 정책 엔진 비교.
- #4 — CRD와 Operator 패턴. K8s API 자체를 확장하는 길, controller-runtime 기반 Operator의 골격, ownerReference / finalizer / status subresource.
- #5 — 옵저버빌리티. 메트릭 / 로그 / 트레이스의 세 축, Prometheus + kube-state-metrics, Loki, OpenTelemetry, Grafana, Alertmanager.
- #6 — GitOps. 매니페스트의 source of truth를 git에 두는 운영 모델, ArgoCD와 Flux, 디렉터리 구조와 비밀 관리.
기초 시리즈가 매니페스트 한 장의 모델을, 중급 시리즈가 그 매니페스트가 운영 클러스터에서 굴러가는 깊이를, 고급 시리즈가 그 위에 정책 엔진,확장,관측,동기화의 깊이를 한 층씩 더했습니다. 이 20편을 다 따라온 시점이라면 K8s를 도입하고 운영하는 사람의 시야 — “어떤 CNI를 도입할지, 어떤 정책 엔진을 도입할지, 어떤 옵저버빌리티 스택을 고를지, GitOps 파이프라인을 어떻게 짤지"를 결정하는 단계의 시야가 손에 들어옵니다.
다음 트랙 — K8s 실전 #
고급 시리즈까지가 K8s의 객체 모델과 정책의 깊이를 다뤘다면, K8s 실전 6편은 그 위에 진짜 서비스를 한 개 올리고 운영하는 한 사이클입니다. 중급 #7에서 미리 정리한 표를 다시 가져오면 다음과 같습니다.
| 주제 | 설명 |
|---|---|
| EKS 클러스터 셋업 | AWS EKS 클러스터를 처음부터, IAM, VPC, 노드 그룹, 애드온. |
| 앱 배포 골격 | Deployment + Service + Ingress + ConfigMap + Secret의 한 묶음, Helm 차트로 정리. |
| DB 연동 | RDS / Aurora를 Pod에서 안전하게 부르는 길, Secrets Manager 통합, 커넥션 풀. |
| CI/CD 파이프라인 | GitHub Actions에서 컨테이너 빌드 → ECR push → ArgoCD sync. |
| 모니터링,알람 | CloudWatch + Prometheus, 핵심 알람 룰셋, on-call 흐름. |
| 운영 체크리스트 | 업그레이드, 백업,복구, 비용 점검, 보안 점검의 정기 운영 사이클. |
기초,중급,고급 트랙이 K8s를 매니페스트 차원에서 이해하는 길이었다면, 실전 트랙은 EKS 위에 한 서비스를 처음부터 끝까지 따라가는 길입니다. 추상이 아니라 진짜 도입 사례로 트랙을 닫습니다.
마무리 #
K8s 고급 시리즈 6편을 마무리합니다. 이번 글에서는 매니페스트의 source of truth를 git에 두는 GitOps 모델 — push 대 pull, ArgoCD의 Application CRD와 sync wave, Flux의 작은 컨트롤러 묶음, 디렉터리 구조, Sealed Secrets / External Secrets로 비밀을 git에 두는 방법까지를 한 사이클로 정리했습니다. 시리즈 전체로 보면, 기초 7편,중급 7편이 매니페스트와 그 운영의 깊이였다면, 고급 6편은 그 위에 정책,확장,관측,동기화의 결을 한 층씩 더한 단계였습니다. 다음 트랙인 K8s 실전 6편에서는 EKS 클러스터 셋업부터 앱 배포 골격, DB 연동, CI/CD 파이프라인, 모니터링,알람, 운영 체크리스트까지 — 진짜 서비스를 한 개 올리는 한 사이클을 처음부터 끝까지 따라가겠습니다.