Skip to content
Published on

ArgoCD ApplicationSet과 Progressive Rollout: GitOps 멀티클러스터 배포 실전 가이드

Authors
  • Name
    Twitter

1. ArgoCD ApplicationSet이란

ApplicationSet은 ArgoCD의 확장 리소스로, 하나의 템플릿에서 여러 Application을 자동 생성한다. 멀티클러스터, 멀티테넌트, 모노레포 환경에서 수십~수백 개의 Application을 수동으로 관리하는 대신 선언적으로 자동화할 수 있다.

1.1 왜 ApplicationSet인가?

기존 방식:
  app-dev.yamlApplication (dev)
  app-staging.yamlApplication (staging)
  app-prod.yamlApplication (prod)
  ... × 50개 서비스 = 150YAML 수동 관리 😱

ApplicationSet:
  appset.yaml → 하나의 템플릿으로 150개 자동 생성 ✅

2. ApplicationSet Generator 유형

2.1 List Generator

가장 단순한 형태. 명시적 파라미터 목록에서 Application 생성:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-envs
  namespace: argocd
spec:
  generators:
  - list:
      elements:
      - cluster: dev
        url: https://dev-k8s.example.com
        namespace: myapp-dev
      - cluster: staging
        url: https://staging-k8s.example.com
        namespace: myapp-staging
      - cluster: prod
        url: https://prod-k8s.example.com
        namespace: myapp-prod
  template:
    metadata:
      name: 'myapp-{{cluster}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/myapp.git
        targetRevision: HEAD
        path: 'k8s/overlays/{{cluster}}'
      destination:
        server: '{{url}}'
        namespace: '{{namespace}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

2.2 Git Generator (Directory)

모노레포 구조에서 디렉토리 기반 자동 검색:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: cluster-addons
  namespace: argocd
spec:
  generators:
  - git:
      repoURL: https://github.com/org/cluster-addons.git
      revision: HEAD
      directories:
      - path: addons/*
      - path: addons/exclude-this
        exclude: true
  template:
    metadata:
      name: '{{path.basename}}'
    spec:
      project: addons
      source:
        repoURL: https://github.com/org/cluster-addons.git
        targetRevision: HEAD
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{path.basename}}'

2.3 Cluster Generator

ArgoCD에 등록된 모든 클러스터에 자동 배포:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: monitoring-stack
  namespace: argocd
spec:
  generators:
  - clusters:
      selector:
        matchLabels:
          env: production
  template:
    metadata:
      name: 'monitoring-{{name}}'
    spec:
      project: infrastructure
      source:
        repoURL: https://github.com/org/monitoring.git
        path: helm-chart
        helm:
          valueFiles:
          - 'values-{{metadata.labels.region}}.yaml'
      destination:
        server: '{{server}}'
        namespace: monitoring

2.4 Matrix Generator (조합)

두 Generator를 교차 결합:

spec:
  generators:
  - matrix:
      generators:
      - git:
          repoURL: https://github.com/org/apps.git
          revision: HEAD
          directories:
          - path: apps/*
      - clusters:
          selector:
            matchLabels:
              env: production

3. Progressive Sync: 단계적 롤아웃

3.1 개념

Progressive Sync는 ApplicationSet이 생성한 Application들을 한꺼번에 배포하지 않고 단계적으로 배포하는 전략이다. QA → Staging → Production 순서로 검증하면서 진행할 수 있다.

3.2 설정

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapp-progressive
  namespace: argocd
spec:
  generators:
  - list:
      elements:
      - cluster: qa
        url: https://qa-k8s.example.com
      - cluster: staging
        url: https://staging-k8s.example.com
      - cluster: prod-east
        url: https://prod-east-k8s.example.com
      - cluster: prod-west
        url: https://prod-west-k8s.example.com
  strategy:
    type: RollingSync
    rollingSync:
      steps:
      - matchExpressions:
        - key: cluster
          operator: In
          values:
          - qa
        # QA 먼저 배포 → 자동 Sync
      - matchExpressions:
        - key: cluster
          operator: In
          values:
          - staging
        maxUpdate: 100%
        # QA 성공 후 staging 자동
      - matchExpressions:
        - key: cluster
          operator: In
          values:
          - prod-east
          - prod-west
        maxUpdate: 50%
        # prod는 50%씩 단계적으로
  template:
    metadata:
      name: 'myapp-{{cluster}}'
      labels:
        cluster: '{{cluster}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/myapp.git
        targetRevision: HEAD
        path: 'k8s/overlays/{{cluster}}'
      destination:
        server: '{{url}}'
        namespace: myapp
      syncPolicy:
        automated:
          prune: true

4. Argo Rollouts: Canary와 Blue-Green

4.1 Canary 배포

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp
spec:
  replicas: 10
  strategy:
    canary:
      steps:
      - setWeight: 10
      - pause: {duration: 5m}
      - setWeight: 30
      - pause: {duration: 5m}
      - analysis:
          templates:
          - templateName: success-rate
          args:
          - name: service-name
            value: myapp
      - setWeight: 60
      - pause: {duration: 10m}
      - setWeight: 100
      canaryService: myapp-canary
      stableService: myapp-stable
      trafficRouting:
        istio:
          virtualServices:
          - name: myapp-vsvc
            routes:
            - primary
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:v2
        ports:
        - containerPort: 8080

4.2 AnalysisTemplate (자동 롤백)

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  args:
  - name: service-name
  metrics:
  - name: success-rate
    interval: 30s
    count: 10
    successCondition: result[0] >= 0.95
    failureLimit: 3
    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]))

4.3 Blue-Green 배포

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: myapp-bluegreen
spec:
  replicas: 5
  strategy:
    blueGreen:
      activeService: myapp-active
      previewService: myapp-preview
      autoPromotionEnabled: false
      prePromotionAnalysis:
        templates:
        - templateName: success-rate
      scaleDownDelaySeconds: 300
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:v2

5. 실전: ApplicationSet + Rollouts 통합

5.1 디렉토리 구조

repo/
├── apps/
│   ├── frontend/
│   │   ├── base/
│   │   │   ├── rollout.yaml
│   │   │   ├── service.yaml
│   │   │   └── kustomization.yaml
│   │   └── overlays/
│   │       ├── dev/
│   │       ├── staging/
│   │       └── prod/
│   └── backend/
│       ├── base/
│       └── overlays/
└── appsets/
    └── all-apps.yaml

5.2 통합 ApplicationSet

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: all-apps
  namespace: argocd
spec:
  generators:
  - matrix:
      generators:
      - git:
          repoURL: https://github.com/org/gitops.git
          revision: HEAD
          directories:
          - path: apps/*
      - list:
          elements:
          - env: dev
            cluster: https://dev-k8s.example.com
          - env: staging
            cluster: https://staging-k8s.example.com
          - env: prod
            cluster: https://prod-k8s.example.com
  strategy:
    type: RollingSync
    rollingSync:
      steps:
      - matchExpressions:
        - key: env
          operator: In
          values: [dev]
      - matchExpressions:
        - key: env
          operator: In
          values: [staging]
      - matchExpressions:
        - key: env
          operator: In
          values: [prod]
  template:
    metadata:
      name: '{{path.basename}}-{{env}}'
      labels:
        app: '{{path.basename}}'
        env: '{{env}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/gitops.git
        targetRevision: HEAD
        path: '{{path}}/overlays/{{env}}'
      destination:
        server: '{{cluster}}'
        namespace: '{{path.basename}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
        - CreateNamespace=true

6. ArgoCD CLI 운영 명령어

# ApplicationSet 상태 확인
argocd appset list
argocd appset get myapp-progressive

# 특정 Application Sync 상태
argocd app get myapp-prod --refresh

# 수동 Sync (Progressive에서 수동 승인 필요 시)
argocd app sync myapp-staging

# Rollout 상태 확인 (Argo Rollouts kubectl plugin)
kubectl argo rollouts status myapp -n myapp
kubectl argo rollouts get rollout myapp -n myapp -w

# Canary 수동 승인
kubectl argo rollouts promote myapp -n myapp

# 롤백
kubectl argo rollouts undo myapp -n myapp

7. 모범 사례

  1. Progressive Sync 필수: prod 배포 시 반드시 단계적 롤아웃
  2. AnalysisTemplate 연동: 메트릭 기반 자동 롤백 설정
  3. App of Apps 대신 ApplicationSet: 관리 복잡도 대폭 감소
  4. Matrix Generator 활용: 앱 × 환경 조합 자동화
  5. Sync Wave 활용: 의존성 있는 리소스 순서 제어
  6. Notification 연동: Slack/Telegram으로 배포 상태 알림

8. 퀴즈

Q1. ApplicationSet의 핵심 역할은?

하나의 템플릿에서 여러 ArgoCD Application을 자동 생성. 멀티클러스터/멀티환경 배포 자동화.

Q2. Matrix Generator는 어떻게 동작하는가?

두 Generator의 결과를 **교차 결합(Cartesian product)**하여 모든 조합에 대해 Application을 생성한다.

Q3. Progressive Sync의 maxUpdate: 50%는 무엇을 의미하는가?

해당 단계의 Application 중 50%만 동시에 Sync하고, 성공 후 나머지 50%를 진행한다.

Q4. Argo Rollouts의 AnalysisTemplate 실패 시 어떻게 되는가?

자동으로 롤백된다. failureLimit을 초과하면 Rollout이 Degraded 상태가 되고 이전 버전으로 복원.

Q5. Blue-Green에서 autoPromotionEnabled: false의 의미는?

Preview 환경 검증 후 수동으로 promote해야 Active로 전환된다. kubectl argo rollouts promote 명령 필요.

Q6. Canary 배포에서 trafficRouting을 설정하는 이유는?

실제 트래픽의 비율을 정밀하게 제어하기 위함. Istio/Nginx/ALB 등과 연동하여 Pod 수가 아닌 트래픽 비율 기반 Canary를 구현.

Q7. App of Apps 패턴 대비 ApplicationSet의 장점은?

(1) 단일 리소스로 관리 (2) Generator를 통한 자동 검색 (3) Progressive Sync 지원 (4) 코드 중복 제거