본문으로 건너뛰기
김신건의 로그

[GitOps] 패턴: 단일 vs 다중 repo, environment promotion

· 수정 · 📖 약 2분 · 565자/단어 #gitops #argocd #flux #ci-cd #devops #kustomize
GitOps patterns, monorepo manifests, environment promotion, image updater, Renovate, Kustomize, Flux, GitOps repo structure

정의

GitOps = Git 을 single source of truth 로 삼아 자동으로 인프라 + 앱 상태를 reconcile 하는 운영 방식.

GitOps 4가지 원칙 (OpenGitOps)

  1. Declarative: 시스템 원하는 상태선언적 (YAML).
  2. Versioned and Immutable: Git 으로 버전 + 불변.
  3. Pulled Automatically: 시스템이 자동 fetch + apply.
  4. Continuously Reconciled: 지속적 desired vs actual 비교 + 수정.

Push vs Pull 모델

flowchart LR
    subgraph "Push-based (전통 CI/CD)"
        CI1[CI] -->|"kubectl apply (cluster 접근 필요)"| Cluster1[K8s Cluster]
    end
    subgraph "Pull-based (GitOps)"
        CI2[CI] -->|"이미지 빌드 + manifest 업데이트"| Git[Git Manifest Repo]
        Agent["ArgoCD / Flux (cluster 내부)"] -->|"pull + apply"| Cluster2[K8s Cluster]
        Git -->|"감시"| Agent
    end
항목Push-basedPull-based (GitOps)
Cluster 접근CI 서버가 직접 접근에이전트가 내부에서 pull
보안CI 에 cluster 권한 노출cluster 권한 외부 노출 없음
자동 복구없음drift 감지 + 자동 복구
감사 (audit)CI historyGit log (PR + 리뷰)
다중 cluster복잡에이전트 배포만으로 확장

Repo 구조 패턴

flowchart TD
    Q{"Repo 구조 선택"}
    Q --> Mono["Monorepo (app + manifest 함께)"]
    Q --> Two["2-repo (app / manifest 분리, 권장)"]
    Q --> Multi["Multi-repo (앱별 + 인프라별 분리)"]

1. Monorepo (app + manifest)

my-repo/
├── src/          (app code)
├── k8s/          (manifest)
│   ├── base/
│   └── overlays/
│       ├── dev/
│       └── prod/
└── .github/workflows/
    └── build.yml

장점: 단순. 단점: app PR 이 인프라 변경 동시 일으킴 → 위험.

2. 2-repo (권장)

app-repo/
├── src/
└── .github/workflows/
    └── build.yml  # 이미지 빌드 → registry push

manifest-repo/
├── apps/
│   └── web/
│       ├── base/
│       │   ├── deployment.yaml
│       │   └── kustomization.yaml
│       └── overlays/
│           ├── dev/
│           │   └── kustomization.yaml  # image tag dev
│           └── prod/
│               └── kustomization.yaml  # image tag prod
└── argocd/
    └── applications.yaml

CI 가 새 imagemanifest repo 의 image tag 자동 update → ArgoCD 가 cluster 동기화.

CI/CD 통합 흐름

sequenceDiagram
    participant Dev
    participant AppRepo as App Repo
    participant CI as GitHub Actions
    participant Reg as Container Registry
    participant ManRepo as Manifest Repo
    participant Argo as ArgoCD

    Dev->>AppRepo: feature PR merge
    AppRepo->>CI: push trigger
    CI->>CI: test + build
    CI->>Reg: docker push myapp:abc123
    CI->>ManRepo: PR (image tag abc123)
    Dev->>ManRepo: PR review + merge
    ManRepo->>Argo: webhook (또는 polling)
    Argo->>Argo: manifest diff
    Argo->>Argo: kubectl apply
# GitHub Actions: build.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build and push
        run: |
          docker buildx build \
            --platform linux/amd64,linux/arm64 \
            -t ghcr.io/myorg/myapp:${{ github.sha }} \
            --push .
      - name: Update manifest
        run: |
          git clone https://x-token:${{ secrets.MANIFEST_TOKEN }}@github.com/myorg/manifests
          cd manifests
          # yq 로 image tag 업데이트
          yq e '.images[0].newTag = "${{ github.sha }}"' \
            -i apps/web/overlays/prod/kustomization.yaml
          git commit -am "chore: update web image to ${{ github.sha }}"
          git push

Environment Promotion

flowchart LR
    Dev["dev branch"] -->|"PR merge"| Stage["staging branch"]
    Stage -->|"PR merge (manual approval)"| Prod["prod branch"]
    ArgoDev["ArgoCD: dev cluster"] -.->|"watch"| Dev
    ArgoStage["ArgoCD: stage cluster"] -.->|"watch"| Stage
    ArgoProd["ArgoCD: prod cluster"] -.->|"watch"| Prod

또는 디렉토리 분리 (Kustomize overlay):

manifests/
├── base/
│   ├── deployment.yaml    # 공통 설정
│   └── kustomization.yaml
└── overlays/
    ├── dev/
    │   ├── kustomization.yaml  # replica: 1, image: dev-tag
    │   └── patch-resources.yaml
    ├── staging/
    │   └── kustomization.yaml  # replica: 2
    └── prod/
        ├── kustomization.yaml  # replica: 5, HPA
        └── patch-hpa.yaml
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
images:
  - name: myapp
    newName: ghcr.io/myorg/myapp
    newTag: abc123sha
patches:
  - path: patch-hpa.yaml

Image Updater

flowchart LR
    CI -->|push| Registry[("Container Registry")]
    Updater["ArgoCD Image Updater\n(또는 Renovate)"] -.->|poll| Registry
    Updater -->|"PR / direct commit"| Manifest["manifest repo"]
    Argo -->|sync| Cluster
도구방식특징
ArgoCD Image Updatertag 정책 기반 자동 commitArgoCD 통합
Renovatedependency PR광범위한 ecosystem
Flux Image AutomationFlux 내장Flux 전용
Keel자동 deploywebhook 기반
# ArgoCD Image Updater annotation
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  annotations:
    argocd-image-updater.argoproj.io/image-list: myapp=ghcr.io/myorg/myapp
    argocd-image-updater.argoproj.io/myapp.update-strategy: semver
    argocd-image-updater.argoproj.io/myapp.allow-tags: regexp:^v[0-9]+\.[0-9]+\.[0-9]+$

Secret 처리 (git 안전)

flowchart TD
    Q["Secret 처리 방법"]
    Q --> Sealed["Sealed Secrets\n(kubeseal, cluster 내 복호화)"]
    Q --> ESO["External Secrets Operator\n(AWS SM, GCP SM, Vault)"]
    Q --> SOPS["SOPS + KMS\n(AGE / AWS KMS 암호화)"]
    Q --> Vault["Vault Agent Injector / CSI"]
# Sealed Secrets 예시
kubeseal --fetch-cert > pub-cert.pem
kubectl create secret generic db-pass \
  --from-literal=password=mysecret --dry-run=client -o yaml \
  | kubeseal --cert pub-cert.pem > sealed-secret.yaml
# sealed-secret.yaml 을 git 에 commit 가능

# External Secrets Operator
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-password
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-ssm
    kind: ClusterSecretStore
  target:
    name: db-password
  data:
    - secretKey: password
      remoteRef:
        key: /prod/db/password

ArgoCD vs Flux

항목ArgoCDFlux
UI강력Weave GitOps UI 별도
학습 곡선중간낮음
설치 방식kubectl applyFlux CLI (flux bootstrap)
Multi-tenancy강 (Project 개념)보통
Image automation별도 Image Updater내장
Notificationbuilt-inAlertmanager 통합
OCI artifact지원지원
인기2026 기준 더 인기단순함 선호

Multi-cluster GitOps

flowchart LR
    Git["Manifest repo"]
    Git -->|watch| Argo1["ArgoCD (cluster-dev)"]
    Git -->|watch| Argo2["ArgoCD (cluster-prod-us)"]
    Git -->|watch| Argo3["ArgoCD (cluster-prod-eu)"]
    Argo1 --> ClusterDev["Dev Cluster"]
    Argo2 --> ProdUS["Prod US"]
    Argo3 --> ProdEU["Prod EU"]

또는 중앙 ArgoCD + 다중 cluster credential:

# ArgoCD Application: 다른 cluster 배포
spec:
  destination:
    server: https://k8s.prod-us.example.com   # 외부 cluster
    namespace: web

Drift Detection

상황ArgoCD 동작
수동 kubectl editOut of Sync 표시 + selfHeal 시 자동 복구
Git updateSync 트리거 (webhook 또는 3분 polling)
Image tag 변경Image Updater 가 manifest commit
CRD 삭제Sync 오류 (finalizer 남음)

흔한 함정

WARNING

  1. 수동 hotfix = git 우회. cluster 상태 ArgoCD 가 revert. 항상 git PR.
  2. Branch 정책 부재 = main 직접 push. branch protection + PR 리뷰 필수.
  3. 모든 cluster 같은 manifest = 환경별 차이 못 표현. Kustomize overlay / Helm values.
  4. Secret 평문 git commit = 히스토리 영구 노출. SOPS / Sealed Secrets / ESO.
  5. 거대한 단일 PR = ArgoCD 가 한 번에 대량 변경 적용. 작은 PR + canary 배포.
  6. Webhook 미설정 = 3분 polling 지연. GitHub webhook 설정 권장.

관련 위키

이 글의 용어 (4개)
[CI/CD] GitHub Actions: workflow, action, runnerdevops
정의 GitHub Actions = GitHub 내장 CI/CD. YAML workflow + marketplace action. 2018 출시 → Travis CI 대체. 구조…
[GitOps] ArgoCD: Kubernetes GitOpsdevops
정의 ArgoCD = Kubernetes 의 GitOps 컨트롤러. Git 리포지토리의 manifest 가 source of truth → cluster 가 자동 동기화. Git…
[K8s] ConfigMap & Secret: 설정과 비밀의 분리kubernetes
정의 ConfigMap 과 Secret 은 컨테이너 이미지에서 설정/비밀 값을 분리하기 위한 Kubernetes 리소스. | 항목 | ConfigMap | Secret | |:-…
[LLM Eval] HELM: Holistic Evaluation of Language Modelsai
정의 HELM (Holistic Evaluation of Language Models) 는 Stanford CRFM (Center for Research on Foundation…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기