이 글에서 다루는 것

GitHub Actions로 Helm lint + kubeconform + yamllint 정적 검증을 자동화하고, ArgoCD GitOps 배포 + FastAPI alias 기반 트래픽 실험까지 하나의 커밋으로 이어지는 CI/CD 운영 자동화 구조를 다룹니다.

선수지식


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

운영 환경에서 사고는 대부분 배포 전에 체크되지 못한 문제, 수동 배포의 실수, 배포 후 검증 부재에서 시작된다. CI 단계에서 문제를 막고, CD에서는 GitOps가 원하는 상태를 자동으로 보장하며, 배포 직후 FastAPI alias로 모델 트래픽까지 자동 검증되는 구조를 만든다.


🎯 핵심 요약

  • Helm Lint(strict) + kubeconform + yamllint로 PR 단계에서 오류/오탈자 차단
  • **GitHub Actions → ArgoCD(ApplicationSet)**로 GitOps 배포 자동화
  • A/B·Canary·Blue-Green을 FastAPI alias 기반으로 배포 (코드 수정 없이 실험 전환)
  • Slack 연동으로 CI → CD → 실험 결과까지 엔드투엔드 모니터링
  • “PR → Merge → Auto Deploy → 트래픽 실험” 완전 자동 사이클

1️⃣ 전체 구조 요약

mermaid-08.png


2️⃣ GitHub Actions – Helm CI (Advanced Validation)

목표: PR에서 형식·스키마·스타일 문제를 완전히 걸러내기

  • Helm Lint(strict): 값/템플릿 정합성 검증
  • kubeconform: 쿠버네티스 스키마 검증(주요 코어 리소스; CRD류는 스킵)
  • yamllint: 서식/인덴트/불필요 공백 검사
  • env×chart 매트릭스(dev/prod × airflow/fastapi/mlflow) 병렬 검증

워크플로 핵심 구조

# .github/workflows/ci-helm-validate.yaml (핵심부)
name: Helm CI
on:
  pull_request:
    paths: ['charts/**', 'envs/**', 'apps/**']
    types: [opened, synchronize, reopened, ready_for_review]

jobs:
  validate:
    name: Lint & Render & Validate (${{ matrix.env }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        env: [dev, prod]
        chart: [airflow, fastapi, mlflow]
    env:
      KUBE_VERSION: "1.30.0"
      SKIP_KINDS: "Application,ApplicationSet,AppProject,SealedSecret,Certificate,..."

    steps:
      - uses: actions/checkout@v4
      - uses: azure/setup-helm@v4
        with: { version: v3.18.3 }

      - name: YAML style check (repo-wide)
        run: git ls-files -z '*.yml' '*.yaml' ... | xargs -0 -r yamllint -s

      - name: Helm lint (strict)
        working-directory: charts/${{ matrix.chart }}
        run: helm lint . -f values/base.yaml -f values/${{ matrix.env }}.yaml --strict

      - name: Render manifests with helm template
        run: helm template ... > /tmp/rendered/${{ matrix.chart }}-${{ matrix.env }}.yaml

      - name: kubeconform (core K8s kinds only)
        run: kubeconform -kubernetes-version "${KUBE_VERSION}" -ignore-missing-schemas -skip "${SKIP_KINDS}" ...

kubeconform/yamllint가 envs/*apps/*를 분리 검사. Helm 차트는 dev/prod 병렬 검증.


3️⃣ GitOps CD – ArgoCD Auto Sync & SelfHeal

  • apps/appset-env-matrix.yaml 기반으로 dev/prod × 앱 자동 생성
  • automated.prune: true + selfHeal: true 유지 → 드리프트 자동 복구
# 필요 시 수동 트리거/점검
argocd app sync dev-fastapi --prune --grpc-web
argocd app get  dev-fastapi
argocd app diff dev-fastapi

역할 분리: CI는 “문제 차단”, ArgoCD는 “원하는 상태 보장”.


4️⃣ FastAPI 라우팅(공통 실험 로직)

# app/services/alias_selector.py (요지)
if settings.alias_selection_mode == "ab_test":
    return "A" if hashed % 100 < 90 else "B"
elif settings.alias_selection_mode == "canary":
    return "B" if hashed % 100 < settings.canary_percent else "A"
elif settings.alias_selection_mode == "blue_green":
    return settings.default_alias

alias 기반이라 모델 교체는 MLflow/alias 변경 + /reload로 일원화. 실험 정책은 환경변수로만 제어 → 배포 없이 즉시 전환 가능.


5️⃣ A/B·Canary·Blue-Green 테스트 스크립트

#!/bin/bash
# ab_test.sh — A/B/Canary/Blue-Green 분포 검증
set -euo pipefail

N=${1:-200}
URL=${FASTAPI_URL:-"https://fastapi.local"}
PAYLOAD='{"data": [[5.1, 3.5, 1.4, 0.2]]}'

: > ab_test_result.log
for i in $(seq 1 $N); do
  id="client_$i"
  variant=$(curl -sk "${URL}/predict" \
    -H "Content-Type: application/json" \
    -H "x-client-id: ${id}" \
    -d "${PAYLOAD}" | jq -r '.variant')
  echo "$id$variant" >> ab_test_result.log
done

count_B=$(grep -c "→ B" ab_test_result.log || true)
count_A=$((N - count_B))
ratio=$((count_B * 100 / N))
echo "결과: A=${count_A}, B=${count_B} (B=${ratio}%)"

검증 포인트: Canary 비율, A/B 90:10, Blue-Green 100% 전환이 실요청 분포로 확인돼야 합격.


6️⃣ 실험 시나리오 매핑 표

유형env 변수기대 결과
A/BALIAS_SELECTION_MODE=ab_testA/B ≈ 90:10
CanaryALIAS_SELECTION_MODE=canary, CANARY_PERCENT=30B ≈ 30%
Blue-GreenALIAS_SELECTION_MODE=blue_green, DEFAULT_ALIAS=BB = 100%

7️⃣ Slack 통합(끝단까지 가시화)

구간메시지 예시비고
CIHelm Lint Passed / Lint FailedGitHub Actions → Webhook
CDSync 시작 / Sync 완료7단계의 ArgoCD Notifications 재사용
TestA/B 분포 48:52 (N=200)스크립트/파이프라인에서 전송

8️⃣ 체크리스트

  • CI: Helm Lint + kubeconform + yamllint 모두 통과
  • AppSet: values/dev.yaml·prod.yaml 정확히 참조
  • ArgoCD: SelfHeal + Prune 활성화
  • FastAPI: /predict 분배 결과가 정책과 일치
  • Slack: CI/CD/Test 알림이 적시에 도착
  • Canary 비율/Blue-Green 전환 시 실유저 분포도 정상

🧩 팁

증상원인해결
CI 통과했는데 배포 실패CRD 스키마 미검증 영역-ignore-missing-schemas 유지, 클러스터에서 Health 체크
OutOfSync 반복외부에서 수동 수정Git 반영 후 SelfHeal로 동기화
Slack 누락Webhook Secret 누락/오타SealedSecret 키/네임스페이스 재확인
Canary 불균형해시 기준/헤더 누락x-client-id 고정, 샘플 수 충분히 확보

설계 판단 (Why This Way?)

env x chart 매트릭스 병렬 CI로 환경 간 독립 검증을 보장하고, kubeconform으로 K8s 스키마를 검증하되 CRD는 설치 시점에 맡깁니다. CI(차단 게이트)와 CD(상태 보장 엔진)의 역할을 명확히 분리했습니다.


다음에 읽을 글

MLOps 운영 고도화 9단계: 시크릿 관리 & 키 회전 자동화 — SealedSecret + AWS 키 로테이션