Project Portfolio

강민성

서비스 안정성을 설계하고 운영으로 연결하는 인프라 엔지니어

CuKee — AI 영화 큐레이션 플랫폼 Gleey — 웹소설 작가용 AI 워크스페이스

Project 01

CuKee

AI 영화 큐레이션 플랫폼

Qwen-14B 기반 AI 큐레이션과 3D 전시회 경험을 제공하는 팀 프로젝트.
저는 AI 서빙 서버 구축, GPU 배포, 모니터링, CI/CD 파이프라인을 담당했습니다.

기간

2025.11 ~ 2026.02

담당 영역

AI 서빙, GPU 배포, 모니터링, CI/CD

주요 기술

FastAPI, PyTorch, Docker, Nginx, Prometheus, Grafana, GitHub Actions

인프라 아키텍처 — 2-Tier 구성과 판단 근거

CuKee 인프라 아키텍처 다이어그램

왜 DB를 GPU 서버에 배치했는가

상황

VM1에 배정된 저장 용량은 10GB. Frontend, Backend, Nginx, Redis 컨테이너만으로도 저장 공간이 부족한 상황이었습니다.

판단

VM2(GPU 서버)는 100GB가 배정되어 있고, AI 모델은 메모리에 로드되므로 디스크 여유가 충분했습니다. PostgreSQL을 GPU 서버에 설치하고, VM1에서만 접속할 수 있도록 네트워크를 격리하여 보안을 확보했습니다.

결과

애플리케이션과 DB 사이의 네트워크 경로가 명확해졌고, GPU 서버의 남는 디스크를 활용하여 별도 스토리지 없이 안정적으로 운영할 수 있었습니다.

CI/CD 파이프라인 — 서비스별 분리 배포

6개 GitHub Actions 워크플로를 서비스 단위로 분리 — 변경된 서비스만 트리거되어 Docker Hub를 거쳐 VM1(앱 서버)·VM2(GPU 서버)에 배포됩니다.

CuKee CI/CD 파이프라인 다이어그램

왜 워크플로를 분리했는가

배포 대상 서버가 다릅니다. VM1(애플리케이션)과 VM2(GPU)는 별도 서버이므로, SSH 접속 정보와 배포 스크립트가 달라야 합니다. AI 워크플로는 GPU 서버에 docker-compose.ai.yml을 SCP로 전송한 뒤 배포합니다.
빌드 시간과 비용이 다릅니다. AI 서버 이미지는 PyTorch + CUDA 기반으로 빌드 시간이 깁니다. 프론트엔드 코드 한 줄 수정에 AI 서버까지 다시 빌드하는 건 낭비입니다.
장애 격리가 가능합니다. Nginx 설정 변경이 백엔드 배포에 영향을 주지 않고, 각 서비스를 독립적으로 롤백할 수 있습니다.
핵심: 서비스 단위로 워크플로를 분리하여, 변경된 서비스만 빠르게 배포하고 나머지는 영향받지 않는 구조를 만들었습니다. 특히 AI 서버처럼 빌드 비용이 높은 서비스를 독립적으로 관리하여 CI/CD 비용과 배포 안정성을 동시에 확보했습니다.

GPU 배포 트러블슈팅 — 호환성과 메모리 문제 해결

문제 1: PyTorch & 의존성 호환 충돌

발생

BGE-M3 임베딩 모델이 PyTorch 2.6.0을 요구했으나, 기존 Docker 이미지는 이전 버전 기반이었습니다. 모델 로딩 시점에 런타임 에러가 발생했습니다.

조치

Dockerfile의 베이스 이미지를 pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime으로 변경했습니다. T4 GPU가 CUDA 12.4를 지원하는 것을 확인한 뒤 결정했습니다.

후속

PyTorch 업그레이드 후 bitsandbytes(4-bit 양자화 라이브러리)가 빌드에 실패했습니다. C 컴파일러가 없어서 발생한 문제로, build-essential 패키지를 추가하고 triton도 함께 설치하여 호환성을 확보했습니다.

문제 2: GPU VRAM 부족 — 모델 + LoRA + 임베딩 동시 서빙

발생

T4 GPU의 VRAM은 16GB. Qwen-14B 베이스 모델 + LoRA 어댑터 + BGE-M3 임베딩 모델을 동시에 올리면 VRAM이 초과되어 OOM(Out of Memory)이 발생했습니다.

판단 과정

이 문제를 감으로 해결하지 않기 위해 Prometheus + Grafana + DCGM Exporter 모니터링 스택을 구축했습니다. 실시간으로 VRAM 사용량, GPU Utilization, 온도를 관측하면서 다음 실험을 진행했습니다.

해결
  • 베이스 모델: bitsandbytes 4-bit 양자화 적용하여 VRAM 점유를 절반 이하로 축소
  • LoRA 어댑터: fp32 → fp16으로 전환. 모니터링 결과 정확도 차이 없이 VRAM을 절약
  • 임베딩 모델: 시스템 리소스 모니터링을 기반으로 BGE-M3가 VRAM 여유 내에서 운영 가능함을 확인 후 채택

최종 환경 구성

베이스 이미지

pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime 기반. build-essential, triton 추가 설치로 양자화 라이브러리 호환성 확보

모델 구성

Qwen-14B (4-bit 양자화) + LoRA (fp16) + BGE-M3 임베딩. T4 16GB VRAM 내에서 안정 운영

핵심: 모니터링 데이터를 근거로 모델 정밀도와 양자화 수준을 결정했습니다. "될 때까지 시도"가 아니라 "측정하고 판단"하는 방식으로 접근했습니다.

모니터링 시스템 구축 — 측정 기반 의사결정

GPU 서버의 리소스를 실시간으로 관측하기 위해 docker-compose.ai.yml에 모니터링 스택을 함께 구성했습니다.

DCGM Exporter

NVIDIA GPU 메트릭 수집. VRAM 사용량, GPU Utilization, 온도, 전력 소비를 Prometheus 포맷으로 노출합니다.

Node Exporter

시스템 메트릭 수집. CPU, RAM, 디스크 I/O를 수집하여 GPU 외 병목도 함께 확인합니다.

Prometheus

메트릭 수집 허브. 5GB 저장 + 7일 보존 정책으로 운영하며, DCGM과 Node Exporter 데이터를 통합 저장합니다.

Grafana

대시보드. /monitor/ 경로로 서빙. GPU 사용량, 시스템 리소스, 모델 서빙 상태를 시각화합니다.

CuKee Grafana 대시보드

모니터링이 의사결정에 기여한 사례

양자화 수준 결정

fp16 로드 시 16GB 초과 확인 → 4-bit 양자화 적용 후 ~8GB로 안정화

LoRA 정밀도 결정

fp32 vs fp16 VRAM 비교 → 응답 품질 차이 없어 fp16 확정

임베딩 모델 선택

BGE-M3 추가 VRAM 실측 → 전체 조합이 16GB 내 운영 가능 확인

OpenAI 호환 Public AI API — 외부 서빙 구조

GPU 서버에서 운영 중인 페르소나 모델(Qwen-14B + LoRA)을 외부에서도 API로 호출할 수 있도록, OpenAI chat completion 포맷과 호환되는 Public API를 구축했습니다.

전체 요청 흐름

1
인증

Bearer 토큰 또는 X-API-Key 헤더로 API Key 검증. 만료·폐기 상태 확인

2
요청 파싱

OpenAI messages 배열에서 사용자 프롬프트 추출. ticketId로 페르소나 테마 매핑

3
GPU 서버 프록시

백엔드가 내부망(10.0.x.x:5000)의 AI 서버로 요청을 중계. 120초 타임아웃 + 에러 핸들링

4
응답 변환 + 로깅

AI 서버 응답을 OpenAI chat.completion 포맷으로 변환하여 반환. 토큰 사용량·비용·레이턴시를 DB에 기록

설계 포인트

API Key 관리

콘솔에서 발급·폐기. RPM/TPM/RPD 제한 설정. 키별 사용량 독립 추적

빌링 시스템

요청별 토큰 추정 + 비용 산출. 일별 집계 테이블로 대시보드 조회 지원

보안 격리

GPU 서버는 내부망에서만 접근 가능. 백엔드가 유일한 프록시 경로로 외부 노출 차단

핵심: GPU 서버를 직접 노출하지 않고, 백엔드가 인증·과금·로깅을 담당하는 프록시 역할을 하여 보안과 운영 관측성을 동시에 확보했습니다.

API 관리 콘솔 — 운영 대시보드 구축

Public AI API의 사용량·비용·상태를 확인하고 API Key를 관리할 수 있는 웹 콘솔을 프론트엔드 + 백엔드 양쪽에 구현했습니다.

CuKee Console - Usage & Billing CuKee Console - API Keys, Alerts, Docs

Usage

24시간 기준 총 요청 수, 성공률, 평균 레이턴시 표시. 2시간 단위 트래픽 바 차트와 엔드포인트별 호출 빈도 테이블 제공

Billing

30일 누적 비용 산출 및 월별 결제 히스토리 조회. 토큰당 단가 기반 비용 모델(Input ₩2,000/M, Output ₩15,000/M) 적용

API Keys

키 발급·폐기·목록 조회. 키별 상태(active/revoked/expired) 관리 및 프리뷰 마스킹 처리

Alerts & Docs

사용량·에러율·빌링 알림 설정 UI 및 OpenAI 호환 API 명세서 제공

API 콘솔 — 백엔드 구현 구조

인증

콘솔 전용 토큰으로 로그인. httpOnly 쿠키 기반 세션 관리, 환경별(dev/prod) 쿠키 정책 분리

데이터 집계

요청별 로그(cuk_api_usage_logs) + 일별 집계(cuk_api_usage_daily) 2단 구조. 대시보드 조회 시 집계 테이블에서 즉시 응답

콘솔 API 엔드포인트

GET /console/usage

기간별 요청 수·성공률·레이턴시 집계 조회. 2시간 단위 바 차트 데이터 반환

GET /console/billing

월별 비용 집계·결제 히스토리 조회. 토큰 단가 기반 비용 산출

POST /console/api-keys

API Key 발급. RPM/TPM/RPD 제한값·만료일 설정

DELETE /console/api-keys/{id}

API Key 폐기. 즉시 revoked 전환, 기존 사용 로그 보존

DB 스키마

cuk_api_keys

API Key 레코드. 상태(active/revoked/expired), 만료일, RPM·TPM·RPD 제한값 저장. SHA-256 해시로 키 보안 저장

cuk_api_usage_logs

요청별 로그. request_id, 토큰 수, 비용, 레이턴시, IP, User-Agent 기록. 빌링·디버깅 양쪽에 활용

cuk_api_usage_daily

일별 집계. 키·모델별 요청 수, 에러 수, 토큰 합계, 비용 합계를 사전 집계하여 대시보드 즉시 응답

핵심: API를 만드는 것에서 끝내지 않고, 운영자가 사용량과 비용을 직접 확인하고 키를 관리할 수 있는 콘솔까지 구축하여 자체 운영이 가능한 구조를 완성했습니다.

Project 02

Gleey

웹소설 작가용 AI 통합 워크스페이스

기획, 집필, AI 보조를 하나의 흐름으로 연결하는 멀티플랫폼 작업 환경.
저는 인프라 설계, Traefik 기반 리버스 프록시, CI/CD 파이프라인, AI 서비스 서버를 담당했습니다.

기간

2026.02 ~ 2026.03

담당 영역

인프라, CI/CD, 리버스 프록시, AI 서빙

주요 기술

Traefik, Docker, GitHub Actions, GHCR, FastAPI, Electron

인프라 아키텍처 — 단일 VPS 통합 구성

Gleey 인프라 아키텍처 다이어그램

구성 한눈에 보기

진입 · 보안

사용자 요청은 Cloudflare(WAF · SSL/TLS · DDoS 방어 · CDN 캐싱)를 거쳐 ServaRica VPS로 들어오고, Traefik(Edge Network)이 받아 각 서비스로 라우팅합니다.

애플리케이션

Frontend(Nginx · React · Vite, :3000), Backend API Gateway(FastAPI, :8000), Redis 캐시(:6379)가 VPS 안에서 동작합니다.

데이터 · 관측 · 외부

PostgreSQL(:5432)로 데이터를 영속하고, Prometheus · Grafana · Node Exporter로 모니터링하며, AI 기능은 외부 Google Gemini API를 호출합니다.

인프라 설계 — Traefik 선택과 멀티 도메인 구성

왜 Nginx 대신 Traefik을 선택했는가

상황

Gleey는 제품 소개 페이지(랜딩)와 데스크톱 애플리케이션 서비스를 서로 다른 도메인으로 운영해야 했습니다. 또한 프론트엔드, 백엔드, AI 서비스 등 여러 컨테이너가 동시에 돌아가는 환경이었습니다.

판단

  • 동적 서비스 디스커버리: Traefik은 Docker 소켓을 통해 컨테이너를 자동 감지합니다. 새로운 서비스를 추가할 때 설정 파일을 직접 수정하고 리로드할 필요 없이, Docker 라벨만 붙이면 라우팅이 자동 설정됩니다.
  • 멀티 도메인 라우팅: 도메인별로 다른 서비스에 연결하는 규칙을 선언적으로 관리할 수 있습니다. Nginx에서는 server 블록을 도메인마다 작성해야 하지만, Traefik은 라벨 기반으로 간결하게 처리합니다.
  • 무중단 설정 변경: Nginx는 설정 변경 시 reload가 필요하지만, Traefik의 Docker provider는 컨테이너 상태 변화를 실시간 반영합니다. 배포 중 다운타임 없이 서비스 전환이 가능합니다.

구성

Traefik 3.6.9을 edge 프록시로 배치하고, edgegleey-net 두 개의 Docker 네트워크를 분리했습니다. TLS 인증서는 Cloudflare Origin 인증서를 /etc/traefik/ssl/에 마운트하여 관리합니다.

네트워크 및 인증서 구성

edge 네트워크

Traefik ↔ 외부 트래픽 연결. HTTPS 종단점 역할, 도메인별 라우팅 규칙 적용

gleey-net 네트워크

서비스 간 내부 통신 전용. 외부 접근 차단으로 서비스 격리 보장

TLS 인증서

Cloudflare Origin 인증서를 파일 마운트. Traefik entrypoint에서 HTTPS 종단 처리

서비스 등록

Docker 라벨로 도메인 → 서비스 매핑 선언. 컨테이너 시작만으로 라우팅 자동 반영

핵심: 프록시(edge)와 서비스 간 통신(gleey-net)을 네트워크 수준에서 분리하여, Docker 라벨만으로 라우팅이 자동 반영되면서도 내부 서비스는 외부에 노출되지 않는 구조를 만들었습니다.

CI/CD 파이프라인 — 워크플로 분리 전략

Gleey는 Web, Desktop(macOS/Windows), Backend로 구성된 프로젝트입니다. 각 영역의 빌드 환경과 배포 대상이 다르기 때문에 워크플로를 분리했습니다.

Gleey CI/CD 파이프라인 다이어그램

분리 배경

빌드 환경이 다릅니다. 웹 배포는 ubuntu-latest, macOS 데스크톱은 macos-latest, Windows 데스크톱은 windows-latest가 필요합니다. 하나의 워크플로에 합치면 matrix 전략으로 복잡도가 급증합니다.
빌드 비용과 시간이 다릅니다. 데스크톱 빌드는 네이티브 컴파일이 포함되어 느립니다. UI 한 줄 수정에 Electron 빌드까지 함께 돌면 CI 비용이 불필요하게 증가합니다.
배포와 릴리즈 주기가 다릅니다. 웹은 feature 브랜치 push마다 배포하지만, 데스크톱은 dev → main 머지 시에만 릴리즈합니다. path 기반 트리거로 각 영역의 코드가 변경될 때만 해당 워크플로가 실행됩니다.
GHCR을 컨테이너 레지스트리로 사용합니다. CuKee에서는 Docker Hub를 사용했지만, Gleey에서는 GitHub Container Registry(GHCR)로 전환했습니다. 레포지토리와 같은 GitHub 생태계 내에서 이미지를 관리하면 권한 관리가 단순해지고, GITHUB_TOKEN으로 인증이 자동 처리됩니다.

배포 트러블슈팅 — 패키징, 배포 트리거, AI 안정화

문제 1: Electron 데스크톱 패키징 실패

발생

macOS에서 Electron 앱 패키징 시, 코드 서명 없이 빌드하면 실패하는 문제가 발생했습니다. Apple Developer 인증서 없이 팀 내 배포가 필요한 상황이었습니다.

조치

미서명 Mac 패키징 폴백을 구현하여 인증서 없이도 빌드가 가능하도록 처리했습니다. macOS drag region 이슈와 중복 relayout 핸들러도 함께 수정했습니다.

문제 2: 배포 트리거 과잉 실행

발생

dev 브랜치에 push할 때마다 전체 배포 워크플로가 실행되면서, 불필요한 빌드와 배포가 반복됐습니다.

조치

dev 브랜치의 push 이벤트에서 자동 배포 트리거를 제거하고, 특정 feature 브랜치와 workflow_dispatch로만 배포되도록 수정했습니다. concurrency group을 설정하여 동시 배포를 방지했습니다.

문제 3: AI 모델 교체 과정의 불안정

발생

AI 모델 교체 과정에서 맞춤법 검사 기능의 응답이 불안정했고, AI 패널과 캐릭터 이름 생성 기능에서 간헐적 오류가 발생했습니다.

조치

AI 모델을 단계적으로 교체하면서 각 기능별 안정성을 검증했습니다. 맞춤법 검사 모델을 4차례 반복 수정하고, AI 패널 응답 처리와 캐릭터 이름 생성 로직을 안정화했습니다.

핵심: 배포 파이프라인의 트리거 조건, concurrency 제어, 릴리즈 조건을 점진적으로 정교화하면서 "코드 변경 → 배포"의 신뢰성을 높였습니다.

Summary

두 프로젝트를 마치며

측정 기반 의사결정

Prometheus + Grafana로 GPU 리소스를 실시간 관측하고, 데이터를 근거로 모델 정밀도와 양자화 수준을 결정했습니다.

환경에 맞는 인프라 설계

제한된 리소스(10GB VM)에서 DB 배치를 최적화하고, 멀티 도메인 환경에서 Traefik을 선택하는 등 상황에 맞는 판단을 내렸습니다.

서비스별 분리 배포

CuKee 6개, Gleey 5개의 CI/CD 워크플로를 구성하여, 빌드 환경·비용·배포 대상에 따라 파이프라인을 분리했습니다.

점진적 문제 해결

GPU 호환성, 데스크톱 패키징, 배포 트리거 과잉 등의 문제를 커밋 단위로 추적하며 단계적으로 해결했습니다.