이 글에서 다루는 것

ArgoCD ApplicationSet으로 dev/prod 환경을 단일 템플릿으로 관리하고, MetalLB로 온프레미스 LoadBalancer를 구성하며, App-of-Apps 패턴으로 전체 플랫폼을 선언적으로 동기화하는 GitOps 고도화 과정을 다룹니다.

선수지식


이 단계에서 해결하려는 문제

환경(dev/prod)이 늘어날수록 ArgoCD Application YAML이 중복되고, 환경 간 설정 드리프트가 발생한다. 온프레미스 환경에서는 클라우드 로드밸런서가 없어 외부 접근이 어렵다. 개별 Application을 수동으로 관리하면 전체 플랫폼 상태를 한눈에 파악하기 힘들다.


🎯 핵심 요약

  • ApplicationSet: List Generator로 dev/prod 환경 매트릭스를 단일 템플릿으로 관리
  • MetalLB: L2 모드로 온프레미스에서 LoadBalancer 타입 Service에 실제 IP 할당
  • App-of-Apps 패턴: 최상위 Application이 하위 Application들을 관리하여 전체 플랫폼 상태를 Git 한 곳에서 선언
  • Sync Wave: MLflow(10) → Airflow(20) → Triton(30) → FastAPI(40) 순서 보장

1️⃣ ApplicationSet — 환경 매트릭스 자동화

구조

# apps/applicationset-mlops.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: mlops-platform
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - env: dev
            namespace: mlops-dev
            valuesFile: values/dev.yaml
          - env: prod
            namespace: mlops-prod
            valuesFile: values/prod.yaml
  template:
    metadata:
      name: '{{env}}-{{chart}}'
      annotations:
        argocd.argoproj.io/sync-wave: '{{syncWave}}'
    spec:
      project: '{{env}}'
      source:
        repoURL: https://github.com/keonhoban/mlops-infra-gitops
        targetRevision: main
        path: 'charts/{{chart}}'
        helm:
          valueFiles:
            - '{{valuesFile}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{namespace}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

환경을 추가하려면 elements에 항목만 추가하면 된다. 차트나 동기화 정책의 코드 중복이 없다.


2️⃣ MetalLB — 온프레미스 LoadBalancer

IPAddressPool + L2Advertisement

# bootstrap/metallb/ip-pool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: mlops-pool
  namespace: metallb-system
spec:
  addresses:
    - 192.168.18.240-192.168.18.250
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: mlops-l2
  namespace: metallb-system
spec:
  ipAddressPools:
    - mlops-pool

동작 방식

  1. LoadBalancer 타입 Service가 생성되면 MetalLB가 Pool에서 IP 할당
  2. L2 모드는 ARP 응답으로 해당 IP를 Node에 바인딩
  3. 라우터 설정 없이 같은 서브넷에서 즉시 접근 가능
# 검증
kubectl -n metallb-system get ipaddresspool
kubectl get svc -A | grep LoadBalancer

3️⃣ App-of-Apps 패턴

구조

apps/
├─ root-app.yaml            # 최상위 Application (apps/ 디렉토리 자체를 소스로 지정)
├─ project-dev.yaml          # AppProject: dev
├─ project-prod.yaml         # AppProject: prod
├─ mlflow-dev.yaml           # Application: MLflow dev
├─ mlflow-prod.yaml          # Application: MLflow prod
├─ airflow-dev.yaml          # Application: Airflow dev
├─ ...
└─ applicationset-mlops.yaml # (위 1️⃣의 ApplicationSet)
# apps/root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/keonhoban/mlops-infra-gitops
    targetRevision: main
    path: apps
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

root Application이 apps/ 디렉토리의 모든 Application YAML을 자동으로 감지·동기화한다.


4️⃣ Sync Wave 순서

Wave컴포넌트이유
10MLflow모델 레지스트리가 먼저 떠야 Airflow가 연동 가능
20AirflowMLflow 의존, DAG 실행 준비
30Triton모델 파일이 NFS에 존재해야 로딩 가능
40FastAPIMLflow + Triton 의존, 최종 서빙 레이어
# Application 또는 ApplicationSet 템플릿의 annotations
annotations:
  argocd.argoproj.io/sync-wave: "10"  # MLflow 예시

5️⃣ 검증

# 전체 Application 상태 확인
argocd app list

# Sync 상태
argocd app get root

# MetalLB IP 할당 확인
kubectl get svc -A -o wide | grep LoadBalancer

# ApplicationSet 생성된 앱 확인
kubectl -n argocd get applicationset
kubectl -n argocd get app -l app.kubernetes.io/instance=mlops-platform

체크리스트

  • ApplicationSet의 elements에 dev/prod 환경 정의 완료
  • MetalLB IPAddressPool 대역이 실제 네트워크와 충돌하지 않는지 확인
  • App-of-Apps root Application이 apps/ 디렉토리를 정확히 가리킴
  • Sync Wave 순서가 의존성 순서와 일치 (MLflow → Airflow → Triton → FastAPI)
  • selfHeal: true + prune: true 설정 확인

🧩 팁

  • ApplicationSet 디버깅: kubectl -n argocd describe applicationset <name>으로 Generator 결과 확인
  • MetalLB IP 충돌: 다른 서비스가 같은 대역을 사용하고 있으면 ARP 충돌 발생 — Pool 대역을 전용으로 분리
  • Sync Wave 주의: Wave가 같은 리소스는 병렬 동기화 — 의존성이 있으면 반드시 다른 Wave 번호 할당

설계 판단 (Why This Way?)

ApplicationSet으로 dev/prod 환경을 하나의 템플릿에서 관리하여 코드 중복과 설정 드리프트를 제거하고, App-of-Apps 패턴으로 클러스터 전체 상태를 Git 단일 소스에서 선언적으로 관리합니다. 소규모 온프레미스에서는 MetalLB L2 모드가 설정이 간단합니다.


다음에 읽을 글

MLOps 운영 고도화 8단계: CI/CD 운영 자동화 — GitHub Actions + Helm Lint 파이프라인