K8s ArgoCD GitOps 배포

GitOps란?

GitOps는 Git 저장소를 단일 진실 공급원(Single Source of Truth)으로 삼아 인프라와 애플리케이션을 선언적으로 관리하는 운영 방법론입니다. ArgoCD는 Kubernetes 환경에서 가장 널리 사용되는 GitOps 도구로, Git에 커밋된 매니페스트와 클러스터 상태를 자동으로 동기화합니다. 이 글에서는 ArgoCD의 아키텍처, Application 리소스 설정, Sync 전략, 멀티 클러스터 관리, Webhook 자동화까지 실전 패턴을 다룹니다.

ArgoCD 아키텍처

ArgoCD는 3개의 핵심 컴포넌트로 구성됩니다.

컴포넌트 역할 특징
API Server Web UI, CLI, gRPC API 제공 RBAC, SSO 인증 처리
Repo Server Git 저장소에서 매니페스트 생성 Helm, Kustomize, Jsonnet 지원
Application Controller 클러스터 상태와 Git 상태 비교·동기화 3분 주기 폴링 (기본값)

동작 흐름: Git 커밋 → Repo Server가 매니페스트 렌더링 → Controller가 차이 감지 → 클러스터에 적용(Sync)

ArgoCD 설치

Helm Chart로 설치하는 것이 운영 환경에서 권장됩니다.

# Helm으로 설치
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

helm install argocd argo/argo-cd 
  --namespace argocd 
  --create-namespace 
  --set server.service.type=LoadBalancer 
  --set configs.params.server.insecure=true 
  --set server.extraArgs={--insecure}

# 초기 admin 비밀번호 확인
kubectl -n argocd get secret argocd-initial-admin-secret 
  -o jsonpath="{.data.password}" | base64 -d

# CLI 로그인
argocd login argocd.example.com --username admin --password <password>

# 비밀번호 변경
argocd account update-password

Application 리소스 정의

ArgoCD의 핵심은 Application CRD입니다. Git 저장소와 클러스터 목적지를 연결합니다.

# 기본 Application 정의
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-api
  namespace: argocd
  # Finalizer: 앱 삭제 시 클러스터 리소스도 함께 삭제
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default

  source:
    repoURL: https://github.com/myorg/k8s-manifests.git
    targetRevision: main        # 브랜치, 태그, 커밋 해시
    path: apps/my-api/overlays/production

  destination:
    server: https://kubernetes.default.svc  # in-cluster
    namespace: production

  # 동기화 정책
  syncPolicy:
    automated:
      prune: true       # Git에서 삭제된 리소스 → 클러스터에서도 삭제
      selfHeal: true    # 수동 변경(kubectl edit) → Git 상태로 복원
      allowEmpty: false # 빈 매니페스트 방지
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - PruneLast=true  # 다른 리소스 먼저 동기화 후 삭제
    retry:
      limit: 3
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

Helm 차트 소스 설정

Git에 저장된 Helm 차트나 외부 Helm 레포지토리를 소스로 사용하는 패턴입니다.

# Git 내 Helm 차트 참조
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-api-helm
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/helm-charts.git
    targetRevision: main
    path: charts/my-api
    helm:
      releaseName: my-api
      # values 파일 참조 (같은 저장소 내)
      valueFiles:
        - values.yaml
        - values-prod.yaml
      # 인라인 값 오버라이드
      parameters:
        - name: image.tag
          value: "v1.5.2"
        - name: replicaCount
          value: "4"
      # values를 직접 정의
      values: |
        ingress:
          enabled: true
          hosts:
            - host: api.example.com
              paths:
                - path: /
                  pathType: Prefix

  destination:
    server: https://kubernetes.default.svc
    namespace: production

# 외부 Helm 레포지토리 사용
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: redis
  namespace: argocd
spec:
  source:
    repoURL: https://charts.bitnami.com/bitnami
    chart: redis           # chart 필드 사용 (path 대신)
    targetRevision: 18.6.1
    helm:
      releaseName: redis
      values: |
        architecture: standalone
        auth:
          enabled: true
          password: my-redis-pass
  destination:
    server: https://kubernetes.default.svc
    namespace: production

Kustomize 소스 설정

Kustomize의 overlay 패턴과 ArgoCD를 결합하면 환경별 배포를 깔끔하게 관리할 수 있습니다.

# 디렉터리 구조
apps/my-api/
├── base/
│   ├── kustomization.yaml
│   ├── deployment.yaml
│   ├── service.yaml
│   └── ingress.yaml
└── overlays/
    ├── staging/
    │   ├── kustomization.yaml
    │   └── replica-patch.yaml
    └── production/
        ├── kustomization.yaml
        ├── replica-patch.yaml
        └── resource-patch.yaml

# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
resources:
  - ../../base
patches:
  - path: replica-patch.yaml
  - path: resource-patch.yaml
images:
  - name: myorg/my-api
    newTag: v1.5.2
commonLabels:
  env: production

# ArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-api-prod
  namespace: argocd
spec:
  source:
    repoURL: https://github.com/myorg/k8s-manifests.git
    targetRevision: main
    path: apps/my-api/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production

AppProject로 권한 분리

팀별로 배포 가능한 클러스터, 네임스페이스, 저장소를 제한합니다. K8s ResourceQuota와 함께 멀티테넌시를 구성할 수 있습니다.

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: backend-team
  namespace: argocd
spec:
  description: "백엔드 팀 프로젝트"

  # 허용된 Git 저장소
  sourceRepos:
    - https://github.com/myorg/backend-*
    - https://charts.bitnami.com/bitnami

  # 배포 허용 대상
  destinations:
    - server: https://kubernetes.default.svc
      namespace: backend-*      # 와일드카드 지원
    - server: https://kubernetes.default.svc
      namespace: staging

  # 배포 허용/거부 리소스
  clusterResourceWhitelist:
    - group: ''
      kind: Namespace
  namespaceResourceBlacklist:
    - group: ''
      kind: ResourceQuota      # ResourceQuota 수정 불가

  # 역할 정의 (RBAC)
  roles:
    - name: developer
      description: "배포 및 동기화 권한"
      policies:
        - p, proj:backend-team:developer, applications, get, backend-team/*, allow
        - p, proj:backend-team:developer, applications, sync, backend-team/*, allow
      groups:
        - backend-developers    # SSO 그룹 매핑

    - name: viewer
      policies:
        - p, proj:backend-team:viewer, applications, get, backend-team/*, allow
      groups:
        - backend-viewers

Sync Wave와 Hook

리소스 간 배포 순서가 중요할 때 Sync Wave로 순서를 제어하고, Hook으로 배포 전후 작업을 실행합니다.

# Sync Wave: 숫자가 낮은 것부터 순서대로 적용
# Wave -1: 네임스페이스, ConfigMap
# Wave  0: 기본 (Deployment, Service)
# Wave  1: Ingress, HPA
# Wave  2: 모니터링 설정

# namespace.yaml — 가장 먼저 생성
apiVersion: v1
kind: Namespace
metadata:
  name: production
  annotations:
    argocd.argoproj.io/sync-wave: "-1"

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  annotations:
    argocd.argoproj.io/sync-wave: "-1"
data:
  DATABASE_URL: "jdbc:postgresql://db:5432/mydb"

# deployment.yaml — 기본 wave (0)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-api
  annotations:
    argocd.argoproj.io/sync-wave: "0"
# ...

# DB 마이그레이션 Job — PreSync Hook
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migrate
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: migrate
          image: myorg/my-api:v1.5.2
          command: ["npm", "run", "migration:run"]
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: url

# Slack 알림 — PostSync Hook
apiVersion: batch/v1
kind: Job
metadata:
  name: notify-deploy
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: notify
          image: curlimages/curl:8.5.0
          command:
            - curl
            - -X
            - POST
            - -H
            - "Content-Type: application/json"
            - -d
            - '{"text":"✅ my-api v1.5.2 배포 완료"}'
            - $(SLACK_WEBHOOK_URL)
          env:
            - name: SLACK_WEBHOOK_URL
              valueFrom:
                secretKeyRef:
                  name: slack-webhook
                  key: url

ApplicationSet으로 대량 앱 관리

여러 환경이나 마이크로서비스를 하나의 템플릿으로 생성합니다.

# List Generator: 환경별 앱 자동 생성
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: my-api-envs
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - env: staging
            namespace: staging
            revision: develop
            replicas: "1"
          - env: production
            namespace: production
            revision: main
            replicas: "4"
  template:
    metadata:
      name: 'my-api-{{env}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/k8s-manifests.git
        targetRevision: '{{revision}}'
        path: 'apps/my-api/overlays/{{env}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{namespace}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

# Git Generator: 디렉터리 구조에서 자동 검색
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: all-apps
  namespace: argocd
spec:
  generators:
    - git:
        repoURL: https://github.com/myorg/k8s-manifests.git
        revision: main
        directories:
          - path: apps/*/overlays/production
  template:
    metadata:
      name: '{{path.basename}}-prod'
    spec:
      project: default
      source:
        repoURL: https://github.com/myorg/k8s-manifests.git
        targetRevision: main
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: production

Webhook으로 즉시 동기화

ArgoCD는 기본 3분 주기로 Git을 폴링합니다. GitHub Webhook을 설정하면 커밋 즉시 동기화를 트리거할 수 있습니다.

# GitHub Webhook 설정
# URL: https://argocd.example.com/api/webhook
# Content type: application/json
# Secret: argocd-webhook-secret
# Events: Push events

# ArgoCD에 Webhook secret 등록
kubectl -n argocd edit configmap argocd-secret

# data 섹션에 추가
data:
  webhook.github.secret: "argocd-webhook-secret"

# 또는 Notification으로 Slack 연동
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  service.slack: |
    token: $slack-token
  trigger.on-sync-succeeded: |
    - when: app.status.sync.status == 'Synced'
      send: [app-sync-succeeded]
  template.app-sync-succeeded: |
    slack:
      attachments: |
        [{
          "color": "#18be52",
          "title": "{{.app.metadata.name}} 동기화 완료",
          "text": "리비전: {{.app.status.sync.revision}}",
          "fields": [{
            "title": "네임스페이스",
            "value": "{{.app.spec.destination.namespace}}",
            "short": true
          }]
        }]

정리

ArgoCD GitOps는 “Git에 커밋 = 배포”라는 단순한 원칙으로 Kubernetes 운영을 혁신합니다. Application CRD로 소스와 목적지를 선언하고, automated.selfHeal로 드리프트를 자동 복구하며, Sync Wave/Hook으로 배포 순서를 제어합니다. ApplicationSet으로 환경별·서비스별 앱을 대량 생성하고, AppProject로 팀별 권한을 분리하세요. Git 히스토리가 곧 배포 히스토리가 되는 것이 GitOps의 핵심 가치입니다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux