본문 바로가기
배포·CI,CD·클라우드

Argo CD로 GitOps 파이프라인 구축: App-of-Apps, 환경 분리, 자동 동기화/PR 기반 승격

by yamoojin83 2025. 11. 1.

Argo CD로 GitOps 파이프라인 구축: App-of-Apps, 환경 분리, 자동 동기화/PR 기반 승격

Argo Rollouts로 카나리/블루그린을 자동화했다면, 이제 Argo CD배포 상태 = Git을 구현해 보겠습니다. 핵심은 “모든 클러스터 상태를 선언적으로 Git에 보관하고, Argo CD가 주기적으로 이를 관찰해 자동 동기화”하는 것입니다. 이 글은 App-of-Apps 패턴으로 다수의 애플리케이션을 관리하고, dev→staging→prodPR 승인 기반 승격을 만드는 실전 템플릿을 제공합니다.


1) 저장소 구조(권장 예시)

gitops/
├─ apps/                      # 개별 앱 선언(Helm/Kustomize)
│  └─ order-api/
│     ├─ base/                # 공통 정의
│     │  ├─ kustomization.yaml
│     │  ├─ deployment.yaml
│     │  └─ service.yaml
│     └─ overlays/
│        ├─ dev/
│        │  ├─ kustomization.yaml
│        │  └─ values-dev.yaml
│        ├─ staging/
│        │  └─ ...
│        └─ prod/
│           └─ ...
└─ clusters/
   ├─ dev/
   │  └─ root-app.yaml        # App-of-Apps (dev)
   ├─ staging/
   │  └─ root-app.yaml
   └─ prod/
      └─ root-app.yaml

한 줄 요약: apps/*에 앱 템플릿(Helm/Kustomize), clusters/*에 각 환경의 루트 앱을 둡니다. 루트 앱은 하위 앱들을 자식 App으로 선언해서 환경별 일괄 배포를 가능하게 합니다.


2) Argo CD 설치 및 최초 접근

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# 초기 비밀번호
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
kubectl -n argocd port-forward svc/argocd-server 8080:443
# https://localhost:8080 접속 → admin / (초기 비번)

실서비스에서는 Ingress(또는 LB)를 붙이고 SSO/OIDC로 로그인 연동을 권장합니다.


3) App-of-Apps: 환경 루트 앱 정의

dev 클러스터의 루트 앱 예시입니다(스테이징/프로드는 repoURL/경로만 다르게 복제).

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-dev
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-org/gitops.git
    targetRevision: main
    path: clusters/dev
    directory:
      recurse: true
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true      # Git에 없는 리소스 삭제
      selfHeal: true   # 드리프트 자동 복구
    syncOptions:
      - CreateNamespace=true

clusters/dev 경로에는 실제로 “자식 App”들을 선언한 매니페스트(아래 4장)를 둡니다.


4) 자식 App 선언(여러 서비스 동시 관리)

예: dev 환경에 order-api, user-api를 함께 올립니다.

# clusters/dev/order-api.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: order-api-dev
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-org/gitops.git
    targetRevision: main
    path: apps/order-api/overlays/dev
  destination:
    server: https://kubernetes.default.svc
    namespace: order-api
  syncPolicy:
    automated: { prune: true, selfHeal: true }
    syncOptions: [ "CreateNamespace=true" ]

---
# clusters/dev/user-api.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: user-api-dev
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-org/gitops.git
    targetRevision: main
    path: apps/user-api/overlays/dev
  destination:
    server: https://kubernetes.default.svc
    namespace: user-api
  syncPolicy:
    automated: { prune: true, selfHeal: true }

이런 자식 App 파일들을 clusters/dev에 두고, 루트 앱(root-dev)이 재귀로 읽게 합니다.


5) Kustomize/Helm로 환경 차이 오버레이

order-api의 dev 오버레이 예시입니다.

# apps/order-api/overlays/dev/kustomization.yaml
resources:
  - ../../base
patches:
  - target:
      kind: Deployment
      name: order-api
    path: patch-deploy.yaml
configMapGenerator:
  - name: order-api-config
    literals:
      - SPRING_PROFILES_ACTIVE=dev
secretGenerator:
  - name: db-secret
    literals:
      - DB_URL=jdbc:postgresql://dev-db:5432/shop
      - DB_USER=shop
      - DB_PASSWORD=devpass
generatorOptions:
  disableNameSuffixHash: true
# apps/order-api/overlays/dev/patch-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-api
spec:
  replicas: 2
  template:
    spec:
      containers:
        - name: app
          image: registry.example.com/order-api:1.2.3-dev
          env:
            - name: JAVA_TOOL_OPTIONS
              value: "-Xms512m -Xmx512m"

프로드에서는 replicas·리소스·이미지 태그·도메인·시크릿을 달리합니다. 비밀값은 시크릿 매니저(Sealed Secrets, External Secrets) 연계를 추천합니다.


6) PR 기반 승격(Promotion) 전략

  1. 개발 완료 → CI에서 이미지 태깅 :1.2.3apps/order-api/overlays/dev의 태그 업데이트 PR 생성
  2. dev 동기화 OK → staging 브랜치(또는 폴더)로 동일 태그 PR
  3. staging에서 기능/성능 검증 OK → prod로 PR 머지 → Argo CD가 자동 배포

즉, 승격 행위YAML의 이미지 태그를 바꾸는 PR에 불과합니다. 기록이 남고, 롤백도 쉽습니다 (이전 커밋으로 되돌리기).


7) 동기화(자동/수동)와 보호장치

  • 자동 동기화(automated.prune/selfHeal): Git ≒ 클러스터 상태를 유지
  • 수동 승인: prod만 manual sync로 두고, Merge 후 Sync 버튼으로 승격
  • Sync Waves: 종속성을 고려해 리소스를 순서대로 적용(인그레스→서비스→롤아웃)
# wave 주기(주석)
metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "0"   # 작은 숫자 먼저 적용

8) Rollouts 연계: 카나리 자동 승격/중단 포함

Argo CD는 단순히 매니페스트를 동기화합니다. 런타임 단계 제어는 Rollouts가 담당합니다. 즉, Git에 Rollout을 커밋하면, Argo CD가 이를 반영하고, Rollouts가 가중치 전환 + 지표 판정을 실행합니다. 실패 시 자동 중단/롤백이 일어나므로 GitOps + Progressive Delivery가 결합됩니다.


9) 알림/감시: 드리프트·Sync 실패·헬스 체크

  • 드리프트: 클러스터에서 수동 변경 발생 시 OutOfSync 감지 → SelfHeal로 복구
  • Sync 실패: Slack/Webhook 연동으로 이벤트 통지(Argo CD Notifications)
  • 헬스: 애플리케이션 Health(Progressing/Degraded/Healthy)로 대시보드 모니터링
# Notifications 설정(요지)
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  service.slack: |
    token: xoxb-***
  trigger.on-sync-failed: |
    - when: app.status.operationState.phase in ['Error','Failed']
      send: [slack]
  template.slack: |
    message: |
      [{{.app.metadata.name}}] {{.app.status.operationState.phase}}

10) 보안/권한

  • RBAC: prod 네임스페이스는 읽기 전용 역할을 대부분에게 부여, Sync 권한은 일부에 제한
  • SSO/OIDC: 팀/조직 계정으로 접근 제어, 감사 로그 유지
  • 비밀 관리: Sealed Secrets/External Secrets(Cloud KMS, Vault)로 Git 평문 방지

11) 트러블슈팅 빠른 분기

  • OutOfSync 반복 → 클러스터 내 수동 변경 탐지, selfHeal 켜고, 원인 리소스에 주석/소유권 확인
  • Sync 실패Events에서 원인 확인(네임스페이스 권한/CRD 순서/헬름 차트 값)
  • 프로드만 자동 배포 금지 → prod 루트 앱에서 automated 제거, 수동 Sync로 전환
  • 분기/폴더 혼용 → “브랜치=환경”, “폴더=서비스” 원칙으로 단순화

12) 체크리스트(복붙용)

  • 저장소 구조: apps/* + clusters/*, 환경별 루트 앱(App-of-Apps)
  • Argo CD 설치, admin 초기 비밀번호 변경/SSO 적용
  • 루트 앱 생성 → 자식 App 재귀 로딩 확인
  • 자동 동기화(prune/selfHeal) 여부를 환경별로 결정
  • PR 승인으로 이미지 태그 승격(dev→staging→prod)
  • Rollouts와 결합: 카나리/블루그린 지표 판정 자동화
  • 알림/감사: Notifications + RBAC + Sealed/External Secrets

Argo CD를 도입하면 “누가 언제 무엇을 배포했는가”가 Git 히스토리로 남습니다. PR 승인으로 승격하고, 실패 시 커밋 하나로 롤백하세요. 이것이 안전하고 재현 가능한 운영의 출발점입니다.