이 글에서 다루는 것
GitHub Actions로 Helm lint + kubeconform + yamllint 정적 검증을 자동화하고, ArgoCD GitOps 배포 + FastAPI alias 기반 트래픽 실험까지 하나의 커밋으로 이어지는 CI/CD 운영 자동화 구조를 다룹니다.
선수지식
- MLOps 운영 고도화 6단계: GitOps 고도화 — ArgoCD 동기화 전략 정교화
이 단계에서 해결하려는 문제
운영 환경에서 사고는 대부분 배포 전에 체크되지 못한 문제, 수동 배포의 실수, 배포 후 검증 부재에서 시작된다. 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️⃣ 전체 구조 요약

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/B | ALIAS_SELECTION_MODE=ab_test | A/B ≈ 90:10 |
| Canary | ALIAS_SELECTION_MODE=canary, CANARY_PERCENT=30 | B ≈ 30% |
| Blue-Green | ALIAS_SELECTION_MODE=blue_green, DEFAULT_ALIAS=B | B = 100% |
7️⃣ Slack 통합(끝단까지 가시화)
| 구간 | 메시지 예시 | 비고 |
|---|---|---|
| CI | Helm Lint Passed / Lint Failed | GitHub Actions → Webhook |
| CD | Sync 시작 / Sync 완료 | 7단계의 ArgoCD Notifications 재사용 |
| Test | A/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 키 로테이션