~AI가 멍때리지 않게 감시하기 : PLG 모니터링~ 스톡잇! 개발기 #10

2025. 12. 26. 23:06·모의투자
AI가 멍때리지 않게 감시하기 : PLG 모니터링

지난 글에서 트러블 슈팅을 거쳐 AI 서버를 안정적으로 배포하는 데 성공했습니다. 하지만 배포 버튼을 누르고 "끝났다!"라고 외치는 건 개발자의 착각이었습니다.

진정한 서비스 운영은 배포 직후부터 시작됩니다. 특히나 연산량이 많은 AI 서버는 언제 뻗을지 모르는 시한폭탄과 같습니다.

"사용자가 분석 버튼을 눌렀는데 10초 동안 응답이 없으면 어떡하지?" "장 마감 후 AI 모델이 클러스터링을 돌릴 때 메모리가 터지진 않을까?"

이런 막연한 불안함을 없애기 위해서는 서버의 상태를 24시간 감시하고, 장애 징후를 미리 알려주는 모니터링 시스템이 필수적이었습니다.


왜 모니터링이 필요한가? (AI 서비스의 특수성)

단순히 "있으면 좋아서"가 아닙니다. 분산 시스템(MSA) 환경에서 모니터링은 선택이 아닌 필수 생존 도구입니다.

첫째, 관측 가능성(Observability) 확보 Stockit은 Spring Boot(메인)와 FastAPI(AI)가 분리된 구조입니다. 에러가 발생했을 때, 이것이 Spring 로직 문제인지, AI 모델 연산 문제인지, 아니면 DB 지연 때문인지 kubectl logs만으로는 파악하기 어렵습니다. 전체 시스템을 한눈에 조망할 수 있는 대시보드가 필요했습니다.

둘째, 데이터 기반의 리소스 최적화 K-Means 클러스터링 같은 AI 연산은 CPU와 메모리를 순간적으로 많이 사용합니다. "감"으로 메모리를 할당하면 낭비거나 부족합니다. 실제 사용량 데이터를 근거로 리소스를 할당해야 비용을 아낄 수 있습니다.

셋째, 선제적인 장애 대응 (RCA) 사용자가 "로그인이 안 돼요"라고 불평하기 전에, 시스템이 먼저 "에러율 5% 초과"를 감지하고 저에게 알려줘야 합니다. 그래야 사용자가 떠나기 전에 문제를 고칠 수 있습니다.

일반적인 웹 서버와 달리, AI 백엔드 서비스는 모니터링해야 할 포인트가 조금 다릅니다.

 

  • 추론 속도 (Latency): AI 모델이 결과를 내놓는 데 걸리는 시간이 사용자 경험을 좌우합니다. 평균 추론 시간이 0.1초인지 1초인지 데이터로 확인해야 합니다.
  • 리소스 사용량: AI 연산은 CPU/Memory Bound 작업입니다. 리소스가 부족하면 OOM(Out of Memory)으로 서버가 죽을 수 있습니다.

 


도구 선택 : PLG 스택

쿠버네티스 환경에서 가장 표준적이면서도 강력한 오픈소스 조합을 선택했습니다.

  • Prometheus (Metrics): 서버들의 상태 데이터(CPU, 메모리, 응답 속도 등)를 긁어모으는 수집가.
  • Loki (Logs): 분산된 서버들의 로그를 한곳에 모아주는 로그 저장소.
  • Grafana (Visualization): 수집된 데이터와 로그를 예쁜 그래프로 보여주는 대시보드.

이 세 가지를 합쳐서 흔히 PLG 스택이라고 부릅니다.


구축 : Helm으로 한 방에 해결하기

이 복잡한 도구들을 하나하나 설치하려면 YAML 파일만 수십 개를 작성해야 합니다. 저는 Helm(헬름)이라는 쿠버네티스 패키지 매니저를 사용해 loki-stack을 설치했습니다. 명령어 한 줄이면 모니터링 세트가 뚝딱 만들어집니다.

# Grafana Helm 저장소 추가
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

# Loki Stack 설치 (Prometheus + Grafana + Loki 포함)
helm install loki-stack grafana/loki-stack \
  --set prometheus.enabled=true \
  --set grafana.enabled=true \
  --namespace monitoring \
  --create-namespace

 

Prometheus가 데이터를 긁어가려면, 각 서버가 자신의 상태를 /metrics라는 약속된 주소에 노출해줘야 합니다.

FastAPI 서버 설정

prometheus-fastapi-instrumentator 라이브러리를 사용하면 코드 단 두 줄로 해결됩니다.
# app/main.py
from prometheus_fastapi_instrumentator import Instrumentator

app = FastAPI(...)

# ✅ Prometheus 메트릭 자동 수집 설정
# HTTP 요청 수, 응답 시간, 에러율 등을 자동으로 노출
Instrumentator().instrument(app).expose(app)

이제 AI 서버는 /metrics 주소로 접속하면 현재 상태를 숫자로 뱉어냅니다.


Kubernetes Annotation 추가

서버는 준비됐지만, Prometheus는 아직 모릅니다. Pod에 "나를 감시해 줘"라고 알려주는 꼬리표(Annotation)를 붙여야 합니다.

# k8s/deployment.yaml
template:
  metadata:
    annotations:
      # ✅ Prometheus가 이 Pod를 자동으로 발견하고 스크랩하도록 설정
      prometheus.io/scrape: "true"
      prometheus.io/path: "/metrics"  # 메트릭 엔드포인트 경로
      prometheus.io/port: "8000"      # 서버 포트

이렇게 설정하고 재배포(kubectl apply...)하자, Prometheus가 자동으로 AI 서버 Pod를 발견(Discovery)하고 데이터를 수집하기 시작했습니다.


Grafana 대시보드 구성

수집된 데이터를 시각화했습니다. 특히 AI 서버의 건강 상태를 보여주는 핵심 지표들을 PromQL 쿼리로 작성했습니다.

  • HTTP 요청 수 (Throughput): rate(http_requests_total[1m]) * 60
  • 평균 응답 시간 (Latency): histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
  • AI 서버 CPU 사용량: rate(container_cpu_usage_seconds_total{pod=~"stock-analyze.*"}[5m])


성과 : 데이터 기반의 운영과 결정적 버그 발견 (DevOps 경험)

드디어 Grafana 대시보드에 그래프가 그려지기 시작했습니다.

  • AI 서버 상태: 추론 요청이 몰릴 때 CPU가 얼마나 튀는지 실시간 확인
  • 메인 서버 상태: JVM 힙 메모리와 HikariCP(DB 연결) 상태 확인
  • 통합 로그: 두 서버에서 발생하는 에러 로그를 한 화면에서 검색 (Loki)

이제 막연한 불안감 대신 데이터가 생겼습니다. 서버가 느려지면 어디가 병목인지 그래프를 보고 알 수 있게 되었습니다.

이때, 매우 중요한 로그를 발견했습니다.


문제 : 숨어있던 'Bean 충돌' 버그 발견

모니터링 시스템을 구축하자마자, Loki 로그 대시보드에서 빨간색 ERROR 로그가 쏟아지는 것을 발견했습니다. 메인 서버(Spring Boot)가 배포 직후 시작되지 못하고 계속 죽고 있었습니다.

APPLICATION FAILED TO START
Description:
Parameter 1 of constructor in grit.stockIt.domain.auth.client.KakaoOAuthClient 
required a single bean, but 2 were found:
- kakaoOAuthProperties: ...
- kakao-grit.stockIt.domain.auth.config.KakaoOAuthProperties: ...

문제는 'Bean 충돌'이었습니다.

Spring Boot가 KakaoOAuthProperties라는 설정 객체를 주입하려는데, 똑같은 타입의 Bean이 2개나 등록되어 있어 충돌이 난 것입니다. 로컬 환경에서는 IDE가 알아서 처리해줬지만, 실제 배포 환경(Jar 실행)에서는 엄격하게 체크되면서 터진 문제였습니다.

로그를 보자마자 원인을 파악했고, 중복된 Bean 중 하나에 @Primary 어노테이션을 붙여 우선순위를 지정함으로써 문제를 즉시 해결했습니다.


해결 : DevOps는 문화다

저는 즉시 카카오 로그인 기능을 담당한 팀원에게 로그 화면을 보여주며 문제를 공유하며 현재 배포 환경에서 Bean이 중복 등록되어 서버가 뜨지 않고 있으니 KakaoOAuthClient 생성자 부분 확인을 요청했습니다.

팀원과 함께 코드를 확인했고, 중복된 Bean 중 하나에 @Primary 어노테이션을 붙여 우선순위를 지정함으로써 문제를 즉시 해결했습니다.


결과 및 회고 : 이것이 데브옵스다.

이것이 바로 DevOps의 핵심이었습니다. 만약 모니터링 시스템이 없었다면, 저는 "어? 서버가 왜 안 뜨지?" 하며 kubectl logs 명령어를 수동으로 치고, 수백 줄의 로그를 눈으로 훑느라 몇 시간을 허비했을 겁니다. 팀원 역시 자신의 로컬에선 잘 되니 "내 코드는 문제없어"라고 생각했을지도 모릅니다.

하지만 Grafana Loki 덕분에 문제 발생 1분 만에 원인을 파악했고, 팀원과 데이터(로그)를 기반으로 소통하며 빠르게 문제를 해결할 수 있었습니다. DevOps는 단순한 직무가 아니라, "개발과 운영의 벽을 허물고 함께 문제를 해결하는 문화"임을 몸소 체험한 순간이었습니다.

무엇보다 큰 수확은 "개발(Dev)을 넘어 운영(Ops)의 관점"을 갖게 된 것입니다. 단순히 코드를 짜고 배포하는 것에서 끝나는 것이 아니라, 내 코드가 실제 환경에서 리소스를 얼마나 쓰는지, 사용자에게 어떤 경험을 주는지 데이터로 확인하고 개선하는 피드백 루프를 만들었습니다.

모니터링 시스템까지 갖췄으니, 이제 개발 주기를 빠르게 만들 차례입니다.

 

다음 글에서는 코드를 수정하면 자동으로 테스트하고 배포까지 해주는 "업데이트를 자동화하다: CI/CD 파이프라인 구축기"과정을 다루겠습니다.

'모의투자' 카테고리의 다른 글

~업데이트를 자동화하다 : CI/CD 파이프라인 구축기~ 스톡잇! 개발기 #11  (1) 2025.12.27
~서버가 로그 없이 죽었다 : 쿠버네티스를 디버거로 쓴 사연~ 스톡잇! 개발기 #9  (1) 2025.12.26
~파이썬 AI, 웹 서버가 되다 : MSA와 FastAPI 아키텍처에 대한 고찰~ 스톡잇! 개발기 #8  (0) 2025.12.26
~세상의 모든 주식을 분석하다 : 조회에서 예측으로~ 스톡잇! 개발기 #7  (0) 2025.12.26
~투자 성향을 캐릭터로 만들다: 페르소나 매칭 설계~ 스톡잇! 개발기 #6  (0) 2025.12.26
'모의투자' 카테고리의 다른 글
  • ~업데이트를 자동화하다 : CI/CD 파이프라인 구축기~ 스톡잇! 개발기 #11
  • ~서버가 로그 없이 죽었다 : 쿠버네티스를 디버거로 쓴 사연~ 스톡잇! 개발기 #9
  • ~파이썬 AI, 웹 서버가 되다 : MSA와 FastAPI 아키텍처에 대한 고찰~ 스톡잇! 개발기 #8
  • ~세상의 모든 주식을 분석하다 : 조회에서 예측으로~ 스톡잇! 개발기 #7
MacArthur17
MacArthur17
Mac(Arthur)hine Learning
  • MacArthur17
    Human Learning LAB
    MacArthur17
  • 전체
    오늘
    어제
    • 분류 전체보기 (67)
      • 모의투자 (32)
      • 분산 시스템 (2)
      • 테커 부트캠프 (20)
      • 백준문제 (24)
      • 공부일기 (0)
      • 후기 (3)
      • 팁 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
MacArthur17
~AI가 멍때리지 않게 감시하기 : PLG 모니터링~ 스톡잇! 개발기 #10
상단으로

티스토리툴바