fix: security, data-integrity, and doc-drift findings from repo audit

Consolidates duplicate KIS API client implementations (governance tests
were exercising an unused class instead of the one actually running in
production), closes a SQL injection path in the DB admin page, fixes a
migration that used MySQL-only syntax and had never actually applied
(confirmed against production), resyncs docs/db/quantengine.dbml with
all migrations, and removes a duplicate OMS·WMS·ERP frontend tree in
favor of src/frontend/. Also corrects several unverifiable/inflated
claims in the OMS planning docs and realigns CI/CD and architecture
documentation with what's actually in the repo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 11:20:02 +09:00
parent 99943d9871
commit 70824c2afb
185 changed files with 2062 additions and 34521 deletions
+244
View File
@@ -0,0 +1,244 @@
> **ARCHIVED (2026-07-30)**: 2026-07-11 시점 build.yml/wbs_9_3_*.yml/merge-to-main.yml 등
> 이후 삭제된 워크플로우를 전제로 쓰였습니다. 현재 CI 구조는
> [`../CICD_PIPELINE.md`](../CICD_PIPELINE.md)를 참고하세요.
# QuantEngine CI/CD 파이프라인 — 근본적 개선 분석 및 로드맵
**작성일**: 2026-07-11
**분석 대상**: 522 workflow runs (모두 실패 또는 skipped)
**핵심 발견**: 원론적 아키텍처 결함, 중복 빌드, 불명확한 실패 원인
---
## 📊 현재 상태 분석
### 1. Workflow 구조의 문제
```
Current (병렬 & 독립적):
push → build.yml → GitHub Release 발행 → 🔴 실패
→ ci.yml → 30+ validators → 🔴 실패
→ deploy-prod.yml → 배포 → 🔴 실패
→ wbs_9_3_*.yml → 검증 → 🔴 실패
문제점:
- 세 workflow가 동시에 실행 (경합 위험)
- build.yml과 deploy-prod.yml이 각각 독립적으로 빌드
- 아티팩트 공유 메커니즘 없음
- GitHub Release action 사용 (Gitea에서 미지원)
- ci.yml의 30+ 단계 중 어느 것이 실패하는지 불명확
```
### 2. 실패 패턴 (최근 20개 run 분석)
```
build.yml: 18/20 실패 (90%)
ci.yml: 18/20 실패 (90%)
deploy-prod.yml: 18/20 실패 (90%)
wbs_9_3_*.yml: 5/5 실패 (100%)
validate-ui-*: 5/5 skipped (조건부 실행)
일관된 실패 = 시스템적 문제 (간헐적 flake 아님)
```
### 3. 주요 근본 원인
| 원인 | 영향 | 심각도 |
|------|------|--------|
| **빌드 중복** | CI runner 리소스 낭비, 시간 증가 | 🔴 High |
| **Workflow 의존성 부재** | 각 workflow가 독립적 → 아티팩트 비동기화 | 🔴 High |
| **30+ Python validators 순차 실행** | 하나 실패 시 전체 ci.yml 중단 → 원인 파악 어려움 | 🔴 High |
| **GitHub Release 사용** | Gitea에서 미지원 → build.yml 실패 | 🔴 High |
| **로그 분산** | 실패 원인 추적 어려움 | 🟠 Medium |
| **Secret 관리 부재** | QUANTENGINE_DB_PASSWORD 미설정 | 🟠 Medium |
---
## 🎯 원론적 개선 방향 (Principled Architecture)
### Phase 1: Pipeline 아키텍처 재설계 (필수)
**목표**: SSOT (Single Source of Truth) + 명확한 흐름
```
재설계 (순차 & 의존적):
push → stage: Validate (fast gates)
├─ Lint & Format Check
├─ Security Scan (KIS API governance)
└─ Spec Validation (YAML/JSON)
→ stage: Build (공유 아티팩트)
├─ dotnet build
├─ Unit tests
└─ Package creation
→ stage: Test (통합 테스트)
├─ Python validators (병렬, 독립적 재시도)
└─ E2E tests
→ stage: Deploy (조건부)
├─ Pre-deployment checks
├─ Green-Blue deployment
└─ Health check
효과:
- 빌드 1회만 → 시간 50% 단축
- 아티팩트 중앙화 → 동기화 문제 제거
- 각 stage 독립 실패 처리 → 원인 명확
- Validator 병렬 실행 가능 → 시간 개선
```
### Phase 2: Quality Gates 계층화
```
Tier 1: Fast Gates (< 2분, 모든 PR)
├─ YAML/JSON lint
├─ File size check
├─ Branch naming convention
└─ → 실패 시 즉시 피드백
Tier 2: Critical Gates (3-5분, 모든 PR)
├─ KIS API read-only enforcement
├─ No hardcoded secrets
├─ Security scanning
└─ → 실패 시 배포 차단
Tier 3: Integration Gates (10-15분, merge 시에만)
├─ 30+ Python validators (병렬 실행)
├─ Unit tests
└─ → 실패 시 skipped (로그만 저장)
효과:
- PR 속도 개선 (2분 내 피드백)
- 중요한 gate만 배포 차단
- Validators 실패 = 정보만 저장 (배포는 진행)
```
### Phase 3: Observability 강화
```
각 단계별 명확한 출력:
✅ Stage: Validate
└─ Lint: PASS
└─ Security: PASS
└─ Specs: PASS (3/3 files)
✅ Stage: Build
└─ Restore: PASS (1.2s)
└─ Build: PASS (45s)
└─ Tests: PASS (8/8)
└─ Package: quantengine-abc1234.tar.gz (2.6MB)
✅ Stage: Test
├─ validator-01-kis-governance: PASS
├─ validator-02-specs: PASS
├─ validator-03-formula: PASS
... (병렬 실행)
└─ Summary: 28/30 PASS, 2 SKIP (ok)
✅ Stage: Deploy
└─ Green-Blue: quantengine_20260711_ABC1234_523
└─ Health: OK (HTTP 200)
└─ Rollback: Available
효과:
- 각 단계 진행 상황 실시간 파악
- 실패 시 구체적인 단계 & 원인 명시
- Artifact 추적 가능
```
### Phase 4: Workflow 파일 구조화
```
새로운 파일 구조:
.gitea/workflows/
├─ _common/ # 공유 로직
│ ├─ build-artifact.yml # dotnet build & package
│ ├─ quick-gates.yml # Lint, 정적분석
│ ├─ deploy.yml # Green-Blue deployment
│ └─ notify.yml # Slack/Telegram 알림
├─ pr-validation.yml # PR 검증 (Fast gates 만)
├─ merge-to-main.yml # main 병합 (Critical + Integration)
├─ deploy-production.yml # 배포 (main 태그/release)
└─ scheduled/
├─ nightly-validators.yml # 야간 전체 검증
└─ cleanup-deployments.yml# 배포 정리
각 workflow 책임:
- pr-validation.yml: 2분 내 피드백 (Tier 1)
- merge-to-main.yml: 15분 내 완료 (Tier 1+2+3)
- deploy-production.yml: 10분 내 배포 (Tier 2+3+Deploy)
```
---
## 🛠 구체적 개선 작업 (다음 세션)
### 1단계: 빌드 파이프라인 통일 (1시간)
- [ ] `.gitea/workflows/_common/build-artifact.yml` 생성
- [ ] build.yml → `_common/build-artifact.yml` 참조로 변경
- [ ] deploy-prod.yml → `_common/build-artifact.yml` 참조로 변경
- [ ] 아티팩트 S3/Gitea Release storage로 중앙화
### 2단계: Validator 최적화 (2시간)
- [ ] ci.yml의 30+ validator를 3개 그룹으로 분류
- Group A: Tier 1 (빠른 gates)
- Group B: Tier 2 (중요 gates)
- Group C: Tier 3 (정보성)
- [ ] 각 그룹을 병렬 job으로 분리
- [ ] Validator 실패 시 `continue-on-error: true` 설정
### 3단계: Workflow 통합 (2시간)
- [ ] `pr-validation.yml` 생성 (Tier 1 only)
- [ ] `merge-to-main.yml` 생성 (Tier 1+2+3)
- [ ] `deploy-production.yml` 정리 (Tier 2+3+Deploy)
- [ ] 각 workflow의 outputs 명확히 (success/failure/artifact)
### 4단계: 모니터링 & 알림 (1시간)
- [ ] `.gitea/workflows/_common/notify.yml` 생성
- [ ] 각 stage 완료 후 알림
- [ ] 실패 시 상세 로그 링크 포함
### 5단계: 문서화 & 테스트 (1시간)
- [ ] README.md 업데이트 (workflow 흐름)
- [ ] 로컬에서 workflow 검증 가능한 스크립트
- [ ] CI/CD 트러블슈팅 가이드
---
## 🚀 기대 효과
| 지표 | 현재 | 개선 후 | 개선율 |
|------|------|--------|--------|
| 빌드 시간 | 3-4분 | 1-2분 | -60% |
| 전체 workflow 시간 | 10-15분 | 15-20분 (더 안정적) | +정확성 |
| 실패율 | 90% | <10% | -80% |
| 평균 실패 원인 파악 시간 | 30분 | 5분 | -83% |
| PR 피드백 시간 | 5분 (전체 CI 완료 후) | 2분 (Tier 1만) | -60% |
---
## 📋 최종 체크리스트
- [ ] 새 DB password 설정 (QUANTENGINE_DB_PASSWORD secret)
- [ ] GitHub Release action → Gitea-compatible 버전으로 변경
- [ ] Build artifact 저장소 선정 (S3 / Gitea Releases / 로컬)
- [ ] Validator 병렬화 방안 검토
- [ ] Notification 채널 구성 (Slack/Telegram/Gitea comment)
---
## 참고: 기존 대비 개선 원칙
| 원칙 | 현재 상태 | 개선 방향 |
|------|----------|---------|
| **Single Build** | ❌ 중복 빌드 (build.yml + deploy-prod.yml) | ✅ 공유 아티팩트 |
| **Clear Deps** | ❌ 의존성 없음 (병렬 실행) | ✅ 순차 & 조건부 |
| **Fast Feedback** | ❌ 15분 대기 | ✅ 2분 내 피드백 |
| **Fail Fast** | ❌ 30 validators 순차 | ✅ Validator 병렬 |
| **Observability** | ❌ 로그 분산 | ✅ 단계별 명확한 출력 |
| **Secret Security** | ⚠️ 환경변수만 | ✅ Gitea secret + fail-fast |
+189
View File
@@ -0,0 +1,189 @@
> **ARCHIVED (2026-07-30)**: 2026-07-11 시점 파이프라인 구조 기준입니다. 현재 모니터링
> 방법은 [`../DEPLOYMENT_RUNBOOK.md`](../DEPLOYMENT_RUNBOOK.md)의 "Deployment Monitoring" /
> "API Monitoring (CLI)" 절을 참고하세요.
# CI/CD Pipeline 모니터링 가이드
**작성일**: 2026-07-11
**대상**: QuantEngine CI/CD 파이프라인 모니터링
**상태**: Phase 5 완성
---
## 1. Workflow 실행 추적
### A. Gitea Actions Dashboard
- URL: https://gitea.taxbaik.com/kjh2064/QuantEngineByItz/actions
- **확인 항목**:
- 최근 5개 run 상태 (SUCCESS/FAILURE)
- 각 workflow별 실행 시간
- 어느 stage에서 실패했는지
### B. 주요 metrics
```
Pipeline Performance (최근 10 runs):
┌─────────────────────────────────────┐
│ Success Rate: 10/10 (100%) │
│ Avg Time: 18-20 minutes │
│ Failure Stages: None (목표) │
└─────────────────────────────────────┘
Stage Breakdown:
Stage 1 (Fast Gates): 1-2 min ✓
Stage 2 (Critical): 3-5 min ✓
Stage 3 (Integration): 10-15 min ✓ (병렬)
Stage 4 (Build): 5-8 min ✓
Stage 5 (Deploy): 2-3 min ✓
─────────────────────────────────────
TOTAL: 18-20 min
```
---
## 2. 실패 원인 분석
### Failure Hierarchy
```
Stage 1 실패 (Fast Gates)
├─ YAML 문법 오류 → .gitea/workflows/*.yml 검사
├─ Hardcoded Secrets → grep -r "Password=" 확인
└─ JSON 유효성 → JSON 파일 재검사
Stage 2 실패 (Critical Gates)
├─ KIS API Governance → tools/validate_no_direct_api_trading_v1.py
└─ DB Schema → tools/validate_postgresql_history_contract_v1.py
Stage 3 실패 (Integration)
├─ Spec Validation → tools/validate_specs.py
├─ Formula Registry → tools/validate_formula_registry.py
└─ Other validators → 개별 로그 확인
Stage 4 실패 (Build)
├─ Restore 실패 → NuGet 패키지 문제
├─ Build 실패 → 컴파일 오류
├─ Test 실패 → Unit test 오류
└─ Publish 실패 → 퍼블리시 구성 문제
Stage 5 실패 (Deploy)
├─ Secret 미설정 → QUANTENGINE_DB_PASSWORD 확인
└─ DB 연결 실패 → 원격 DB 상태 확인
```
---
## 3. 주요 체크리스트
### 매일 확인 (Daily)
- [ ] 최근 run 상태 확인 (SUCCESS/FAILURE)
- [ ] 만약 FAILURE → Stage 파악 → 원인 분석
### 주간 확인 (Weekly)
- [ ] 10 runs 평균 성공률 확인 (목표: >95%)
- [ ] Stage별 평균 실행 시간 확인
- [ ] 느려지는 추세 있는지 확인
### 월간 확인 (Monthly)
- [ ] 이번 달 총 run 수
- [ ] Stage별 실패율 추이
- [ ] 배포 성공 및 롤백 이력
- [ ] Performance 개선 여지 (타임아웃 조정)
---
## 4. 실시간 알림 설정 (선택사항)
### Slack/Telegram 연동 (Future)
```bash
# merge-to-main.yml의 Stage 5에 추가될 예정
- name: Notify Deployment Status
run: |
if [ "${{ needs.stage-4-build.result }}" = "success" ]; then
SLACK_MSG="✅ QuantEngine deployed successfully"
else
SLACK_MSG="❌ Deployment failed at $(Stage)"
fi
curl -X POST https://hooks.slack.com/... -d "{\"text\":\"$SLACK_MSG\"}"
```
---
## 5. 성능 개선 추적
### Target Metrics (목표)
| 지표 | 현재 | 목표 | 달성 |
|------|------|------|------|
| 전체 시간 | 18-20분 | <15분 | ⏳ |
| Stage 1 | 1-2분 | <1분 | ⏳ |
| Stage 3 | 10-15분 | 병렬화 | ⏳ |
| 성공률 | 90%→100% | >95% | ✅ |
| DB 연결 실패 | 0 | 0 | ✅ |
### 개선 로드맵
**Phase 5 확장 (이번 분기)**
- [ ] Validator 병렬 그룹화
- [ ] 빌드 캐싱 추가
- [ ] 단위 테스트 최적화
**Phase 6 (다음 분기)**
- [ ] E2E 테스트 추가
- [ ] 성능 프로파일링
- [ ] 배포 속도 분석
---
## 6. 트러블슈팅 Quick Reference
### 문제: Stage 1 계속 실패
**해결**: YAML 인코딩 확인
```bash
file .gitea/workflows/*.yml
# 모두 UTF-8 (또는 ASCII) 여야 함
# 한글/emoji는 포함되면 안 됨
```
### 문제: Stage 2 DB validation 실패
**해결**: Production password 확인
```bash
ssh kjh2064@178.104.200.7
PGPASSWORD="pvuIp8fWNj+oWfZtciw43GzJ4yU0vwKf" \
psql -h 127.0.0.1 -U quantengine_app -d quantenginedb -c "SELECT 1"
```
### 문제: Stage 4 Build 느려짐
**해결**: 캐시 무효화 여부 확인
```bash
dotnet clean src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
# 그 후 다시 build
```
---
## 7. Dashboard 요약 (매주 업데이트)
### 2026-07-11 ~ 2026-07-18
| Run # | Date | Status | Time | Note |
|-------|------|--------|------|------|
| 530 | 7-11 | FAIL | 3m | Tier 1 encoding 이슈 |
| 533 | 7-11 | FAIL | 5m | Tier 2 DB secret |
| 535 | 7-11 | PASS | 18m | Phase 5 첫 성공 |
**Trend**: ✅ Improving (실패율 감소)
---
## 참고 자료
- `.gitea/workflows/` - 모든 CI/CD workflow 정의
- `docs/CICD_ANALYSIS_AND_ROADMAP.md` - 아키텍처 및 로드맵
- `docs/CI_CD_IMPLEMENTATION_SUMMARY.md` - 이전 구현 요약
- `CLAUDE.md` - 프로젝트 기준 및 정책
@@ -0,0 +1,332 @@
> **ARCHIVED (2026-07-30)**: "로컬 Green-Blue 배포(SSH 제거)" 방식은 이후
> SSH 기반 release-artifact 배포(prepare-release.yml → deploy-prod.yml)로 대체되었습니다.
> 현재 배포 방식은 [`../DEPLOYMENT_RUNBOOK.md`](../DEPLOYMENT_RUNBOOK.md)를 참고하세요.
# QuantEngine CI/CD 파이프라인 구현 완료 보고서
**작성일**: 2026-07-11
**상태**: ✅ 완료 (Phase 1 + Phase 2 준비)
**커밋**: 538fc74 (자동화된 배포 테스트)
---
## 📋 Executive Summary
QuantEngine의 CI/CD 파이프라인을 **본질적으로 개선**했습니다.
- **문제**: SSH 원격 배포, 복잡한 구조, 롤백 전략 부재
- **해결**: 로컬 Green-Blue 배포, 자동 롤백, 사전 검증
- **결과**: 배포 시간 -20%, 신뢰성 ↑↑, 사람 개입 최소화
---
## 🎯 주요 개선사항
### 1️⃣ **로컬 배포 (SSH 제거)**
**이전**:
```
Gitea Actions (Runner)
→ SSH 키 설정
→ SSH 연결
→ SCP 파일 전송
→ SSH 배포 스크립트 호출
❌ 불필요한 오버헤드
```
**현재**:
```
Gitea Actions (로컬)
→ 직접 파일 시스템 접근
→ 직접 systemctl 실행
✅ 오버헤드 제거
```
**효과**:
- SSH 오버헤드 제거 (-1-2분)
- 네트워크 장애 영향 제거
- 코드 복잡도 감소 (-60줄)
---
### 2️⃣ **Green-Blue 배포 (taxbaik 패턴 적용)**
**특징**:
```
Phase 1: Green 버전 준비 (배포 중단 없음)
Phase 2: 마이그레이션 검증 (사전 차단)
Phase 3: Nginx 설정 검증 (오류 사전 차단)
Phase 4: 데이터베이스 준비 확인
Phase 5: 원자적 전환 (Blue → Green)
Phase 6: 서비스 재시작
Phase 7: 이전 버전 정리
```
**구현 파일**:
- `deploy_gb.sh` - Green-Blue 배포 자동화
- `scripts/validate_migrations.sh` - 마이그레이션 검증
- `.gitea/workflows/deploy-prod.yml` - 통합 워크플로우
**장점**:
- ✅ 무중단 배포 (링크 전환 시만 짧은 중단)
- ✅ 즉시 롤백 가능 (이전 Blue 유지)
- ✅ 배포 중 검증으로 실패 사전 차단
---
### 3️⃣ **자동화된 배포 검증 (사람 개입 없음)**
**스크립트**: `scripts/auto_deployment_test.sh`
```bash
./scripts/auto_deployment_test.sh
```
**자동 실행**:
1. SSH로 원격 서버 연결 (자동 인증)
2. Green-Blue 구조 검증
3. 서비스 헬스체크
4. Nginx 설정 검증
5. 결과 보고
**결과**:
```
✅ Test 1: Green-Blue 배포 구조 검증
✅ Test 2: 서비스 헬스체크
✅ Test 3: Nginx 설정 검증
```
---
### 4️⃣ **자동 롤백**
배포 중 헬스체크 실패 시:
```bash
# 이전 버전으로 즉시 복구
ln -sfn /previous/version /active
systemctl restart quantengine
# Telegram 자동 알림
send_telegram "❌ 배포 실패 (자동 롤백 실행)"
```
**효과**:
- 배포 실패 → 자동 복구 (1-2분)
- 이전 방식: 수동 대응 (15-30분)
---
### 5️⃣ **배포 이력 추적**
파일: `/home/kjh2064/.config/quantengine_deploy_history.log`
```
TIMESTAMP=20260711_181524
COMMIT=db19f0c
DEPLOY_PATH=/home/kjh2064/deployments/quantengine_20260711_181524
PREV_VERSION=quantengine_20260711_181342
STATUS=success
DEPLOYED_AT=2026-07-11T09:15:27Z
```
**용도**:
- 배포 이력 조회
- 빠른 롤백 결정
- 근본 원인 분석
---
## 📊 성능 비교
| 지표 | 이전 | 현재 | 개선 |
|------|------|------|------|
| 배포 시간 | 7-10분 | 5-8분 | -20% |
| SSH 오버헤드 | 1-2분 | 0 | 제거 |
| 무중단 배포 | ❌ | ✅ | 추가 |
| 즉시 롤백 | ❌ | ✅ | 추가 |
| 사전 검증 | ❌ | ✅ | 추가 |
| 자동 롤백 | ❌ | ✅ | 추가 |
| 배포 이력 | ❌ | ✅ | 추가 |
---
## 📁 구현 파일 목록
### 배포 자동화
- **`deploy_gb.sh`** - Green-Blue 배포 스크립트 (7단계)
- **`.gitea/workflows/deploy-prod.yml`** - CI/CD 워크플로우 (개선됨)
### 검증 스크립트
- **`scripts/validate_migrations.sh`** - 마이그레이션 사전 검증
- **`scripts/auto_deployment_test.sh`** - 자동화된 배포 검증
### 문서
- **`CICD_ROADMAP.md`** - 전체 로드맵 (Phase 1-3)
- **`docs/DEPLOYMENT_ARCHITECTURE.md`** - 배포 아키텍처 상세
- **`docs/CI_CD_IMPLEMENTATION_SUMMARY.md`** - 이 문서
---
## 🔄 배포 워크플로우 (현재)
```yaml
git push main
Gitea Actions 트리거
├─ [2-3분] 빌드
├─ [1-2분] 테스트
├─ [30초] 패킹
│ ├─ deploy_gb.sh 포함
│ └─ scripts/validate_migrations.sh 포함
├─ [30초] Pre-Deployment 검증
│ ├─ DB 연결 테스트
│ ├─ 마이그레이션 호환성
│ └─ 필수 테이블 확인
├─ [1분] Green-Blue 배포
│ ├─ Green 버전 준비
│ ├─ Nginx 검증
│ ├─ 링크 전환 (원자적)
│ └─ 서비스 재시작
├─ [15초] 헬스체크 (3회)
└─ [즉시] Telegram 알림
📊 총 시간: 5-8분
```
---
## ✅ 검증 결과 (2026-07-11 18:31)
```
Test 1: Green-Blue 배포 구조 검증
✓ Active (Blue): quantengine_20260711_181524
✓ Rollback: quantengine_20260711_181342
✓ 원자적 전환: 가능
Test 2: 서비스 헬스체크
✓ 서비스 상태: Running (PID 3944910)
✓ 로컬 헬스체크: HTTP 302
✓ 공개 라우트: HTTP 302/200
✓ 배포 이력: 기록됨 (2개)
Test 3: Nginx 설정 검증
✓ 설정 파일: /etc/nginx/sites-enabled/taxbaik-domains.conf
✓ Nginx 상태: Running (PID 3676240)
✓ Location 블록: 3개 존재
```
---
## 🚀 다음 단계 (Phase 2-3)
### Phase 2: 빌드/배포 분리 (예상 2시간)
- [ ] `build.yml` 워크플로우 활성화
- [ ] Gitea Releases로 아티팩트 발행
- [ ] 빌드 아티팩트 재사용으로 속도 ↑
### Phase 3: E2E 검증 강화 (예상 1시간)
- [ ] 로그인 기능 E2E 테스트
- [ ] API 응답 검증
- [ ] 데이터베이스 쿼리 테스트
---
## 📚 운영 가이드
### 배포 이력 조회
```bash
ssh kjh2064@178.104.200.7
tail -20 ~/.config/quantengine_deploy_history.log
```
### 현재 배포 버전 확인
```bash
ssh kjh2064@178.104.200.7
readlink -f /home/kjh2064/quantengine_active
```
### 자동화된 검증 실행
```bash
./scripts/auto_deployment_test.sh
```
### 수동 롤백 (긴급)
```bash
ssh kjh2064@178.104.200.7
ln -sfn /home/kjh2064/deployments/quantengine_[PREVIOUS_TIMESTAMP] \
/home/kjh2064/quantengine_active
sudo systemctl restart quantengine
```
---
## 💡 아키텍처 원칙
1. **신뢰성 (Reliability)**
- 자동 롤백으로 배포 실패 빠른 대응
- 사전 검증으로 실패 사전 차단
2. **속도 (Speed)**
- SSH 제거로 배포 시간 단축
- 로컬 배포로 네트워크 지연 제거
3. **관찰성 (Observability)**
- 배포 이력 중앙 기록
- 자동화된 검증으로 상태 파악 용이
4. **재현성 (Reproducibility)**
- 같은 커밋 → 같은 배포
- 배포 프로세스 자동화 (사람 개입 최소화)
---
## 📝 Git 커밋 이력
```
538fc74 ✅ 자동화된 배포 테스트 스크립트 (SSH 직접 호출)
db19f0c ✅ Green-Blue 배포 + 마이그레이션 검증 + Nginx 검증
0d8e3a6 ✅ 로컬 배포 재설계 (SSH 제거)
11460fc ✅ Phase 2 빌드 워크플로우 + 로드맵
96cc7fc ✅ 타임아웃 + 자동 롤백 + 헬스체크
```
---
## 🎓 배운 점 및 교훈
### 원칙적 접근의 중요성
- 단순 오류 수정이 아니라 아키텍처 개선
- SSH 제거 → 근본적인 복잡도 감소
- Green-Blue 도입 → 배포 신뢰성 향상
### 자동화의 가치
- SSH 자동 테스트 → 사람 개입 제거
- 배포 이력 → 빠른 의사결정
- 사전 검증 → 실패율 감소
### 오픈소스/패턴 재사용
- taxbaik의 Green-Blue 패턴 적용
- 이미 검증된 방식 → 빠른 구현 + 높은 신뢰도
---
## 🏁 결론
**QuantEngine의 CI/CD 파이프라인이 본질적으로 개선되었습니다.**
| 항목 | 상태 |
|------|------|
| 배포 안정성 | ⬆️⬆️ (자동 롤백) |
| 배포 속도 | ⬆️ (20% 단축) |
| 운영 효율성 | ⬆️⬆️ (사람 개입 제거) |
| 신뢰성 | ⬆️⬆️ (사전 검증) |
| 관찰성 | ⬆️⬆️ (배포 이력) |
**다음 단계**: Phase 2-3 구현 (빌드 분리, E2E 검증)
---
**작성자**: Claude Haiku 4.5
**최종 수정**: 2026-07-11
**상태**: ✅ Production Ready
+323
View File
@@ -0,0 +1,323 @@
> **ARCHIVED (2026-07-30)**: 이미 삭제된 `merge-to-main.yml`, 옛 `deploy_gb.sh` Green-Blue
> 스크립트를 전제로 쓰였습니다. 현재 트러블슈팅 가이드는
> [`../DEPLOYMENT_RUNBOOK.md`](../DEPLOYMENT_RUNBOOK.md)의 "Troubleshooting Deployment
> Failures" 절을 참고하세요.
# CI/CD 배포 트러블슈팅 가이드
**작성일**: 2026-07-11
**버전**: 1.0
**대상**: QuantEngine 배포 담당자
---
## 1. 배포 실패 진단
### 1.1 Pre-Deployment 실패
**증상**: 배포가 시작되지 않음
```
[ERR] ERROR: SSH key not found
[ERR] ERROR: Build artifact not found
[ERR] ERROR: DB password secret not configured
```
**해결방법**:
| 오류 | 원인 | 해결책 |
|------|------|--------|
| SSH key not found | Gitea Actions에서 SSH 키 미설정 | Gitea Settings > Repository Secrets에서 SSH_KEY 추가 |
| Build artifact missing | 이전 단계(Build) 실패 | merge-to-main.yml의 Stage 4 로그 확인 |
| DB password not configured | Gitea Secrets 미설정 | Gitea Settings > Repository Secrets에서 QUANTENGINE_DB_PASSWORD 추가 |
| Config files missing | deploy/ 디렉토리 미포함 | 소스 코드의 `deploy/` 폴더 확인 |
**빠른 확인**:
```bash
# 로컬에서 필수 파일 확인
ls -la ./deploy/
ls -la deploy_gb.sh
file quantengine.tar.gz # 파일 크기 1MB 이상 확인
```
---
### 1.2 배포 실패 (Extract Stage)
**증상**:
```
[ERR] FATAL: Failed to extract artifact
[ERR] tar: (standard input): gzip: stdin: unexpected end of file
```
**원인 분석**:
- 빌드 아티팩트 손상
- 부분 다운로드된 파일
- 압축 형식 오류
**해결책**:
1. **빌드 아티팩트 재생성**:
```bash
# 로컬에서 강제 재빌드
dotnet clean src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
dotnet build -c Release
```
2. **tar 파일 검증**:
```bash
# 정상 tar 파일인지 확인
tar -tzf quantengine.tar.gz | head -20
# 파일 크기 확인 (최소 1MB 이상)
ls -lh quantengine.tar.gz
```
3. **재배포 트리거**:
```bash
# 새 커밋 생성 또는 manual dispatch
git commit --allow-empty -m "rebuild: Force redeployment"
git push origin main
```
---
### 1.3 배포 실패 (Structure Normalization)
**증상**:
```
[ERR] FATAL: QuantEngine.Web.dll not found in deployment
```
**원인**:
- net10.0 구조 정규화 실패
- DLL 파일이 중첩된 폴더에 있음
**해결책**:
1. **배포 디렉토리 구조 확인**:
```bash
ls -lh /home/kjh2064/deployments/quantengine_*/
```
2. **수동 구조 정리** (긴급 복구):
```bash
# 가장 최근 배포 확인
LATEST=$(ls -dt /home/kjh2064/deployments/quantengine_* | head -1)
# net10.0 아래 파일들 이동
mv $LATEST/net10.0/* $LATEST/
rmdir $LATEST/net10.0
# 서비스 재시작
systemctl restart quantengine
```
---
### 1.4 헬스 체크 실패
**증상**:
```
[ERR] FAILED: Health check did not pass after 5 attempts
[ERR] Service not responding on http://127.0.0.1:5000/
```
**진단**:
```bash
# 1. 서비스 상태 확인
systemctl status quantengine.service
# 2. 포트 점유 확인
lsof -i :5000 || ss -tlnp | grep 5000
# 3. 서비스 로그 확인
journalctl -u quantengine.service -n 50
# 4. DB 연결 테스트
PGPASSWORD='pvuIp8fWNj+oWfZtciw43GzJ4yU0vwKf' \
psql -h 127.0.0.1 -U quantengine_app -d quantenginedb -c "SELECT 1;"
# 5. 포트 수동 테스트
curl -v http://127.0.0.1:5000/
```
**공통 해결책**:
| 증상 | 원인 | 해결책 |
|------|------|--------|
| Connection refused | 서비스 시작 안 됨 | `systemctl restart quantengine` |
| Address already in use | 이전 프로세스 남음 | `pkill -f "dotnet.*QuantEngine"` |
| Database error | DB 연결 실패 | appsettings.Production.json 비밀번호 확인 |
| Timeout | 느린 시작 | HEALTH_CHECK_RETRIES 증가 |
---
### 1.5 자동 롤백 실패
**증상**:
```
[ERR] CRITICAL: Rollback failed - previous deployment not found
```
**원인**:
- 이전 배포가 삭제됨
- 배포 디렉토리 정리로 인한 손실
**예방**:
```bash
# 배포 히스토리 확인
ls -ldt /home/kjh2064/deployments/quantengine_* | head -10
# 수동 롤백 (긴급)
PREV_DEPLOY="/home/kjh2064/deployments/quantengine_YYYYMMDD_HHMMSS"
ln -sfn $PREV_DEPLOY /home/kjh2064/quantengine_active
systemctl restart quantengine
```
---
## 2. 배포 수동 관리
### 2.1 수동 배포 트리거
```bash
# Gitea Actions에서 Manual Dispatch
# 또는 CI/CD에서 commit → main 푸시
git commit --allow-empty -m "deploy: Manual trigger"
git push origin main
```
### 2.2 현재 배포 상태 확인
```bash
# 활성 배포 확인
readlink /home/kjh2064/quantengine_active
# 배포 디렉토리 목록
ls -lht /home/kjh2064/deployments/quantengine_* | head -5
# 서비스 상태
systemctl status quantengine.service
# 최근 로그
journalctl -u quantengine.service -f
```
### 2.3 즉시 롤백
```bash
# 1. 이전 배포 선택
DEPLOYMENTS=$(ls -dt /home/kjh2064/deployments/quantengine_*)
PREV=$(echo "$DEPLOYMENTS" | head -2 | tail -1)
# 2. 롤백 실행
ln -sfn $PREV /home/kjh2064/quantengine_active
# 3. 서비스 재시작
systemctl restart quantengine
# 4. 확인
systemctl status quantengine.service
curl http://127.0.0.1:5000/
```
---
## 3. 성능 최적화
### 3.1 배포 시간 단축
```bash
# 배포 캐시 검증
du -sh /home/kjh2064/deployments/
# 오래된 배포 수동 정리 (유지: 3개)
ls -dt /home/kjh2064/deployments/quantengine_* | tail -n +4 | xargs rm -rf
```
### 3.2 헬스 체크 타임아웃 조정
`.gitea/workflows/deploy-prod.yml`에서:
```yaml
env:
HEALTH_CHECK_RETRIES: "5" # 재시도 횟수
HEALTH_CHECK_DELAY: "3" # 재시도 간격 (초)
```
---
## 4. 모니터링 & 알림
### 4.1 Telegram 알림 설정
```bash
# Gitea Settings > Repository Secrets에서 설정
TELEGRAM_BOT_TOKEN=<your_token>
TELEGRAM_CHAT_ID=<your_chat_id>
```
### 4.2 배포 로그 위치
```bash
# 최근 배포 로그
journalctl -u quantengine.service -n 100
# 배포 정보 확인
cat /home/kjh2064/deployments/quantengine_*/(.deployment_info)
```
---
## 5. 자주 묻는 질문 (FAQ)
**Q: 배포는 되었는데 변경사항이 반영되지 않음**
```bash
# 1. 캐시 확인
curl -H "Cache-Control: no-cache" https://quant.taxbaik.com/
# 2. 서비스 재시작
systemctl restart quantengine
# 3. 브라우저 캐시 삭제 후 재접속
```
**Q: "appsettings.Production.json not found" 오류**
```bash
# 파일이 자동 생성되므로 정상
# 만약 없다면:
cat > /home/kjh2064/quantengine_active/appsettings.Production.json << 'EOF'
{
"ConnectionStrings": {
"DefaultConnection": "Host=127.0.0.1;Database=quantenginedb;Username=quantengine_app;Password=<PASSWORD>;Search Path=quantengine;"
}
}
EOF
systemctl restart quantengine
```
**Q: 데이터베이스 연결이 계속 실패**
```bash
# 비밀번호 확인
grep "Password=" /home/kjh2064/quantengine_active/appsettings.Production.json
# DB 직접 테스트
PGPASSWORD='pvuIp8fWNj+oWfZtciw43GzJ4yU0vwKf' \
psql -h 127.0.0.1 -U quantengine_app -d quantenginedb -c "SELECT version();"
```
---
## 6. 연락처 & 지원
- **배포 담당**: kjh2064
- **긴급 롤백**: systemctl restart quantengine
- **로그 위치**: /var/log/journalctl, /home/kjh2064/deployments/*/logs/
- **모니터링**: https://quant.taxbaik.com/Admin/Monitoring
---
**마지막 업데이트**: 2026-07-11
**다음 업데이트 예정**: 버그 수정 후
+406
View File
@@ -0,0 +1,406 @@
> **ARCHIVED (2026-07-30)**: 테스트 섹션은 bUnit + MudBlazor 컴포넌트(`Dashboard.razor`,
> `mud-card-kpi` 등) 기준으로, MudBlazor/Blazor WASM이 Razor Pages로 대체된 2026-07-11
> Phase 1 이후 더 이상 유효하지 않습니다. 배포 섹션은
> [`../DEPLOYMENT_RUNBOOK.md`](../DEPLOYMENT_RUNBOOK.md)로 대체되었습니다.
# QuantEngine - Testing & Deployment Guide
**Status**: Phase 6 (Testing) & Phase 8 (Deployment) - Configuration & Documentation
---
## Phase 6: Testing & Optimization
### 6.1 Unit Testing (bUnit)
#### Setup
```bash
cd src/dotnet
dotnet add package bunit
dotnet add package bunit.web
```
#### Example Test: Dashboard Component
```csharp
// Tests/Pages/DashboardTests.cs
[TestFixture]
public class DashboardTests
{
[Test]
public void Dashboard_Renders_KPICards()
{
// Arrange
var cut = new TestContext().RenderComponent<Dashboard>();
// Act & Assert
var kpiCards = cut.FindAll(".mud-card-kpi");
kpiCards.Count.Should().Be(4);
}
[Test]
public async Task Dashboard_LoadsAssets_OnInitialize()
{
// Arrange
var httpClient = new HttpClientStub();
var cut = new TestContext();
cut.Services.AddScoped(sp => httpClient);
var dashboard = cut.RenderComponent<Dashboard>();
// Act
await Task.Delay(100); // Wait for async init
// Assert
httpClient.Requests.Should().Contain(r => r.Url.Contains("/api/portfolio"));
}
}
```
#### Test Coverage Targets
- Dashboard rendering (4 KPI cards)
- Users list (search, filter, pagination)
- Portfolio components (asset table, categories)
- Form fields (all input types)
- Dialogs (confirm/cancel actions)
#### Run Tests
```bash
dotnet test src/dotnet/QuantEngine.Web.Client.Tests
dotnet test src/dotnet/QuantEngine.Web.Tests
```
### 6.2 Integration Tests
#### Database Test Setup
```csharp
[TestFixture]
public class RepositoryIntegrationTests
{
private IDbConnectionFactory _connectionFactory;
private ICollectionRepository _repository;
[OneTimeSetUp]
public void OneTimeSetUp()
{
_connectionFactory = new DbConnectionFactory(
"Host=localhost;Database=quantengine_test;..."
);
}
[Test]
public async Task SaveCollectionRun_Persists_ToDatabase()
{
// Arrange
var run = new CollectionRun { RunId = Guid.NewGuid().ToString(), ... };
// Act
await _repository.SaveRunAsync(run);
// Assert
var retrieved = await _repository.GetRunAsync(run.RunId);
retrieved.Should().NotBeNull();
retrieved.RunId.Should().Be(run.RunId);
}
}
```
### 6.3 Performance Optimization
#### Bundle Size Optimization
```bash
# Check bundle sizes
dotnet publish -c Release --output ./publish
du -sh publish/wwwroot/_framework/*
```
**Targets**:
- dotnet.wasm: < 2MB
- app.js: < 500KB
- Total: < 5MB
#### Loading Time Optimization
```csharp
// Use lazy loading for pages
[lazy: Dashboard]
@rendermode InteractiveWebAssembly
// Pre-load critical resources
<link rel="prefetch" href="/_framework/QuantEngine.Web.Client.wasm" />
```
### 6.4 Accessibility Testing (WCAG 2.1 AA)
#### Automated Checks
```bash
dotnet add package Deque.AxeCore.Selenium
```
#### Manual Checklist
- [ ] Keyboard navigation (Tab, Enter, Escape)
- [ ] Screen reader support (NVDA, JAWS)
- [ ] Color contrast (4.5:1 for text)
- [ ] Form labels properly associated
- [ ] Error messages clear and descriptive
- [ ] Focus indicators visible
- [ ] No automatic content changes
---
## Phase 8: Deployment & Operations
### 8.1 Production Build
#### Release Build Configuration
```bash
# Build Release configuration
cd src/dotnet
dotnet build -c Release
# Publish for deployment
dotnet publish -c Release -o ./publish/quantengine
# Size check
ls -lh publish/quantengine/
```
#### Build Output
- `publish/quantengine/` - Complete deployment package
- `publish/quantengine/wwwroot/` - Static assets
- `publish/quantengine/QuantEngine.Web.exe` - Server executable
- `publish/quantengine/appsettings.production.json` - Configuration
### 8.2 Docker Deployment
#### Dockerfile
```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app
EXPOSE 80 443
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj", "QuantEngine.Web/"]
RUN dotnet restore "QuantEngine.Web/QuantEngine.Web.csproj"
COPY src/dotnet/ .
RUN dotnet build "QuantEngine.Web/QuantEngine.Web.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "QuantEngine.Web/QuantEngine.Web.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "QuantEngine.Web.dll"]
```
#### Docker Build & Run
```bash
# Build image
docker build -t quantengine:latest .
# Run container
docker run -d \
-p 5265:80 \
-e ConnectionStrings__DefaultConnection="Host=db;Database=quantenginedb;..." \
-e ASPNETCORE_ENVIRONMENT=Production \
quantengine:latest
# Check logs
docker logs -f <container_id>
```
### 8.3 Nginx Reverse Proxy
#### Nginx Configuration
```nginx
upstream quantengine {
server 127.0.0.1:5000;
server 127.0.0.1:5001;
}
server {
listen 80;
server_name quantengine.example.com;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name quantengine.example.com;
ssl_certificate /etc/ssl/certs/cert.pem;
ssl_certificate_key /etc/ssl/private/key.pem;
location / {
proxy_pass http://quantengine;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location ~* \.(js|css|wasm|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
```
### 8.4 Environment Configuration
#### appsettings.production.json
```json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"System": "Warning",
"Microsoft": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Host=prod-db-host;Database=quantenginedb;Username=quantengine_app;Password=***;SslMode=Require;",
"HangfireConnection": "Host=prod-db-host;Database=quantengine_hangfire;..."
},
"AdminSettings": {
"Username": "admin",
"Password": "***"
},
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:5000"
}
}
}
}
```
### 8.5 Deployment Checklist
#### Pre-Deployment
- [ ] All tests pass (`dotnet test`)
- [ ] Code reviewed and approved
- [ ] Security vulnerabilities scanned (`dotnet package-search`)
- [ ] Database migrations tested
- [ ] Hangfire schedules configured
- [ ] Secrets properly managed (not in code)
- [ ] Environment variables documented
#### Deployment Steps
```bash
# 1. Create backup
pg_dump -h prod-db-host -U quantengine_app quantenginedb > backup-$(date +%Y%m%d).sql
# 2. Deploy application
docker pull quantengine:latest
docker stop quantengine
docker run -d --name quantengine -p 5000:80 quantengine:latest
# 3. Health check
curl https://quantengine.example.com/health
# 4. Monitor logs
docker logs -f quantengine
# 5. Verify features
- [ ] Login works
- [ ] Dashboard loads
- [ ] Data collection runs
- [ ] Hangfire jobs scheduled
```
#### Post-Deployment
- [ ] Monitor error logs (Serilog, Telegram alerts)
- [ ] Check Hangfire dashboard
- [ ] Verify scheduled jobs running
- [ ] Monitor database performance
- [ ] Check API response times (< 200ms)
### 8.6 Monitoring & Observability
#### Health Checks
```csharp
app.MapHealthChecks("/health", new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = WriteResponse
});
// Add health checks
builder.Services.AddHealthChecks()
.AddDbContextCheck<QuantEngineDbContext>()
.AddCheck("Database", () => HealthCheckResult.Healthy())
.AddCheck("KIS API", () => CheckKisApiAsync());
```
#### Logging (Serilog)
```csharp
Log.Information("Collection run completed: {RunId}, {Count} items", runId, itemCount);
Log.Warning("API rate limit warning: {Remaining}", remaining);
Log.Error(ex, "Collection failed: {RunId}", runId);
```
#### Monitoring Metrics
- Request rate (requests/sec)
- Error rate (errors/requests)
- Database query time (p50, p95, p99)
- Hangfire job success rate
- API response time by endpoint
### 8.7 Rollback Plan
#### If Deployment Fails
```bash
# 1. Stop current deployment
docker stop quantengine
# 2. Restore previous version
docker run -d --name quantengine -p 5000:80 quantengine:v1.0.0
# 3. Restore database from backup
psql -h prod-db-host -U quantengine_app -d quantenginedb < backup-20260705.sql
# 4. Verify health
curl https://quantengine.example.com/health
```
---
## Deployment Timeline
| Milestone | Target Date | Status |
|-----------|-------------|--------|
| Phase 6: Tests | 2026-07-06 | 📋 |
| Phase 7: Hangfire | 2026-07-05 | ✅ |
| Phase 8: Deploy | 2026-07-07 | 📋 |
| Production Release | 2026-07-10 | 📅 |
---
## Success Criteria
**Phase 6**:
- [ ] 80%+ test coverage
- [ ] All component tests passing
- [ ] WCAG AA compliance verified
- [ ] Bundle size < 5MB
**Phase 8**:
- [ ] Docker image builds successfully
- [ ] Production config validated
- [ ] Database backups automated
- [ ] Rollback plan documented
- [ ] Monitoring alerts configured
- [ ] 99.5% uptime target established
---
**Next**: Execute deployment pipeline and monitor production metrics.