Kim Seogyu
Backend

GitOps 심화 시리즈 #6: CI/CD 파이프라인 통합과 Progressive Delivery

GitOps에서 CI와 CD의 분리, Image Updater, Progressive Delivery(Argo Rollouts, Flagger)를 다루는 시리즈 완결편입니다.

Published 2026년 1월 5일10 min read1,933 words

GitOps 심화 시리즈 #6: CI/CD 파이프라인 통합과 Progressive Delivery

시리즈 개요

#주제핵심 내용
1GitOps 개요철학과 원칙, Push vs Pull 배포, Reconciliation
2ArgoCD Deep Dive아키텍처, Application CRD, Sync 전략
3Flux CD & GitOps Toolkit컨트롤러 아키텍처, GitRepository, Kustomization
4환경별 설정 관리Kustomize vs Helm, 전략 선택 기준
5Secrets ManagementSealed Secrets, External Secrets, SOPS
6CI/CD 파이프라인 통합Image Updater, Progressive Delivery

GitOps에서 CI와 CD의 분리

전통적인 CI/CD 파이프라인에서는 CI와 CD가 하나의 파이프라인에서 연속적으로 실행됩니다. GitOps에서는 명확하게 분리됩니다.

flowchart TB
    subgraph CI [CI Pipeline - 소스 레포]
        Code[코드 변경] --> Build[빌드]
        Build --> Test[테스트]
        Test --> Push[이미지 Push]
        Push --> UpdateManifest[매니페스트 업데이트]
    end
    
    subgraph GitOps [GitOps 레포]
        Manifest[K8s Manifests]
    end
    
    subgraph CD [CD - GitOps Agent]
        Agent[ArgoCD / Flux]
        Cluster[Kubernetes Cluster]
    end
    
    UpdateManifest -->|PR 생성 또는 직접 커밋| Manifest
    Manifest -->|Watch| Agent
    Agent -->|Sync| Cluster

왜 분리하는가?

관점통합 CI/CD분리된 CI + GitOps
배포 권한CI가 클러스터 접근클러스터 내부 Agent만 접근
감사 추적CI 로그에만 기록Git 커밋 히스토리
롤백재빌드 필요git revert
환경 일관성CI 파이프라인에 의존Git이 SSOT

[!IMPORTANT] 핵심 원칙: CI는 아티팩트(이미지)를 생성하고, CD는 GitOps Agent가 담당합니다. CI가 클러스터에 직접 접근하지 않습니다.


CI 파이프라인에서 매니페스트 업데이트

이미지 빌드 후 GitOps 레포의 매니페스트를 업데이트하는 여러 방법이 있습니다.

방법 1: CI에서 직접 커밋

# .github/workflows/build.yaml
name: Build and Update Manifest

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source
        uses: actions/checkout@v4
      
      - name: Build and push image
        run: |
          docker build -t ghcr.io/myorg/myapp:${{ github.sha }} .
          docker push ghcr.io/myorg/myapp:${{ github.sha }}
      
      - name: Update GitOps repo
        env:
          GITHUB_TOKEN: ${{ secrets.GITOPS_PAT }}
        run: |
          git clone https://$GITHUB_TOKEN@github.com/myorg/gitops-repo.git
          cd gitops-repo
          
          # Kustomize 사용 시
          cd apps/myapp/overlays/prod
          kustomize edit set image ghcr.io/myorg/myapp:${{ github.sha }}
          
          git config user.name "GitHub Actions"
          git config user.email "actions@github.com"
          git add .
          git commit -m "Update myapp to ${{ github.sha }}"
          git push

방법 2: PR 생성 (권장)

      - name: Create PR to GitOps repo
        uses: peter-evans/create-pull-request@v6
        with:
          token: ${{ secrets.GITOPS_PAT }}
          repository: myorg/gitops-repo
          branch: update-myapp-${{ github.sha }}
          title: "Update myapp to ${{ github.sha }}"
          body: |
            Automated image update
            - Commit: ${{ github.sha }}
            - Build: ${{ github.run_id }}
          commit-message: "chore: update myapp image to ${{ github.sha }}"

[!TIP] PR 방식의 장점: 코드 리뷰 프로세스 활용, 자동화된 테스트 실행, 승인 후 배포

방법 3: 이미지 태그 자동 업데이트 (권장)

CI는 이미지만 푸시하고, GitOps Agent가 자동으로 매니페스트를 업데이트합니다. 다음 섹션에서 자세히 다룹니다.


ArgoCD Image Updater

ArgoCD Image Updater는 컨테이너 레지스트리를 모니터링하고, 새 이미지 태그가 발견되면 자동으로 Application을 업데이트합니다.

동작 원리

sequenceDiagram
    participant Registry as Container Registry
    participant Updater as Image Updater
    participant Git as GitOps Repo
    participant ArgoCD as ArgoCD
    participant K8s as Kubernetes
    
    loop 주기적 스캔
        Updater->>Registry: 새 이미지 태그 확인
        Registry-->>Updater: v1.2.4 발견 (현재: v1.2.3)
    end
    
    Updater->>Git: 매니페스트 업데이트 커밋
    Git-->>ArgoCD: 변경 감지
    ArgoCD->>K8s: Sync

설치

kubectl apply -n argocd \
  -f https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/manifests/install.yaml

Application 설정

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
  annotations:
    # Image Updater 활성화
    argocd-image-updater.argoproj.io/image-list: myapp=ghcr.io/myorg/myapp
    
    # 업데이트 전략: semver
    argocd-image-updater.argoproj.io/myapp.update-strategy: semver
    
    # SemVer 제약
    argocd-image-updater.argoproj.io/myapp.allow-tags: regexp:^v[0-9]+\.[0-9]+\.[0-9]+$
    
    # Git에 커밋 (write-back)
    argocd-image-updater.argoproj.io/write-back-method: git
    argocd-image-updater.argoproj.io/git-branch: main
spec:
  source:
    repoURL: https://github.com/myorg/gitops.git
    path: apps/myapp

Update Strategies

전략설명예시
semverSemVer 최신v1.2.3 < v1.2.4 < v1.3.0
latest최신 푸시된 태그날짜/시간 기준
name알파벳순 최신a < b < c
digest특정 태그의 digest 변경:latest의 실제 이미지 변경

Write-back Methods

# 1. Git 직접 커밋 (권장)
argocd-image-updater.argoproj.io/write-back-method: git

# 2. ArgoCD에 오버라이드 (Git에 기록 안 됨)
argocd-image-updater.argoproj.io/write-back-method: argocd

[!WARNING] argocd 방식은 Git에 기록되지 않아 GitOps 원칙에 위배됩니다. 프로덕션에서는 git 방식을 권장합니다.


Flux Image Automation

Flux는 Image Automation을 핵심 기능으로 내장하고 있습니다 (3편에서 간략히 소개했습니다).

전체 구성

# 1. ImageRepository: 레지스트리 스캔
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
  name: myapp
  namespace: flux-system
spec:
  image: ghcr.io/myorg/myapp
  interval: 5m
  secretRef:
    name: ghcr-auth

---
# 2. ImagePolicy: 태그 선택 정책
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
  name: myapp
  namespace: flux-system
spec:
  imageRepositoryRef:
    name: myapp
  policy:
    semver:
      range: ">=1.0.0"

---
# 3. ImageUpdateAutomation: Git 업데이트
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageUpdateAutomation
metadata:
  name: myapp
  namespace: flux-system
spec:
  interval: 5m
  sourceRef:
    kind: GitRepository
    name: myapp
  git:
    checkout:
      ref:
        branch: main
    commit:
      author:
        name: Flux
        email: flux@myorg.com
      messageTemplate: |
        Auto-update images
        
        {{range .Changed.Changes}}
        - {{.OldValue}} -> {{.NewValue}}
        {{end}}
    push:
      branch: main
  update:
    path: ./deploy
    strategy: Setters

마커 기반 업데이트

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    spec:
      containers:
      - name: myapp
        image: ghcr.io/myorg/myapp:v1.2.3  # {"$imagepolicy": "flux-system:myapp"}

Flux가 새 버전을 발견하면:

        image: ghcr.io/myorg/myapp:v1.2.4  # {"$imagepolicy": "flux-system:myapp"}

Progressive Delivery

새 버전을 점진적으로 배포하여 위험을 최소화하는 전략입니다.

배포 전략 비교

flowchart TB
    subgraph Recreate [Recreate]
        R1[v1 삭제] --> R2[v2 생성]
        R_Note[다운타임 발생]
    end
    
    subgraph Rolling [Rolling Update]
        RU1[v1: 3개] --> RU2[v1: 2개, v2: 1개]
        RU2 --> RU3[v1: 1개, v2: 2개]
        RU3 --> RU4[v2: 3개]
    end
    
    subgraph BlueGreen [Blue-Green]
        BG1[Blue: v1 운영] --> BG2[Green: v2 대기]
        BG2 --> BG3[트래픽 전환]
        BG3 --> BG4[Green: v2 운영]
    end
    
    subgraph Canary [Canary]
        C1[v1: 100%] --> C2[v1: 90%, v2: 10%]
        C2 --> C3[v1: 50%, v2: 50%]
        C3 --> C4[v2: 100%]
    end
전략다운타임리소스롤백 속도위험 노출
Recreate✅ 있음낮음느림높음
Rolling❌ 없음일시 증가중간중간
Blue-Green❌ 없음2배빠름낮음
Canary❌ 없음약간 증가빠름낮음

Argo Rollouts

Argo Rollouts는 Kubernetes Deployment를 대체하여 Blue-Green, Canary 배포를 제공합니다.

설치

kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts \
  -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

Canary Rollout

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp
spec:
  replicas: 5
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: ghcr.io/myorg/myapp:v1.2.3
        ports:
        - containerPort: 8080
  strategy:
    canary:
      # 단계별 배포
      steps:
      - setWeight: 10      # 10% 트래픽
      - pause: {duration: 5m}  # 5분 대기
      - setWeight: 30
      - pause: {duration: 5m}
      - setWeight: 50
      - pause: {}          # 수동 승인 대기
      - setWeight: 100
      
      # 분석 실행 (자동 롤백)
      analysis:
        templates:
        - templateName: success-rate
        startingStep: 1
        args:
        - name: service-name
          value: myapp

AnalysisTemplate

메트릭 기반 자동 롤백:

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  args:
  - name: service-name
  metrics:
  - name: success-rate
    interval: 1m
    failureLimit: 3
    successCondition: result[0] >= 0.95
    provider:
      prometheus:
        address: http://prometheus.monitoring:9090
        query: |
          sum(rate(http_requests_total{
            service="{{args.service-name}}",
            status=~"2.."
          }[5m])) /
          sum(rate(http_requests_total{
            service="{{args.service-name}}"
          }[5m]))

Blue-Green Rollout

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp
spec:
  replicas: 5
  selector:
    matchLabels:
      app: myapp
  template:
    # ...
  strategy:
    blueGreen:
      activeService: myapp-active
      previewService: myapp-preview
      autoPromotionEnabled: false  # 수동 승인
      prePromotionAnalysis:
        templates:
        - templateName: smoke-test

ArgoCD와 통합

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
spec:
  source:
    repoURL: https://github.com/myorg/gitops.git
    path: apps/myapp
  # Rollout 리소스를 인식하도록 설정
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Flagger

Flagger는 Flux와 함께 사용되는 Progressive Delivery 도구입니다. Istio, Linkerd, NGINX Ingress 등과 통합됩니다.

설치 (with Flux)

apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
  name: flagger
  namespace: flux-system
spec:
  interval: 1h
  url: https://flagger.app

---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: flagger
  namespace: flagger-system
spec:
  interval: 1h
  chart:
    spec:
      chart: flagger
      sourceRef:
        kind: HelmRepository
        name: flagger
        namespace: flux-system
  values:
    meshProvider: istio
    metricsServer: http://prometheus.monitoring:9090

Canary CRD

apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: myapp
  namespace: production
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  
  # Istio VirtualService 자동 생성
  service:
    port: 8080
    targetPort: 8080
  
  # 분석 설정
  analysis:
    interval: 1m           # 분석 주기
    threshold: 5           # 최대 실패 횟수
    maxWeight: 50          # 최대 Canary 트래픽
    stepWeight: 10         # 단계별 증가량
    
    metrics:
    - name: request-success-rate
      thresholdRange:
        min: 99
      interval: 1m
    
    - name: request-duration
      thresholdRange:
        max: 500
      interval: 1m
    
    # Webhook 테스트
    webhooks:
    - name: smoke-test
      type: pre-rollout
      url: http://flagger-loadtester/
      timeout: 30s
      metadata:
        type: bash
        cmd: "curl -s http://myapp-canary:8080/health | grep ok"

동작 흐름

sequenceDiagram
    participant Git as GitOps Repo
    participant Flux as Flux
    participant Flagger as Flagger
    participant Istio as Istio
    participant Prom as Prometheus
    
    Git->>Flux: 새 이미지 감지
    Flux->>Flux: Deployment 업데이트
    
    Flagger->>Flagger: Canary Deployment 생성
    Flagger->>Istio: VirtualService 업데이트 (10% canary)
    
    loop 분석
        Flagger->>Prom: 성공률 확인
        alt 성공률 >= 99%
            Flagger->>Istio: 트래픽 증가 (20%, 30%, ...)
        else 실패
            Flagger->>Istio: 롤백 (100% primary)
            Flagger->>Flagger: Canary 삭제
        end
    end
    
    Flagger->>Flux: Primary 업데이트
    Flagger->>Istio: 100% primary

프로덕션 GitOps 워크플로우

모든 것을 종합한 프로덕션 워크플로우입니다.

flowchart TB
    subgraph Dev [개발]
        Code[코드 작성]
        PR[Pull Request]
        Review[코드 리뷰]
    end
    
    subgraph CI [CI Pipeline]
        Build[빌드]
        Test[테스트]
        Push[이미지 Push]
    end
    
    subgraph GitOps [GitOps Repo]
        ImageUpdate[Image Updater\n또는 Flux Image Automation]
        Manifest[K8s Manifests]
    end
    
    subgraph CD [CD - Progressive Delivery]
        Agent[ArgoCD / Flux]
        Rollout[Argo Rollouts / Flagger]
        Canary[Canary 분석]
    end
    
    subgraph Prod [Production]
        Cluster[Kubernetes Cluster]
        Monitor[Prometheus/Grafana]
    end
    
    Code --> PR --> Review
    Review -->|Merge| Build --> Test --> Push
    Push -->|새 이미지 감지| ImageUpdate
    ImageUpdate --> Manifest
    Manifest --> Agent
    Agent --> Rollout --> Canary
    Canary -->|성공| Cluster
    Canary <-->|메트릭 수집| Monitor
    
    Canary -->|실패| Rollback[자동 롤백]

레포지토리 구조 (최종)

gitops-repo/
├── clusters/
│   ├── dev/
│   │   └── kustomization.yaml
│   ├── staging/
│   │   └── kustomization.yaml
│   └── prod/
│       └── kustomization.yaml
│
├── infrastructure/
│   ├── base/
│   │   ├── ingress-nginx/
│   │   ├── cert-manager/
│   │   ├── monitoring/
│   │   └── flagger/
│   └── overlays/
│       ├── dev/
│       └── prod/
│
├── apps/
│   ├── frontend/
│   │   ├── base/
│   │   │   ├── deployment.yaml
│   │   │   ├── service.yaml
│   │   │   ├── canary.yaml        # Flagger Canary
│   │   │   └── kustomization.yaml
│   │   └── overlays/
│   │       ├── dev/
│   │       ├── staging/
│   │       └── prod/
│   └── backend/
│       └── ...
│
└── image-automation/              # Flux Image Automation
    ├── image-repositories.yaml
    ├── image-policies.yaml
    └── image-update-automation.yaml

정리: GitOps 시리즈 총정리

6편의 시리즈를 통해 다룬 내용을 정리합니다.

주제핵심 메시지
1GitOps 개요Git = SSOT, Pull 모델, Reconciliation
2ArgoCD모놀리식 아키텍처, 강력한 UI, ApplicationSet
3Flux CD마이크로서비스 컨트롤러, Image Automation 내장
4설정 관리Kustomize(순수 YAML) vs Helm(템플릿)
5SecretsSealed Secrets, ESO, SOPS
6CI/CD 통합CI와 CD 분리, Progressive Delivery

GitOps 성숙도 모델

flowchart LR
    L1[Level 1\nGit에 매니페스트 저장]
    L2[Level 2\nGitOps Agent 도입]
    L3[Level 3\n환경별 설정 분리]
    L4[Level 4\nSecrets 자동화]
    L5[Level 5\nProgressive Delivery]
    
    L1 --> L2 --> L3 --> L4 --> L5

시작하기 권장 순서

  1. ArgoCD 또는 Flux 설치
  2. 간단한 앱으로 GitOps 워크플로우 경험
  3. Kustomize로 환경별 설정 분리
  4. Sealed Secrets 또는 ESO 도입
  5. Image Updater로 자동화
  6. Argo Rollouts 또는 Flagger로 Progressive Delivery

마치며

GitOps는 단순한 도구가 아닌 운영 철학입니다. Git을 중심으로 선언적 인프라를 관리하고, 자동화된 Reconciliation으로 일관성을 유지합니다.

이 시리즈가 여러분의 GitOps 여정에 도움이 되길 바랍니다. 🚀


참고 자료

Share

Related Articles

Comments

이 블로그는 제가 알고 있는 것들을 잊지 않기 위해 기록하는 공간입니다.
직접 작성한 글도 있고, AI의 도움을 받아 정리한 글도 있습니다.
정확하지 않은 내용이 있을 수 있으니 참고용으로 봐주세요.

© 2026 Seogyu Kim