Skip to content
Published on

ArgoCD ApplicationSet 컨트롤러 심층 분석

Authors

1. ApplicationSet 개요

ApplicationSet은 ArgoCD의 확장 컨트롤러로, 하나의 ApplicationSet 리소스에서 여러 ArgoCD Application을 자동으로 생성하고 관리합니다. 대규모 멀티클러스터, 멀티테넌트 환경에서 수백 개의 Application을 효율적으로 관리할 수 있습니다.

핵심 구성 요소

ApplicationSet Controller
    |
    +-- Generator: Application 목록 생성을 위한 데이터 소스
    |
    +-- Template: Application 리소스의 템플릿
    |
    +-- Sync Policy: Application 생성/삭제/업데이트 정책

ApplicationSet 기본 구조

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: my-appset
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - cluster: staging
            url: https://staging.example.com
          - cluster: production
            url: https://production.example.com
  template:
    metadata:
      name: 'myapp-{{cluster}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/myapp.git
        targetRevision: HEAD
        path: 'overlays/{{cluster}}'
      destination:
        server: '{{url}}'
        namespace: myapp

2. Generator 유형 상세

List Generator

가장 단순한 Generator로, 정적 목록에서 파라미터를 생성합니다:

spec:
  generators:
    - list:
        elements:
          - cluster: staging
            url: https://staging.example.com
            revision: develop
          - cluster: production
            url: https://production.example.com
            revision: main

사용 사례: 소수의 명시적 환경을 관리할 때

Cluster Generator

ArgoCD에 등록된 클러스터를 기반으로 Application을 생성합니다:

spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            env: production
            region: ap-northeast-2

Cluster Generator의 파라미터:

파라미터설명
name클러스터 이름
server클러스터 API 서버 URL
metadata.labels클러스터에 설정된 레이블
metadata.annotations클러스터에 설정된 어노테이션

로컬 클러스터(in-cluster) 처리:

# 로컬 클러스터를 포함하려면 명시적으로 설정
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            env: production
        # 값이 비어있는 셀렉터는 모든 클러스터 매칭 (로컬 포함)

Git Generator - Directory

Git 저장소의 디렉토리 구조를 스캔하여 Application을 생성합니다:

spec:
  generators:
    - git:
        repoURL: https://github.com/org/gitops-config.git
        revision: HEAD
        directories:
          - path: apps/*
          - path: apps/excluded-app
            exclude: true

생성되는 파라미터:

파라미터예시
pathapps/frontend
path.basenamefrontend
path[0]apps
path[1]frontend
path.basenameNormalizedfrontend (RFC 1123 호환)

디렉토리 구조 예시:

gitops-config/
  apps/
    frontend/
      kustomization.yaml
    backend/
      kustomization.yaml
    database/
      kustomization.yaml

각 디렉토리에 대해 별도의 Application이 자동 생성됩니다.

Git Generator - File

Git 저장소의 JSON/YAML 파일에서 파라미터를 읽어 Application을 생성합니다:

spec:
  generators:
    - git:
        repoURL: https://github.com/org/gitops-config.git
        revision: HEAD
        files:
          - path: 'clusters/*/config.json'

config.json 파일 예시:

{
  "cluster": {
    "name": "production-east",
    "server": "https://prod-east.example.com",
    "environment": "production",
    "region": "us-east-1"
  }
}

템플릿에서 중첩 필드 참조:

template:
  metadata:
    name: 'app-{{cluster.name}}'
  spec:
    destination:
      server: '{{cluster.server}}'

Matrix Generator

두 Generator를 결합하여 카테시안 곱(Cartesian Product)을 생성합니다:

spec:
  generators:
    - matrix:
        generators:
          # Generator 1: 클러스터 목록
          - clusters:
              selector:
                matchLabels:
                  env: production
          # Generator 2: 앱 디렉토리 목록
          - git:
              repoURL: https://github.com/org/apps.git
              revision: HEAD
              directories:
                - path: apps/*

결과 예시:

(cluster=prod-east, path=apps/frontend)
(cluster=prod-east, path=apps/backend)
(cluster=prod-west, path=apps/frontend)
(cluster=prod-west, path=apps/backend)

각 조합에 대해 Application이 생성됩니다.

Merge Generator

여러 Generator의 결과를 공통 키로 병합합니다:

spec:
  generators:
    - merge:
        mergeKeys:
          - server
        generators:
          # 기본 Generator: 모든 클러스터
          - clusters:
              values:
                replicas: '3'
                env: default
          # 오버라이드 Generator: 특정 클러스터 설정
          - list:
              elements:
                - server: https://production.example.com
                  values.replicas: '5'
                  values.env: production

server 키가 일치하는 항목에 대해 오버라이드 값이 적용됩니다.

PullRequest Generator

Git 호스팅 서비스의 PR을 감지하여 프리뷰 환경을 자동 생성합니다:

spec:
  generators:
    - pullRequest:
        github:
          owner: myorg
          repo: myapp
          labels:
            - preview
        requeueAfterSeconds: 60
  template:
    metadata:
      name: 'preview-{{branch}}-{{number}}'
    spec:
      source:
        repoURL: https://github.com/myorg/myapp.git
        targetRevision: '{{head_sha}}'
        path: deploy/preview
      destination:
        server: https://kubernetes.default.svc
        namespace: 'preview-{{number}}'

PullRequest Generator 파라미터:

파라미터설명
numberPR 번호
branch소스 브랜치 이름
branch_slugURL 안전한 브랜치 이름
head_shaHEAD 커밋 SHA
head_short_sha짧은 HEAD SHA
labelsPR에 부여된 레이블

SCMProvider Generator

SCM(Source Code Management) 프로바이더에서 저장소를 자동 발견합니다:

spec:
  generators:
    - scmProvider:
        github:
          organization: myorg
          allBranches: false
        filters:
          - repositoryMatch: '^gitops-.*'
            pathsExist:
              - deploy/kustomization.yaml

지원 SCM 프로바이더:

  • GitHub
  • GitLab
  • Bitbucket Server
  • Azure DevOps
  • Gitea

3. 템플릿 렌더링 엔진

기본 템플릿 문법

ApplicationSet은 이중 중괄호 문법으로 파라미터를 참조합니다. 이 문법은 Generator가 제공하는 값으로 치환됩니다.

template:
  metadata:
    name: 'app-{{cluster}}-{{path.basename}}'
    labels:
      env: '{{values.env}}'
  spec:
    source:
      repoURL: '{{url}}'
      path: '{{path}}'
    destination:
      server: '{{server}}'
      namespace: '{{namespace}}'

Go 템플릿 모드

더 복잡한 로직이 필요한 경우 Go 템플릿을 사용할 수 있습니다:

spec:
  goTemplate: true
  goTemplateOptions: ['missingkey=error']
  template:
    metadata:
      name: 'app-{{ .cluster | lower }}'
    spec:
      source:
        path: '{{ if eq .env "production" }}overlays/prod{{ else }}overlays/dev{{ end }}'

Go 템플릿에서 사용 가능한 함수:

함수설명예시
lower소문자 변환{{ .name | lower }}
upper대문자 변환{{ .name | upper }}
normalizeRFC 1123 정규화{{ .name | normalize }}
toJsonJSON으로 변환{{ .data | toJson }}
default기본값 설정{{ .value | default "3" }}

fasttemplate vs Go 템플릿 비교

특성fasttemplate (기본)Go 템플릿
문법이중 중괄호 (변수 치환만)Go template 전체 문법
조건문불가가능 (if/else)
반복문불가가능 (range)
함수불가Sprig 함수 사용 가능
성능빠름상대적으로 느림
설정기본값goTemplate: true 필요

4. Progressive Sync (단계적 동기화)

Rolling Sync 전략

ApplicationSet은 여러 Application을 단계적으로 동기화하는 기능을 제공합니다:

spec:
  strategy:
    type: RollingSync
    rollingSync:
      steps:
        - matchExpressions:
            - key: env
              operator: In
              values:
                - staging
          maxUpdate: 100% # staging 환경 먼저 전부 업데이트
        - matchExpressions:
            - key: env
              operator: In
              values:
                - production
          maxUpdate: 25% # production은 25%씩 업데이트

Progressive Sync 동작 원리

Step 1: staging 레이블이 있는 모든 Application 동기화
  -> 모든 Application이 Healthy가 될 때까지 대기

Step 2: production 레이블이 있는 Application25%만 동기화
  -> Healthy 확인 후 다음 25% 진행
  -> 모든 production Application이 완료될 때까지 반복

maxUpdate 옵션

설명
100%매칭되는 모든 Application을 동시에 업데이트
25%매칭되는 Application의 25%만 업데이트
1한 번에 1개만 업데이트
0자동 업데이트 없음 (수동으로 진행)

5. Cluster Decision Resource

개요

Cluster Decision Resource는 외부 컨트롤러가 Application의 대상 클러스터를 결정하도록 위임하는 메커니즘입니다:

spec:
  generators:
    - clusterDecisionResource:
        configMapRef: my-placement-decision
        name: placement-decision
        requeueAfterSeconds: 180

사용 사례

  • Open Cluster Management (OCM)의 Placement Decision 연동
  • 커스텀 클러스터 선택 로직 구현
  • 외부 정책 엔진 기반의 클러스터 라우팅

6. Reconciliation Loop

ApplicationSet Controller의 Reconciliation

반복:
  1. 모든 ApplicationSet 리소스 감시 (Informer)
  2.ApplicationSet에 대해:
     a. Generator 실행하여 파라미터 집합 생성
     b. 템플릿을 렌더링하여 원하는 Application 목록 생성
     c. 현재 존재하는 Application 목록 조회
     d. 비교하여:
        - 새로 추가할 Application 생성
        - 변경된 Application 업데이트
        - 제거 대상 Application 삭제 (정책에 따라)
  3. requeueAfterSeconds 후 재실행

Requeue 메커니즘

spec:
  generators:
    - pullRequest:
        github:
          owner: myorg
          repo: myapp
        requeueAfterSeconds: 60 # 60초마다 PR 상태를 다시 확인

requeueAfterSeconds는 Generator의 데이터 소스가 변경되었는지 주기적으로 확인합니다. Webhook을 사용할 수 없는 경우에 특히 유용합니다.

Event-Driven Reconciliation

ApplicationSet Controller는 다음 이벤트에 반응하여 즉시 reconciliation을 수행합니다:

  • ApplicationSet 리소스 변경
  • ArgoCD 클러스터 Secret 변경
  • Git Webhook 수신 (Git Generator)

7. Application 생명주기 관리

생성 정책

spec:
  syncPolicy:
    preserveResourcesOnDeletion: false # ApplicationSet 삭제 시 Application도 삭제

업데이트 정책

ApplicationSet은 다음 상황에서 Application을 업데이트합니다:

  • Generator 파라미터가 변경된 경우
  • 템플릿이 변경된 경우
  • ApplicationSet 자체가 수정된 경우

삭제 정책

spec:
  syncPolicy:
    preserveResourcesOnDeletion: true # ApplicationSet 삭제해도 Application 유지
옵션동작
preserveResourcesOnDeletion: falseApplicationSet 삭제 시 모든 Application도 삭제
preserveResourcesOnDeletion: trueApplicationSet 삭제해도 Application은 유지

Orphan Application 처리

Generator가 더 이상 특정 Application을 생성하지 않으면:

  • 기본: 해당 Application을 삭제
  • preserveResourcesOnDeletion: true: Application 유지

8. 실전 패턴

멀티클러스터 배포

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: platform-services
  namespace: argocd
spec:
  generators:
    - matrix:
        generators:
          - clusters:
              selector:
                matchLabels:
                  tier: production
          - git:
              repoURL: https://github.com/org/platform.git
              revision: HEAD
              directories:
                - path: services/*
  template:
    metadata:
      name: '{{name}}-{{path.basename}}'
    spec:
      project: platform
      source:
        repoURL: https://github.com/org/platform.git
        targetRevision: HEAD
        path: '{{path}}'
      destination:
        server: '{{server}}'
        namespace: '{{path.basename}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

PR 프리뷰 환경

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: preview-envs
  namespace: argocd
spec:
  generators:
    - pullRequest:
        github:
          owner: myorg
          repo: myapp
          labels:
            - deploy-preview
        requeueAfterSeconds: 30
  template:
    metadata:
      name: 'preview-{{number}}'
      annotations:
        preview-url: 'https://pr-{{number}}.preview.example.com'
    spec:
      project: previews
      source:
        repoURL: https://github.com/myorg/myapp.git
        targetRevision: '{{head_sha}}'
        path: deploy/preview
        helm:
          parameters:
            - name: ingress.host
              value: 'pr-{{number}}.preview.example.com'
            - name: image.tag
              value: '{{head_short_sha}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: 'preview-{{number}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

환경별 값 오버라이드 (Merge Generator)

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: app-with-overrides
  namespace: argocd
spec:
  generators:
    - merge:
        mergeKeys:
          - name
        generators:
          - clusters:
              values:
                replicas: '2'
                logLevel: info
          - list:
              elements:
                - name: production-cluster
                  values.replicas: '5'
                  values.logLevel: warn
  template:
    metadata:
      name: 'myapp-{{name}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/org/myapp.git
        targetRevision: HEAD
        path: deploy
        helm:
          parameters:
            - name: replicas
              value: '{{values.replicas}}'
            - name: logLevel
              value: '{{values.logLevel}}'
      destination:
        server: '{{server}}'
        namespace: myapp

9. 트러블슈팅

일반적인 문제와 해결

Application이 생성되지 않는 경우:

  • Generator의 selector가 올바른지 확인
  • Git 경로가 존재하는지 확인
  • ApplicationSet Controller 로그 확인

Application이 예상과 다르게 삭제되는 경우:

  • preserveResourcesOnDeletion 설정 확인
  • Generator의 출력이 변경되었는지 확인
  • ApplicationSet의 소유권 관리 확인

템플릿 렌더링 오류:

  • 파라미터 이름이 Generator 출력과 일치하는지 확인
  • Go 템플릿 사용 시 goTemplate: true 설정 확인
  • goTemplateOptions에서 missingkey 처리 방식 확인

디버깅 명령

# ApplicationSet 상태 확인
kubectl get applicationset -n argocd

# ApplicationSet 상세 정보 확인
kubectl describe applicationset my-appset -n argocd

# ApplicationSet Controller 로그 확인
kubectl logs -n argocd deployment/argocd-applicationset-controller

# 생성된 Application 확인
argocd app list

10. 정리

ApplicationSet Controller의 핵심 요소:

  1. Generator: 다양한 데이터 소스(클러스터, Git, PR, SCM)에서 파라미터 생성
  2. Matrix/Merge: Generator를 조합하여 복잡한 배포 토폴로지 구현
  3. Template Engine: fasttemplate 또는 Go 템플릿으로 Application 리소스 렌더링
  4. Progressive Sync: Rolling 업데이트 전략으로 안전한 대규모 배포
  5. Reconciliation: Event-driven + 주기적 polling으로 상태 동기화
  6. 생명주기 관리: 생성/업데이트/삭제 정책으로 Application 자동 관리

ApplicationSet을 효과적으로 활용하면 수백 개의 Application을 하나의 리소스로 관리할 수 있어, 대규모 GitOps 환경의 운영 복잡도를 크게 줄일 수 있습니다.