# 인프라 주간 모니터링 보고서 시스템 - 기술 리서치 ## 1. 프로젝트 개요 **목표**: Prometheus API와 Kubernetes API에서 인프라 메트릭 데이터를 수집하여 주간 모니터링 보고서를 자동 생성하는 시스템 **배포 환경**: Kubernetes 클러스터 내 Pod으로 배포 (namespace: `soo`) **핵심 모니터링 메트릭**: - CPU 사용률 - Memory 사용률 - Disk 사용률 - NAS (NFS/CIFS) 스토리지 - Network Traffic (송/수신) **핵심 분석 항목**: - 평균(avg) / 최대(max) / 최소(min) 값 - Peak 구간 감지 (이상 탐지) - 주의가 필요한 데이터 구간 하이라이트 --- ## 2. 데이터 소스 분석 ### 2.1 Prometheus HTTP API Prometheus는 REST API를 통해 메트릭 데이터를 조회할 수 있다. #### 주요 엔드포인트 | 엔드포인트 | 용도 | 설명 | |---|---|---| | `GET /api/v1/query` | 즉시 쿼리 | 현재 시점의 메트릭 조회 | | `GET /api/v1/query_range` | 범위 쿼리 | **주간 보고서의 핵심** - 시작/종료 시간 + step 간격으로 시계열 데이터 조회 | | `GET /api/v1/labels` | 레이블 목록 | 사용 가능한 메트릭 레이블 조회 | | `GET /api/v1/label//values` | 레이블 값 | 특정 레이블의 값 조회 | | `GET /api/v1/metadata` | 메트릭 메타데이터 | 메트릭 설명 및 타입 정보 | #### query_range API 호출 예시 ``` GET /api/v1/query_range?query=&start=&end=&step= ``` - **최대 조회 범위**: 32일 (주간 보고서에 충분) - **step**: 데이터 해상도 (예: `5m`, `15m`, `1h`) - 주간 보고서용 권장 step: `5m` ~ `15m` (세밀한 peak 감지를 위해) #### 핵심 PromQL 쿼리 **CPU 사용률 (%):** ```promql # 노드별 CPU 사용률 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) # 또는 sum by (instance) (rate(node_cpu_seconds_total{mode!="idle"}[5m])) * 100 ``` **Memory 사용률 (%):** ```promql # 노드별 메모리 사용률 (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 ``` **Disk 사용률 (%):** ```promql # 파일시스템별 디스크 사용률 (1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) * 100 ``` **NAS/NFS 모니터링:** ```promql # NFS 마운트 디스크 사용률 (1 - node_filesystem_avail_bytes{fstype="nfs"} / node_filesystem_size_bytes{fstype="nfs"}) * 100 # NFS 마운트 디스크 사용률 (nfs4 포함) (1 - node_filesystem_avail_bytes{fstype=~"nfs|nfs4|cifs"} / node_filesystem_size_bytes{fstype=~"nfs|nfs4|cifs"}) * 100 ``` > **참고**: NAS 모니터링은 node_exporter의 filesystem collector가 NFS/CIFS 마운트도 수집함. Synology 등 전용 NAS는 SNMP exporter(`snmp_exporter`)를 통해 더 상세한 메트릭 수집 가능. **Network Traffic (bytes/sec):** ```promql # 수신 트래픽 (bytes/sec) sum by (instance) (rate(node_network_receive_bytes_total{device!~"lo|veth.*|docker.*|br-.*"}[5m])) # 송신 트래픽 (bytes/sec) sum by (instance) (rate(node_network_transmit_bytes_total{device!~"lo|veth.*|docker.*|br-.*"}[5m])) # bps 단위로 변환 sum by (instance) (rate(node_network_receive_bytes_total{device!~"lo|veth.*|docker.*|br-.*"}[5m])) * 8 ``` #### 통계 분석용 PromQL (avg / max / peak 감지) **평균 (avg_over_time):** ```promql # 주간 평균 CPU 사용률 avg_over_time( (100 - avg by (instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)[7d:5m] ) ``` **최대 (max_over_time):** ```promql # 주간 최대 CPU 사용률 max_over_time( (100 - avg by (instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)[7d:5m] ) ``` **Peak/이상 탐지 (Z-Score 기반):** ```promql # Z-Score = (현재값 - 평균) / 표준편차 # |Z| > 2 이면 이상 구간으로 판단 ( rate(node_cpu_seconds_total{mode!="idle"}[5m]) - avg_over_time(rate(node_cpu_seconds_total{mode!="idle"}[5m])[1d]) ) / stddev_over_time(rate(node_cpu_seconds_total{mode!="idle"}[5m])[1d]) ``` **표준편차 기반 이상 탐지:** ```promql # 평균 + 2*표준편차를 초과하는 구간 감지 > (avg_over_time([7d:5m]) + 2 * stddev_over_time([7d:5m])) ``` --- ### 2.2 Kubernetes API #### 노드 정보 조회 | 엔드포인트 | 용도 | |---|---| | `GET /api/v1/nodes` | 전체 노드 목록 + 스펙(CPU, Memory, OS 등) | | `GET /api/v1/nodes/` | 특정 노드 상세 정보 | | `GET /apis/metrics.k8s.io/v1beta1/nodes` | 노드별 실시간 리소스 사용량 (metrics-server 필요) | | `GET /apis/metrics.k8s.io/v1beta1/nodes/` | 특정 노드 실시간 사용량 | #### 노드 정보에서 얻을 수 있는 데이터 ```json { "metadata": { "name": "node-1", "labels": { "role": "worker" } }, "status": { "capacity": { "cpu": "16", "memory": "65536Mi" }, "allocatable": { "cpu": "15800m", "memory": "63000Mi" }, "nodeInfo": { "osImage": "Ubuntu 22.04", "kubeletVersion": "v1.28.0", "containerRuntimeVersion": "containerd://1.7.0" }, "conditions": [ { "type": "Ready", "status": "True" }, { "type": "MemoryPressure", "status": "False" }, { "type": "DiskPressure", "status": "False" } ] } } ``` #### 인증 방식 (Pod 내부 배포 기준) Pod으로 배포되므로 **In-Cluster Config**를 사용한다. K8s가 자동으로 ServiceAccount 토큰을 Pod에 마운트해준다. ``` /var/run/secrets/kubernetes.io/serviceaccount/token ← Bearer Token /var/run/secrets/kubernetes.io/serviceaccount/ca.crt ← CA 인증서 KUBERNETES_SERVICE_HOST / KUBERNETES_SERVICE_PORT ← API 서버 주소 (자동 주입) ``` `@kubernetes/client-node` 라이브러리 사용 시: ```typescript import * as k8s from '@kubernetes/client-node'; const kc = new k8s.KubeConfig(); kc.loadFromCluster(); // ← in-cluster config 자동 로드 const coreApi = kc.makeApiClient(k8s.CoreV1Api); const nodes = await coreApi.listNode(); ``` > **참고**: 외부 개발 시에는 `kc.loadFromDefault()`로 로컬 kubeconfig 사용 가능 --- ## 3. 아키텍처 (K8s Pod 배포) ### 3.1 배포 아키텍처 K8s Pod으로 배포되므로 클러스터 내부 통신을 활용한 **Next.js 풀스택 (BFF 패턴)** 단일 구성이 최적이다. ``` ┌─── K8s Cluster ──────────────────────────────────────────────┐ │ │ │ ┌─── Pod: infra-report (ns: soo) ────────────────────────┐ │ │ │ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ │ │ Next.js (App Router) │ │ │ │ │ │ ┌───────────┐ ┌────────────┐ │ │ │ │ │ │ │ Frontend │ │ API Routes │──┼──→ Prometheus │ │ │ │ │ │ (React) │ │ (BFF) │──┼──→ K8s API Server │ │ │ │ │ └───────────┘ └────────────┘ │ │ │ │ │ └─────────────────────────────────┘ │ │ │ │ ServiceAccount: infra-report-sa │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌── Service ──┐ │ │ │ ClusterIP │ ← Ingress / NodePort로 외부 접근 │ │ │ :3000 │ │ │ └─────────────┘ │ │ │ │ Prometheus: http://prometheus-server.monitoring │ │ K8s API: https://kubernetes.default.svc (in-cluster) │ └──────────────────────────────────────────────────────────────┘ ``` ### 3.2 K8s Pod 배포의 이점 | 이점 | 설명 | |---|---| | **CORS 문제 없음** | API Routes가 서버 사이드에서 Prometheus 호출 (Pod 간 통신) | | **인증 자동 처리** | ServiceAccount 토큰이 Pod에 자동 마운트 → K8s API 인증 | | **Prometheus 내부 접근** | `prometheus-server.monitoring`으로 클러스터 내부 DNS 호출 | | **설정 관리** | ConfigMap/Secret으로 환경변수 주입 | | **헬스체크** | K8s liveness/readiness probe 활용 | | **자동 복구** | Pod 장애 시 자동 재시작 | | **수평 확장** | 필요시 replica 증가 가능 (보고서 용도라 보통 1개면 충분) | ### 3.3 프론트엔드 Only 가능 여부 K8s Pod으로 배포하는 이상 **BFF 패턴이 자연스러운 선택**이다. Next.js 자체가 서버 런타임을 포함하므로 "프론트만 구현"해도 API Routes로 백엔드 로직을 가진 풀스택 앱이 된다. 즉, **프론트엔드 코드만 작성해도 사실상 백엔드가 포함된 구조**이므로 프론트 개발자 혼자서 충분히 구현 가능하다. --- ## 4. 기술 스택 상세 ### 4.1 프론트엔드 | 카테고리 | 추천 기술 | 대안 | 선택 이유 | |---|---|---|---| | **프레임워크** | **Next.js (App Router)** | Vite + React | API Routes로 BFF 패턴, standalone 빌드로 K8s 최적화 | | **언어** | **TypeScript** | JavaScript | 타입 안정성, API 응답 타입 정의 | | **차트 라이브러리** | **Apache ECharts** (`echarts-for-react`) | Recharts, Chart.js | 대량 데이터 처리 성능 우수, 다양한 차트 타입, 인터랙티브 기능 | | **UI 프레임워크** | **Tailwind CSS + shadcn/ui** | Ant Design, MUI | 경량, 커스터마이징 용이, 보고서 레이아웃에 적합 | | **테이블** | **TanStack Table** | AG Grid | 정렬/필터링, 경량 | | **상태관리** | **TanStack Query (React Query)** | SWR, Zustand | API 데이터 캐싱/재조회 최적화 | | **날짜 처리** | **date-fns** 또는 **dayjs** | moment.js (deprecated) | 경량, 트리쉐이킹 지원 | | **PDF 생성** | **html2canvas + jsPDF** (클라이언트) 또는 **Puppeteer** (서버) | react-pdf | 보고서 레이아웃 그대로 PDF 변환 | #### 차트 라이브러리 비교 (보고서 용도 관점) | 기준 | ECharts | Recharts | Chart.js | |---|---|---|---| | **대량 데이터 (1만+ 포인트)** | WebGL 렌더링으로 최고 | SVG 기반, 느려짐 | Canvas 기반, 양호 | | **시계열 차트** | 내장 dataZoom, 브러시 선택 | 기본적 | 줌 플러그인 필요 | | **보고서 스타일** | 풍부한 옵션 | 심플 | 심플 | | **툴팁/인터랙션** | 매우 풍부 | 기본적 | 기본적 | | **번들 사이즈** | ~1MB (트리쉐이킹 가능) | ~200KB | ~60KB | | **React 통합** | echarts-for-react 래퍼 | 네이티브 React | react-chartjs-2 래퍼 | > **권장**: 인프라 모니터링 보고서는 데이터 포인트가 많고 시계열 분석이 핵심이므로 **Apache ECharts** 추천. 데이터가 적고 단순한 보고서라면 **Recharts**도 충분. ### 4.2 백엔드 (경량) | 카테고리 | 추천 기술 | 대안 | 선택 이유 | |---|---|---|---| | **런타임** | **Node.js** | Python (FastAPI) | 프론트와 언어 통일, Next.js API Routes 활용 가능 | | **HTTP 클라이언트** | **axios** 또는 **fetch** (Node 18+) | got, node-fetch | Prometheus/K8s API 호출 | | **K8s 클라이언트** | **@kubernetes/client-node** | kubectl exec | 공식 JS 클라이언트, in-cluster config 지원 | | **스케줄링** | **node-cron** 또는 **K8s CronJob** | Bull Queue | Pod 내부 cron 또는 K8s 네이티브 CronJob | | **PDF (서버)** | **Puppeteer** | Playwright | 헤드리스 브라우저로 pixel-perfect PDF | ### 4.3 Python 백엔드 대안 (데이터 분석 강점) | 카테고리 | 기술 | 장점 | |---|---|---| | **프레임워크** | FastAPI | 비동기, 자동 API 문서, 타입 힌트 | | **Prometheus 클라이언트** | `prometheus-api-client` | PromQL 결과를 Pandas DataFrame으로 변환 | | **K8s 클라이언트** | `kubernetes` (공식) | 완성도 높은 공식 클라이언트 | | **데이터 분석** | Pandas, NumPy | 통계 분석(평균/최대/표준편차/이상탐지) 강력 | | **PDF 생성** | WeasyPrint, ReportLab | HTML → PDF 변환 | > **참고**: 데이터 분석/통계에 중점을 둔다면 Python 백엔드가 더 강력하지만, 프론트엔드와의 통합성과 단일 프로젝트 관리를 고려하면 Next.js + API Routes가 더 실용적. --- ## 5. 보고서 데이터 가공 로직 ### 5.1 데이터 수집 흐름 ``` 1. 보고 기간 설정 (예: 지난 7일) ├─ start: 2026-03-05T00:00:00Z └─ end: 2026-03-12T00:00:00Z 2. Prometheus query_range API 호출 ├─ CPU 사용률 쿼리 (step=5m → 2,016 데이터포인트/노드) ├─ Memory 사용률 쿼리 ├─ Disk 사용률 쿼리 ├─ NAS 사용률 쿼리 └─ Network 트래픽 쿼리 3. K8s API 호출 ├─ 노드 목록 + 스펙 (CPU cores, RAM 총량) └─ 노드 상태 (conditions) 4. 데이터 가공 ├─ 노드별 통계 계산 (avg, max, min, p95, p99) ├─ Peak 구간 감지 (Z-Score 기반) ├─ 임계값 초과 구간 마킹 └─ 트렌드 분석 (전주 대비 증감) 5. 보고서 렌더링 ├─ 요약 대시보드 (핵심 지표 카드) ├─ 시계열 차트 (peak 구간 하이라이트) ├─ 노드별 상세 테이블 └─ PDF 내보내기 ``` ### 5.2 Peak 감지 알고리즘 #### 방법 1: PromQL 서버 사이드 (Prometheus에서 계산) ```promql # avg_over_time + stddev_over_time 조합 # 평균 + 2σ 초과 시 peak으로 판정 # CPU peak 감지 (100 - avg by (instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > ( avg_over_time((100 - avg by (instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)[7d:5m]) + 2 * stddev_over_time((100 - avg by (instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)[7d:5m]) ) ``` #### 방법 2: 클라이언트/백엔드 사이드 (JavaScript/Python) ```javascript // Z-Score 기반 peak 감지 function detectPeaks(dataPoints, threshold = 2) { const values = dataPoints.map(d => d.value); const mean = values.reduce((a, b) => a + b) / values.length; const stddev = Math.sqrt( values.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / values.length ); return dataPoints.map(d => ({ ...d, zScore: (d.value - mean) / stddev, isPeak: Math.abs((d.value - mean) / stddev) > threshold, })); } ``` ### 5.3 보고서 구성 요소 ``` ┌─────────────────────────────────────────────────┐ │ 인프라 주간 모니터링 보고서 │ │ 2026.03.05 ~ 2026.03.12 │ ├─────────────────────────────────────────────────┤ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ CPU │ │ MEM │ │ DISK │ │ NET │ ← 요약 │ │ │ 평균 │ │ 평균 │ │ 평균 │ │ 평균 │ 카드 │ │ │ 최대 │ │ 최대 │ │ 최대 │ │ 최대 │ │ │ │ 최소 │ │ 최소 │ │ 최소 │ │ │ │ │ └──────┘ └──────┘ └──────┘ └──────┘ │ ├─────────────────────────────────────────────────┤ │ [CPU 사용률 시계열 차트] │ │ ████████░░░░████████████████░░░░████████ │ │ ↑ peak 구간 하이라이트 │ ├─────────────────────────────────────────────────┤ │ [Memory 사용률 시계열 차트] │ ├─────────────────────────────────────────────────┤ │ [Disk/NAS 사용률 차트] │ ├─────────────────────────────────────────────────┤ │ [Network Traffic 차트 (In/Out)] │ ├─────────────────────────────────────────────────┤ │ 노드별 상세 │ │ ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐ │ │ │ 노드 │ CPU │ CPU │ CPU │ MEM │ MEM │ MEM │ DISK │ │ │ │ │ avg │ max │ min │ avg │ max │ min │ max │ │ │ ├──────┼──────┼──────┼──────┼──────┼──────┼──────┼──────┼──────┤ │ │ │node-1│ 45% │ 92% │ 12% │ 67% │ 85% │ 41% │ 72% │ │ │ │node-2│ 38% │ 78% │ 8% │ 55% │ 70% │ 35% │ 45% │ │ │ └──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘ │ ├─────────────────────────────────────────────────┤ │ ⚠ 주의 구간 │ │ - node-1: 03/07 14:00~16:30 CPU 92% 도달 │ │ - node-2: 03/09 02:00~03:00 Memory 85% 초과 │ │ - NAS-01: 디스크 사용률 85% 초과 (증설 검토 필요) │ └─────────────────────────────────────────────────┘ ``` --- ## 6. 기술 스택 최종 추천 ### 6.1 추천 구성: Next.js 풀스택 + K8s 배포 ``` infra-report/ ├── src/ │ ├── app/ # Next.js App Router │ │ ├── page.tsx # 메인 보고서 페이지 │ │ ├── api/ # API Routes (BFF) │ │ │ ├── metrics/ # Prometheus 데이터 프록시 + 가공 │ │ │ │ ├── cpu/route.ts │ │ │ │ ├── memory/route.ts │ │ │ │ ├── disk/route.ts │ │ │ │ ├── nas/route.ts │ │ │ │ └── network/route.ts │ │ │ ├── nodes/route.ts # K8s 노드 정보 │ │ │ └── report/route.ts # 보고서 데이터 통합 │ │ └── report/ │ │ └── [id]/page.tsx # 개별 보고서 상세 │ ├── components/ │ │ ├── charts/ # 차트 컴포넌트 │ │ │ ├── CpuChart.tsx │ │ │ ├── MemoryChart.tsx │ │ │ ├── DiskChart.tsx │ │ │ ├── NetworkChart.tsx │ │ │ └── PeakHighlight.tsx │ │ ├── report/ # 보고서 레이아웃 컴포넌트 │ │ │ ├── SummaryCards.tsx │ │ │ ├── NodeTable.tsx │ │ │ └── AlertSection.tsx │ │ └── ui/ # 공통 UI 컴포넌트 │ ├── lib/ │ │ ├── prometheus.ts # Prometheus API 클라이언트 │ │ ├── kubernetes.ts # K8s API 클라이언트 (in-cluster config) │ │ ├── analytics.ts # 통계 계산 (avg, max, peak 감지) │ │ └── formatters.ts # 데이터 포맷팅 (bytes→GB 등) │ └── types/ │ ├── metrics.ts # 메트릭 데이터 타입 │ └── report.ts # 보고서 데이터 타입 ├── k8s/ # K8s 매니페스트 │ ├── deployment.yaml │ ├── service.yaml │ ├── ingress.yaml │ ├── serviceaccount.yaml │ ├── rbac.yaml # ClusterRole + ClusterRoleBinding │ └── configmap.yaml ├── Dockerfile ├── .dockerignore ├── package.json ├── tailwind.config.ts ├── tsconfig.json └── .env.local # 로컬 개발용 (Pod에서는 ConfigMap/Secret 사용) ``` ### 6.2 핵심 패키지 목록 ```json { "dependencies": { "next": "^14.x", "react": "^18.x", "typescript": "^5.x", "echarts": "^5.x", "echarts-for-react": "^3.x", "@tanstack/react-query": "^5.x", "@tanstack/react-table": "^8.x", "tailwindcss": "^3.x", "@radix-ui/react-*": "latest", "axios": "^1.x", "@kubernetes/client-node": "^0.20.x", "date-fns": "^3.x", "jspdf": "^2.x", "html2canvas": "^1.x", "zod": "^3.x" } } ``` --- ## 7. K8s 배포 상세 ### 7.1 Dockerfile ```dockerfile # ── Build Stage ── FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # ── Production Stage ── FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT=3000 ENV HOSTNAME="0.0.0.0" CMD ["node", "server.js"] ``` > `next.config.js`에 `output: 'standalone'` 설정 필요 ### 7.2 K8s 매니페스트 #### ServiceAccount + RBAC (노드 조회 권한) ```yaml # k8s/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: name: infra-report-sa namespace: soo --- # k8s/rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: infra-report-reader rules: - apiGroups: [""] resources: ["nodes"] verbs: ["get", "list"] - apiGroups: ["metrics.k8s.io"] resources: ["nodes"] verbs: ["get", "list"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: infra-report-reader-binding subjects: - kind: ServiceAccount name: infra-report-sa namespace: soo roleRef: kind: ClusterRole name: infra-report-reader apiGroup: rbac.authorization.k8s.io ``` #### ConfigMap (환경변수) ```yaml # k8s/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: infra-report-config namespace: soo data: PROMETHEUS_URL: "http://prometheus-server.monitoring" REPORT_STEP: "5m" REPORT_RANGE_DAYS: "7" TZ: "Asia/Seoul" ``` #### Deployment ```yaml # k8s/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: infra-report namespace: soo labels: app: infra-report spec: replicas: 1 selector: matchLabels: app: infra-report template: metadata: labels: app: infra-report spec: serviceAccountName: infra-report-sa containers: - name: infra-report image: harbor.inje-private.com/soo/infra-report:latest ports: - containerPort: 3000 envFrom: - configMapRef: name: infra-report-config resources: requests: cpu: "100m" memory: "256Mi" limits: cpu: "500m" memory: "512Mi" livenessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 10 periodSeconds: 30 readinessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 5 periodSeconds: 10 ``` #### Service + Ingress ```yaml # k8s/service.yaml apiVersion: v1 kind: Service metadata: name: infra-report namespace: soo spec: type: ClusterIP selector: app: infra-report ports: - port: 80 targetPort: 3000 protocol: TCP --- # k8s/ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: infra-report namespace: soo annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: infra-report.gpulive.cloud # 실제 도메인으로 변경 http: paths: - path: / pathType: Prefix backend: service: name: infra-report port: number: 80 ``` ### 7.3 환경변수 관리 | 구분 | 로컬 개발 | K8s Pod | |---|---|---| | Prometheus URL | `.env.local` | ConfigMap | | K8s 인증 | kubeconfig (자동) | ServiceAccount (자동) | | 민감 정보 | `.env.local` | Secret | | 타임존 | OS 설정 | ConfigMap (`TZ=Asia/Seoul`) | ### 7.4 빌드/배포 파이프라인 ``` 1. 코드 Push → Gitea (gitea.inje-private.com) 2. CI (Gitea Runner) ├─ npm ci && npm run build ├─ docker build → harbor.inje-private.com/soo/infra-report:${TAG} └─ docker push 3. CD (ArgoCD: argocd.gpulive.cloud) ├─ k8s/ 매니페스트 변경 감지 └─ 자동 배포 (sync) ``` ### 7.5 Next.js 설정 (standalone 빌드) ```javascript // next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { output: 'standalone', // Docker 최적화 필수 experimental: { // 서버 컴포넌트에서 K8s 클라이언트 사용 시 serverComponentsExternalPackages: ['@kubernetes/client-node'], }, }; module.exports = nextConfig; ``` --- ## 8. 구현 우선순위 로드맵 ### Phase 1: 프로젝트 세팅 + K8s 배포 기반 - [ ] Next.js 프로젝트 초기 구성 (TypeScript, Tailwind, standalone 빌드) - [ ] Dockerfile 작성 + 로컬 빌드 테스트 - [ ] K8s 매니페스트 작성 (ServiceAccount, RBAC, Deployment, Service) - [ ] Harbor 이미지 Push + K8s 클러스터 배포 확인 - [ ] Health check API (`/api/health`) 구현 ### Phase 2: 데이터 수집 MVP - [ ] Prometheus API 클라이언트 구현 (lib/prometheus.ts) - [ ] K8s 노드 정보 조회 (lib/kubernetes.ts, in-cluster config) - [ ] CPU/Memory 메트릭 API Routes 구현 - [ ] 기본 시계열 차트 (ECharts) - [ ] 요약 카드 컴포넌트 ### Phase 3: 데이터 확장 - [ ] Disk/NAS/Network 메트릭 추가 - [ ] 노드별 상세 테이블 - [ ] 통계 계산 로직 (avg, max, p95) ### Phase 4: 분석 기능 - [ ] Peak 구간 감지 (Z-Score) - [ ] 차트에 peak 구간 하이라이트 - [ ] 주의 구간 알림 섹션 - [ ] 전주 대비 트렌드 ### Phase 5: 보고서 기능 - [ ] PDF 내보내기 - [ ] 보고서 기간 선택 UI - [ ] Ingress 설정 + 외부 접근 ### Phase 6: 자동화 (추후) - [ ] 주간 자동 보고서 생성 (CronJob 또는 node-cron) - [ ] Slack/Email 발송 - [ ] Gitea Runner CI/CD 파이프라인 연동 - [ ] ArgoCD 자동 배포 --- ## 9. 참고 자료 ### Prometheus - [Prometheus Query Examples](https://prometheus.io/docs/prometheus/latest/querying/examples/) - [Prometheus API Guide - Last9](https://last9.io/blog/prometheus-api/) - [PromQL Cheat Sheet - SigNoz](https://signoz.io/guides/promql-cheat-sheet/) - [PromQL Cheat Sheet - PromLabs](https://promlabs.com/promql-cheat-sheet/) - [Anomaly Detection with PromQL](https://grigorkh.medium.com/practical-anomaly-detection-with-prometheus-and-promql-d3027b97da96) - [Z-Score in PromQL](https://omarghader.github.io/prometheus-anomaly-detection-z-score-in-promql/) - [Prometheus Over Time Analysis](https://oneuptime.com/blog/post/2025-12-17-prometheus-metrics-over-time-analysis/view) - [PromQL Subqueries and Aggregations](https://oneuptime.com/blog/post/2026-02-02-promql-subqueries-aggregations/view) ### Kubernetes - [K8s Resource Metrics Pipeline](https://kubernetes.io/docs/tasks/debug/debug-cluster/resource-metrics-pipeline/) - [K8s Node Metrics Data](https://kubernetes.io/docs/reference/instrumentation/node-metrics/) - [Metrics Server](https://kubernetes-sigs.github.io/metrics-server/) - [kubectl proxy](https://kubernetes.io/docs/tasks/extend-kubernetes/http-proxy-access-api/) - [@kubernetes/client-node](https://github.com/kubernetes-client/javascript) - [K8s RBAC Authorization](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) ### 프론트엔드 - [React Chart Libraries 비교 2025 - LogRocket](https://blog.logrocket.com/best-react-chart-libraries-2025/) - [Apache ECharts](https://echarts.apache.org/) - [Chart Libraries for React 2026 - Weavelinx](https://weavelinx.com/best-chart-libraries-for-react-projects-in-2026/) ### PDF 생성 - [Next.js PDF Generation](https://dev.to/wonder2210/generating-pdf-files-using-next-js-24dm) - [html2canvas + jsPDF in React](https://medium.com/@saidularefin8/generating-pdfs-from-html-in-a-react-application-with-html2canvas-and-jspdf-d46c5785eff2) - [Puppeteer PDF in Next.js](https://medium.com/front-end-weekly/dynamic-html-to-pdf-generation-in-next-js-a-step-by-step-guide-with-puppeteer-dbcf276375d7) ### NAS/Storage - [Node Exporter - GitHub](https://github.com/prometheus/node_exporter) - [Synology NAS + Prometheus](https://github.com/ddiiwoong/synology-prometheus) - [Network Interface Metrics - Robust Perception](https://www.robustperception.io/network-interface-metrics-from-the-node-exporter/)