제목, 태그, 카테고리로 검색

모든 글
약 5분 분량 프로젝트/오락가락

[트러블슈팅] AI 서버 동시 요청 시 GPU OOM

목차

한 줄 요약

동시 요청 제한 없이 AI 서버에 보내다가 하루 5-10회 GPU OOM이 터졌어요. ThreadPool(max=4) + Semaphore(permits=2)로 이중 동시성 제어를 걸어서 OOM 0회로 잡았어요.


증상

운영 중에 음성 분석 요청이 몰리면 Python AI 서버가 죽었어요. Pod가 OOMKilled 상태로 재시작되고, 재시작되는 동안 모든 분석 요청이 타임아웃. 하루에 5-10회 반복됐어요. Grafana의 컨테이너 모니터링 대시보드에서 AI 서버 Pod의 재시작 횟수가 하루 단위로 올라가는 걸 확인했어요.

환경

  • Python FastAPI (AI 서버), PyTorch + GPU (16GB VRAM)
  • Spring Boot (API 서버), WebClient 비동기 호출
  • Docker Compose 단일 서버 구성

원인 분석

GPU 메모리를 계산해봤어요.

GPU 전체 메모리 16GB 중 모델 로딩에 4GB가 상시 점유돼요. 추론 1건당 약 3GB를 써요. 최대 동시 처리는 (16 - 4) / 3 = 약 4건이에요.

그런데 Kafka Consumer에서 이벤트가 들어오는 대로 AI 서버에 요청을 쏘고 있었어요. 동시에 10건이 들어오면 30GB가 필요한데 16GB밖에 없으니 OOM이 나는 게 당연했어요.

AI 분석 담당 팀원과 같이 nvidia-smi로 GPU 메모리 사용량을 모니터링하면서 동시 요청 수와 OOM 발생 시점을 대조했어요. 동시 3건까지는 안정적이고, 4건부터 가끔 스파이크가 나고, 5건 이상이면 거의 확실하게 OOM이 터졌어요.


해결: ThreadPool + Semaphore 이중 제어

단순히 동시 요청을 줄이면 되는 문제가 아니었어요. WAV 변환과 AI 분석이 같은 Consumer에서 처리되는데, WAV 변환은 CPU 바운드라 빠르게 끝나고 AI 분석은 GPU를 오래 점유하거든요. 같은 스레드풀로 처리하면 AI 분석이 WAV 변환까지 블로킹하거든요.

그래서 두 가지를 분리했어요.

ThreadPool(max=4): 시스템 내부 리소스(CPU, 메모리) 보호. 최대 4개 스레드까지 작업을 처리해요.

Semaphore(permits=2): 외부 서비스(AI 서버 GPU) 보호. 4개 스레드가 동시에 실행되더라도 AI 서버에는 2개만 동시 요청해요.

permits를 GPU 계산상 최대 4건까지 가능하지만 2로 잡은 이유는, 요청마다 메모리 사용량이 다르고(음성 길이, 복잡도에 따라 편차), 안전 마진을 둬야 했기 때문이에요.

Semaphore Bean 설정

ThreadPool 설정

Semaphore 사용 코드


작업별 리소스 분리

작업 타입ThreadPoolSemaphore이유
WAV 변환5~108CPU 바운드, 빠른 처리
음성 분석2~42GPU 사용, 무거운 AI 처리
이미지 처리3~64중간 수준
배치 복구2~43백그라운드 처리

무거운 작업(AI 분석)이 가벼운 작업(WAV 변환)을 블로킹하지 않도록 풀을 분리한 게 핵심이에요.


결과

지표개선 전개선 후
AI 서버 OOM 발생하루 5-10회0회
평균 분석 대기 시간실패로 무한 대기30초
GPU 활용률불안정 (100% 스파이크)85% 안정
분석 성공률~70%99%+

요청이 폭주해도 세마포어 대기열에서 순차 처리되니 Pod 재시작이 0회가 됐어요.


참고 자료

Summary

Eliminated daily 5-10 GPU OOM crashes by implementing dual concurrency control: ThreadPool (max=4) for system resources and Semaphore (permits=2) for GPU protection, achieving zero OOM incidents.


Symptoms

During operation, the Python AI server crashed when voice analysis requests spiked. Pods restarted as OOMKilled, causing all analysis requests to timeout during restart. This repeated 5-10 times daily. Grafana’s container monitoring dashboard showed the AI server Pod restart count climbing daily.

Environment

  • Python FastAPI (AI server), PyTorch + GPU (16GB VRAM)
  • Spring Boot (API server), WebClient async calls
  • Docker Compose single-server setup

Root Cause

GPU memory calculation: 16GB total, 4GB constant for model loading, ~3GB per inference. Maximum concurrent processing: (16 - 4) / 3 ≈ 4.

But the Kafka Consumer was firing requests to the AI server as fast as events arrived. 10 concurrent requests need 30GB on 16GB hardware — OOM was inevitable.

Monitoring GPU memory with nvidia-smi alongside the AI team, we correlated concurrent request counts with OOM timing. 3 concurrent was stable, 4 showed occasional spikes, 5+ almost guaranteed OOM.


Solution: ThreadPool + Semaphore Dual Control

Simply reducing concurrent requests wasn’t enough. WAV conversion and AI analysis share the same Consumer, but WAV conversion is CPU-bound (fast) while AI analysis occupies the GPU long. A shared thread pool lets AI analysis block WAV conversion.

Two mechanisms were separated:

ThreadPool (max=4): Protects internal system resources (CPU, memory). Up to 4 threads process tasks.

Semaphore (permits=2): Protects external service (AI server GPU). Even with 4 threads running, only 2 can simultaneously request the AI server.

Permits were set to 2 (not the theoretical max of 4) because per-request memory varies by audio length and complexity, requiring a safety margin.

Semaphore Bean Config

ThreadPool Config

Semaphore Usage Code


Per-Task Resource Isolation

Task TypeThreadPoolSemaphoreReason
WAV Conversion5~108CPU-bound, fast
Voice Analysis2~42GPU-heavy AI processing
Image Processing3~64Medium workload
Batch Recovery2~43Background processing

The key is separating pools so heavy tasks (AI analysis) don’t block light tasks (WAV conversion).


Results

MetricBeforeAfter
AI server OOM5-10/day0
Avg analysis waitInfinite (failures)30s
GPU utilizationUnstable (100% spikes)85% stable
Analysis success rate~70%99%+

Even under request surges, sequential processing via semaphore queue reduced Pod restarts to zero.


References

Author
작성자 @범수

오늘의 노력이 내일의 전문성을 만든다고 믿습니다.

댓글