ArgoCD GitOps 구성

OpenEG 시스템을 위한 GitOps 기반 Kubernetes 인프라 자동화

📋 프로젝트 개요

OpenEG 교육 기술 시스템(Flask 웹 서버, Node.js 서버, Webhook 등)을 Kubernetes 환경에 GitOps 방식으로 배포 및 관리하는 인프라 자동화 프로젝트입니다.

핵심 개념: GitOps

  • Git = Single Source of Truth: 모든 인프라 설정을 Git으로 관리
  • 선언적 구성: 원하는 상태를 YAML로 선언
  • 자동 동기화: ArgoCD가 Git 상태를 클러스터에 자동 반영
  • 불변성: 이미지 태그를 Git Commit Hash로 고정

🎯 해결한 문제

기존 방식의 문제점

  • 수동 배포: kubectl apply로 수동 배포 (오류 발생 가능)
  • 재현성 부족: “내 컴퓨터에선 되는데요?” 문제
  • 시크릿 관리: 민감 정보를 Git에 저장 불가
  • 추적 어려움: 누가, 언제, 무엇을 변경했는지 불명확

GitOps 솔루션

  • 자동화: Git push만 하면 자동 배포
  • 선언적 관리: YAML 파일로 모든 상태 정의
  • 버전 관리: Git으로 모든 변경 이력 추적
  • 롤백 용이: Git revert로 즉시 이전 상태 복구
  • 보안: Vault + Sealed Secrets로 시크릿 안전 관리

🛠 기술 스택

GitOps Core

  • ArgoCD: GitOps 배포 도구 (CD)
  • Kustomize: 환경별 설정 관리 (base + overlays)

Secret Management

  • HashiCorp Vault: 동적 시크릿 주입 (런타임 환경변수)
  • Sealed Secrets: 정적 시크릿 암호화 (K8s 리소스)

Monitoring Stack

  • Prometheus: 메트릭 수집
  • Grafana: 시각화 대시보드
  • Loki + Promtail: 로그 중앙화

Infrastructure

  • Kubernetes: 컨테이너 오케스트레이션
  • Docker: 컨테이너 런타임
  • Private Registry: 사설 이미지 저장소 (registry.oud.kr)

💡 주요 구현 내용

1. App of Apps 패턴

전체 시스템의 진입점: argocd/applications/openeg-system.yaml

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: openeg-system
spec:
  source:
    path: overlays/production
    repoURL: https://github.com/***
  destination:
    namespace: openeg-system
    server: https://kubernetes.default.svc
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true

특징:

  • 하나의 ArgoCD Application이 전체 시스템 관리
  • 자동 동기화 (prune, selfHeal)
  • Slack 알림 통합 (cicd 채널)

2. Kustomize 기반 환경 관리

리포지토리 구조:
├── base/                    # 공통 베이스
│   ├── deployments/        # Flask, Node.js 등
│   ├── services/           # ClusterIP, NodePort
│   └── monitoring/         # Prometheus, Grafana
│
└── overlays/
    └── production/         # 프로덕션 오버레이
        ├── kustomization.yaml
        ├── patches/        # 환경별 패치
        └── secrets/        # Sealed Secrets

kustomization.yaml 예시:

bases:
- ../../base
 
# 이미지 태그 고정 (Git Commit Hash)
images:
- name: openegflask
  newName: registry.oud.kr/openegflask
  newTag: 19717d45e19b8de5ae7a31217f396506724d57c0
 
# 환경별 설정 패치
patchesStrategicMerge:
- patches/production-config.yaml
- patches/resources.yaml
 
# Sealed Secrets
resources:
- secrets/registry-cred.sealed.yaml
- secrets/app-secrets.sealed.yaml

3. 이중 시크릿 관리 전략

HashiCorp Vault (동적 주입) ⭐

용도: 애플리케이션 런타임 환경변수 (DB 비밀번호 등)

# Flask Deployment에 Vault Agent Injector 적용
annotations:
  vault.hashicorp.com/agent-inject: 'true'
  vault.hashicorp.com/role: 'openeg-role'
  vault.hashicorp.com/agent-inject-secret-mongodb: 'openeg/data/data/databases/mongodb'
  vault.hashicorp.com/agent-inject-template-mongodb: |
    {{- with secret "openeg/data/data/databases/mongodb" -}}
    export MONGODB_URI="{{ .Data.data.uri }}"
    export MONGODB_USER="{{ .Data.data.user }}"
    {{- end }}

작동 방식:

1. Pod 시작 → vault-agent-init 컨테이너 실행
2. Vault 서버 인증 (Kubernetes Auth)
3. /vault/secrets/ 경로에 파일 생성
4. 애플리케이션 시작 스크립트에서 source로 로드

Sealed Secrets (정적 암호화)

용도: Kubernetes 리소스 자체 시크릿 (레지스트리 자격증명)

# 암호화 과정
kubectl create secret docker-registry registry-cred \
  --docker-server=registry.oud.kr \
  --docker-username=*** \
  --docker-password=*** \
  --dry-run=client -o yaml | \
kubeseal -o yaml > registry-cred.sealed.yaml
 
# Git에 커밋 (암호화되어 안전)
git add overlays/production/secrets/registry-cred.sealed.yaml

4. 이미지 불변성 보장

Git Commit Hash로 이미지 태그 고정:

# CI/CD 파이프라인
1. 코드 변경 → Git Push
2. GitHub Actions 트리거
3. Docker 이미지 빌드
   - 태그: <git-commit-hash>
4. Private Registry 푸시
5. kustomization.yaml 업데이트
   - newTag: <git-commit-hash>
6. GitOps 리포지토리에 커밋
7. ArgoCD 자동 동기화

장점:

  • 특정 버전으로 즉시 롤백 가능
  • 어떤 코드가 배포되었는지 명확
  • latest 태그 사용 금지 (불변성)

5. 모니터링 스택 통합

# base/monitoring/ 구조
├── prometheus/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── configmap.yaml
├── grafana/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── dashboards/
├── loki/
│   └── statefulset.yaml
└── promtail/
    └── daemonset.yaml

통합 메트릭:

  • Node Exporter: 하드웨어 메트릭 (CPU, Memory, Disk)
  • Prometheus: 애플리케이션 메트릭 수집
  • Grafana: 대시보드 시각화
  • Loki + Promtail: 로그 중앙화

🏗 시스템 아키텍처

GitOps 배포 플로우

[개발자]
    ↓ (코드 변경)
[Git Push]
    ↓
[GitHub Actions CI]
    ↓ (빌드 & 푸시)
[Docker Image]
    ↓ (태그: commit-hash)
[Private Registry]
    ↓
[GitOps 리포지토리 업데이트]
    ↓ (kustomization.yaml)
[Git Commit]
    ↓ (3분마다 폴링)
[ArgoCD 감지]
    ↓ (자동 Sync)
[Kubernetes 클러스터]
    ↓
[Running Pods]
    ↓ (Slack 알림)
[운영팀 확인]

클러스터 아키텍처

Kubernetes Cluster
├── openeg-system (Namespace)
│   ├── Flask App (Deployment)
│   ├── Node.js Server (Deployment)
│   ├── Webhook Service (Deployment)
│   └── Services (ClusterIP/NodePort)
│
├── vault-system (Namespace)
│   └── Vault Server (StatefulSet)
│
└── monitoring (Namespace)
    ├── Prometheus (Deployment)
    ├── Grafana (Deployment)
    ├── Loki (StatefulSet)
    └── Promtail (DaemonSet)

📊 주요 설정 파일

Flask App Deployment (Vault 통합)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: flask-app
spec:
  template:
    metadata:
      annotations:
        # Vault Agent Injector
        vault.hashicorp.com/agent-inject: 'true'
        vault.hashicorp.com/role: 'openeg-role'
        # MongoDB 시크릿
        vault.hashicorp.com/agent-inject-secret-mongodb: 'openeg/data/data/databases/mongodb'
        # Redis 시크릿
        vault.hashicorp.com/agent-inject-secret-redis: 'openeg/data/data/databases/redis'
    spec:
      # Control Plane 노드 스케줄링
      nodeSelector:
        node-role.kubernetes.io/control-plane: ""
      tolerations:
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      
      containers:
      - name: flask
        image: registry.oud.kr/openegflask:latest
        args:
        - /bin/sh
        - -c
        - |
          # Vault 시크릿 로드
          source /vault/secrets/mongodb
          source /vault/secrets/redis
          # Flask 실행
          python app.py
        livenessProbe:
          httpGet:
            path: /health
            port: 5000
        readinessProbe:
          httpGet:
            path: /health
            port: 5000

📈 성과

배포 자동화

  • 배포 시간: 수동 30분 → 자동 5분
  • 오류율: 수동 배포 오류 90% 감소
  • 배포 빈도: 주 1회 → 일 3회 (빠른 피드백)

인프라 관리

  • 추적성: Git으로 모든 변경 이력 100% 추적
  • 롤백 시간: 수동 1시간 → Git revert 30초
  • 일관성: 환경별 설정 불일치 0건

보안

  • 시크릿 노출: Git에 평문 시크릿 0건
  • 권한 관리: Vault Role 기반 세밀한 접근 제어
  • 감사 로그: 모든 시크릿 접근 Vault 로그 기록

모니터링

  • 가시성: Prometheus + Grafana로 실시간 모니터링
  • 로그 중앙화: Loki로 모든 Pod 로그 수집
  • 알림: Slack으로 배포 성공/실패 즉시 통지

🔧 운영 가이드

새 버전 배포

# 1. 코드 변경 및 푸시 (자동 CI 트리거)
git commit -m "feat: 새 기능 추가"
git push origin main
 
# 2. CI에서 빌드된 이미지 태그 확인
# 예: 19717d45e19b8de5ae7a31217f396506724d57c0
 
# 3. GitOps 리포지토리 업데이트
cd openeg-gitops
vim overlays/production/kustomization.yaml
 
# images 섹션 수정
images:
- name: openegflask
  newTag: 19717d45e19b8de5ae7a31217f396506724d57c0
 
# 4. 커밋 및 푸시
git commit -m "deploy: Flask v2.1.0"
git push
 
# 5. ArgoCD 자동 동기화 (3분 이내)
# 6. Slack 알림 확인

환경변수 변경

일반 설정 (ConfigMap):

vim overlays/production/patches/production-config.yaml
git commit -m "config: API 엔드포인트 변경"
git push

민감 정보 (Vault):

# Vault CLI 사용
vault kv put openeg/data/databases/mongodb \
  uri="mongodb://***" \
  user="***"
 
# Pod 재시작 (자동으로 새 시크릿 로드)
kubectl rollout restart deployment/flask-app -n openeg-system

롤백

# Git으로 이전 커밋으로 되돌리기
git revert HEAD
git push
 
# 또는 특정 커밋으로
git reset --hard <commit-hash>
git push --force
 
# ArgoCD가 자동으로 이전 상태로 복구

🚨 문제 해결 (Troubleshooting)

Pod가 시작하지 않을 때

# 1. Pod 상태 확인
kubectl get pods -n openeg-system
 
# 2. 상세 정보 확인
kubectl describe pod <pod-name> -n openeg-system
 
# 3. Vault Agent Init 단계 확인
kubectl logs <pod-name> -c vault-agent-init -n openeg-system
 
# 일반적인 원인:
# - Vault Role 권한 부족
# - Vault 서버 다운
# - 시크릿 경로 오류

ArgoCD 동기화 실패

# 1. ArgoCD UI에서 오류 확인
# https://argocd.example.com
 
# 2. 로컬에서 Kustomize 빌드 테스트
cd overlays/production
kustomize build .
 
# 3. YAML 문법 오류 확인
yamllint overlays/production/*.yaml

Sealed Secrets 디코딩 실패

# 1. Sealed Secrets Controller 확인
kubectl get pods -n kube-system | grep sealed-secrets
 
# 2. 재암호화
kubeseal --fetch-cert > pub-cert.pem
kubectl create secret ... | kubeseal --cert pub-cert.pem -o yaml

🔗 관련 프로젝트

배포 대상

인프라 연관

📝 배운 점

GitOps 패턴 마스터

  1. 선언적 vs 명령형

    • 선언적: “이렇게 되어야 한다” (YAML)
    • 명령형: “이렇게 해라” (kubectl apply)
    • GitOps는 선언적 방식으로 일관성 보장
  2. Git as Single Source of Truth

    • Git이 모든 진실의 원천
    • 클러스터 상태는 Git 상태를 따름
    • 수동 변경은 즉시 되돌려짐 (selfHeal)
  3. 불변 인프라 (Immutable Infrastructure)

    • 이미지 태그를 Git Commit Hash로 고정
    • 변경은 새 이미지 빌드로만 가능
    • 롤백이 단순하고 안전

Kubernetes 운영 노하우

  1. 시크릿 관리의 이중화

    • Vault: 동적 시크릿 (런타임 주입)
    • Sealed Secrets: 정적 시크릿 (K8s 리소스)
    • 각각의 용도에 맞게 사용
  2. Kustomize vs Helm

    • Kustomize: 단순, 학습 곡선 낮음, 패치 기반
    • Helm: 복잡, 템플릿 기반, 패키지 관리
    • 이 프로젝트는 Kustomize 선택 (단순성)
  3. 모니터링 필수성

    • Prometheus + Grafana는 필수
    • Liveness/Readiness Probe 설정 중요
    • Slack 알림으로 빠른 대응

자동화의 가치

  • 신뢰성: 사람 실수 제거
  • 재현성: 언제든 같은 결과
  • 속도: 배포 시간 대폭 단축
  • 협업: Git으로 변경 이력 공유

🔮 향후 계획

단기

  • Multi-cluster 지원 (Dev/Staging/Prod 분리)
  • Canary 배포 전략 도입
  • 자동화된 보안 스캔 (Trivy)

중기

  • GitOps로 전체 인프라 관리 (Terraform → GitOps)
  • Progressive Delivery (Flagger)
  • Disaster Recovery 자동화

장기

  • Service Mesh 도입 (Istio)
  • Cost Optimization 자동화
  • Multi-Cloud 지원

프로젝트 기간: 2024.01 - 2025.12 현재 상태: 완료됨 (운영 중) 네임스페이스: openeg-system 최종 업데이트: 2025-12-21