K8s 고급 #6 GitOps — ArgoCD / Flux

11 분 소요

K8s 고급 시리즈의 마지막 글입니다. #1 CNI부터 #5 옵저버빌리티까지 클러스터의 데이터 플레인,권한,정책,확장,관측을 한 층씩 쌓아 왔습니다. 이번 글은 그 모든 매니페스트가 클러스터에 들어가는 방식 자체를 다룹니다 — GitOps입니다. kubectl apply를 사람이 손으로 돌리는 대신, 매니페스트의 source of truth를 git에 두고 클러스터 안의 컨트롤러가 git을 watch해 자동으로 동기화하는 운영 모델입니다. ArgoCD와 Flux가 이 모델의 두 표준 구현체이고, 시리즈 마지막 글로 두 도구의 모델,운영 패턴,시리즈 회고,다음 트랙까지 한 사이클로 정리하겠습니다.

이번 시리즈는 K8s 고급 6편입니다.

Push 모델과 Pull 모델 #

먼저 GitOps가 등장하기 전의 표준이었던 모델과 비교해 보겠습니다. CI/CD 파이프라인이 클러스터에 매니페스트를 적용하는 방식은 크게 두 갈래입니다.

모델흐름
PushCI 파이프라인이 클러스터의 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 Immutabledesired 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입니다.

application-my-app.yaml
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에 적용되면 다음 일이 자동으로 일어납니다.

  1. ArgoCD 컨트롤러가 repoURLpath 디렉터리를 git에서 가져옴
  2. Kustomize / Helm / 일반 YAML을 자동 인식해서 매니페스트를 렌더링
  3. 클러스터의 destination에 동기화 (automated.selfHeal: true이므로 drift를 자동 복구)
  4. git의 매니페스트가 변경되면 자동으로 다시 동기화 (automated)
  5. git에서 사라진 객체는 클러스터에서도 삭제 (prune: true)

App of Apps — Application의 묶음 관리 #

여러 Application을 한곳에서 관리하는 패턴이 App of Apps입니다. 한 Application의 source가 다른 Application 매니페스트들이 들어 있는 디렉터리를 가리키는 구조입니다.

App of Apps 디렉터리 구조
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으로 이 순서를 표현합니다.

순서 표현 — argocd.argoproj.io/sync-wave
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-controllergit / Helm 저장소 / OCI 이미지에서 매니페스트 fetching
kustomize-controllerKustomize 매니페스트 적용
helm-controllerHelmRelease 객체로 Helm 차트 적용
notification-controllerSlack / Teams / GitHub 등에 이벤트 통지
image-automation-controller컨테이너 이미지 새 버전을 git에 자동 commit

각 컨트롤러가 자기 CRD를 갖고, 그 CRD의 매니페스트로 모든 동작이 표현됩니다.

GitRepository + Kustomization — Flux의 기본 묶음 #

git 저장소 등록
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-app

GitRepository가 git을 watch하고, Kustomization이 그 git의 한 디렉터리를 클러스터에 적용합니다. 두 객체의 분리 덕에 같은 git을 여러 Kustomization이 다른 path로 가리킬 수 있습니다.

HelmRelease — Helm 차트의 GitOps화 #

HelmRelease — Helm 차트도 매니페스트로 표현
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: 30d

helm install을 손으로 돌리는 대신 HelmRelease 매니페스트로 표현하면 Helm 차트의 설치,업그레이드도 GitOps의 흐름 안으로 들어옵니다.

ArgoCD vs Flux — 선택의 결 #

차원ArgoCDFlux
모델한 큰 컴포넌트 + 풍부한 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.yaml

Kustomize의 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 SecretsBitnami가 만든 도구. 비밀을 SealedSecret으로 암호화해 git에 올림. 클러스터 안의 컨트롤러가 자기 키로만 복호화 가능.
External Secrets Operatorgit에는 외부 비밀 저장소(AWS Secrets Manager, Vault 등)의 참조만 두고, 컨트롤러가 K8s Secret으로 동기화.
SOPS + age/PGPgit에 암호화된 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편을 한번 짚어 두겠습니다.

  • #1CNI 깊이. K8s 네트워크 모델의 4조건, CNI 인터페이스, iptables / IPVS / eBPF의 데이터 플레인, Calico와 Cilium의 비교.
  • #2RBAC / ServiceAccount 깊이. Aggregated ClusterRole, Impersonation, projected token, IRSA / Workload Identity로 K8s ServiceAccount를 클라우드 IAM과 연결.
  • #3Admission Controller. API 서버의 5단계 흐름, mutating / validating webhook, OPA Gatekeeper와 Kyverno의 정책 엔진 비교.
  • #4CRD와 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 파이프라인, 모니터링,알람, 운영 체크리스트까지 — 진짜 서비스를 한 개 올리는 한 사이클을 처음부터 끝까지 따라가겠습니다.

X