GPU가 남아도는데 왜 서비스가 먼저 무너지는가 — 추론 서비스의 유령 함정

← 전체 목차 · 이전: 배치 처리로 처리량을 올렸는데 왜 P99 꼬리 지연은 나빠지나 · 다음: 추론 최적화는 왜 엔진 층부터 손대야 하는가

운영 활동으로 트래픽이 3배 튀자 운영팀이 GPU 인스턴스를 2배로 긴급 증설했다. 그런데 접속 지연 타임아웃률은 1%에서 40%로 뛰었다. GPU 사용률은 평균 60%, 메모리는 7할만 찼다 — 자원은 멀쩡한데 서비스가 먼저 죽었다. "부족하면 늘린다"는 반사적 대응이 이 사고 앞에서는 통하지 않는다.

내용
표면 질문대모델 추론 서비스는 고동시성을 어떻게 버티는가
실제 쟁점GPU 자원이 남는데 왜 요청이 쌓이는가 — 그리고 자원을 늘릴 수 없을 때 남은 카드는 무엇인가

"무작정 기계를 늘린다"는 고동시성의 답이 아니다. 답은 스케줄링 정밀도와 링크 설계에 있다.

핵심 답변: 병목은 연산력이 아니라 연산력 이전에 있다

GPU 사용률 60%, 메모리 70%  ← 포화 아님
그런데
요청이 전부 "서비스 진입 큐"에 갇혀 있다
  → 모델에 아직 들어가지도 못한 채 타임아웃

결론: 병목은 연산력이 아니라 연산력 이전(스케줄링)에 있다

이 현상에 이름을 붙이면 유령 함정이다 — 오픈소스 추론 프레임워크를 배치하고 오토스케일링만 걸면 고동시성이 해결된다고 믿지만, 실제 병은 추론 스케줄링의 입도 불균형링크 설계의 부재다.

타임아웃이 새로운 트래픽을 만든다

요청 타임아웃 → 사용자가 페이지를 새로고침·재질문
  → 요청량이 다시 늘어남 → 큐가 더 길어짐 → 더 많은 타임아웃
  → 정방향 피드백(positive feedback)으로 전면 눈사태

과부하는 선형으로 나빠지지 않는다. 재시도가 만드는 정방향 피드백이 붕괴를 가속한다. 어떤 하한책도 사용자가 재시도를 시작하기 전에 작동해야 한다.

1층: 업무 등급별 스케줄링 — 요청은 동등하지 않다

한 줄짜리 짧은 질문과 장문 요약 요청을 똑같이 취급하는 순간 "요청 수" 기준 관리는 이미 실패한다.

수단대상방식대가
고우선 실시간 큐실시간 대화, 단문 문답GPU 자원을 독점 배정처리량 일부를 희생해서라도 저지연을 지킨다
저우선 배치 큐장문 요약, 대량 생성유휴 자원을 비수기에 활용시간으로 비용을 산다
요청 입도 관리초장문 컨텍스트, 대량 출력별도로 떼어 단독 스케줄링대형 요청 하나가 카드 하나의 메모리를 다 먹어 수십 건의 소형 요청을 함께 끌고 내려가는 것을 막는다

가장 간과되는 것이 요청 입도 관리다. 거대 요청 하나가 카드 하나의 메모리를 통째로 삼켜 뒤에 줄 선 수십 건을 함께 끌어내린다.

2층: 링크 하한책과 동적 튜닝

  • 연속 배치(continuous batching): 배치 최대 대기 시간, 최대 배치 크기 두 파라미터로 지연 상한 안에서 단일 카드 처리량을 최대화한다. 단건 직렬 추론으로 되돌아가지 않는다.
  • 계산량 기준 제한(rate limit): 요청 수 기준을 버리고 입출력 token 총량 / 예상 계산량 기준으로 바꾼다. 요청 수 기준은 긴 요청 하나를 짧은 요청 하나와 동일하게 세어 **"숨은 과부하"**를 놓친다.
  • 다단 강등: 부하가 임계값을 넘으면 한 번에 전면 포기가 아니라 단계적으로 내려간다 — 경량 모델 전환 → 출력 길이 축소 → 핫 캐시 답변 반환. 원칙은 핵심 서비스 가용성을 먼저 지키는 것이지 전면 붕괴가 아니다.

3층: 아키텍처 최적화 — 지연과 비용을 동시에 누른다

수단방식언제 절약되는가
핫 문답 캐시고빈도 통용 질문의 생성 결과를 캐싱, 동일 질문에 즉시 반환요청 발생 시점에 모델 호출 자체를 없앤다
KV 캐시 재사용다중 턴 대화에서 이전 턴의 Key/Value 재사용대화 진행 중 매 턴 재계산을 피한다 (단일 턴 연산 비용 최대 절반 이상 절감)
성수기 이전 자원 고정대형 프로모션·이벤트 직전에 전용 GPU 자원 풀을 선점, 비핵심 배치 작업 자원 배정을 끈다피크 도래 전 준비를 끝내는 유일한 수단 — 사후 대응은 이미 늦다

세 수단의 시간축이 다르다는 점이 핵심이다. 핫 캐시는 요청이 발생할 때, KV 재사용은 대화가 진행되는 동안, 자원 고정은 피크가 오기 전에 끝내야 한다.

안정성 재정의: 세 조건과 다섯 실패 시나리오

"정상 트래픽을 버틴다"는 정의로는 우선순위 스케줄링·다단 강등·자원 격리가 자연히 도출되지 않는다. 정의를 먼저 바꿔야 한다.

서비스 안정성 = 세 조건의 동시 충족
  ① 피크 시간대 핵심 업무가 우선 가용
  ② 이상 요청이 전체를 끌고 내려가지 않음
  ③ 장애가 빠르게 자가 치유됨

이 정의 아래 미리 열거해야 할 실패 시나리오는 다섯 가지다.

#실패 시나리오결과
1돌발 트래픽이 서비스 상한을 초과큐 적체, 연쇄 눈사태
2초장문 컨텍스트 요청이 메모리를 대량 점유정상 요청의 연산력 잠식
3개별 요청이 추론 중 멈춤GPU 자원 해제 불가, 지속 점유
4다중 모델 혼합 배치가 연산력 쟁탈핵심 모델이 저우선 작업에 끌려 지연
5노드 장애로 일부 인스턴스 탈락잔여 인스턴스 압력 급증 → 2차 과부하

5번이 특히 간과된다. 장애 자체가 2차 과부하를 만든다. 같은 논리로 스케줄러 자신이 멈추면 스케줄러에 의존하는 모든 인스턴스가 함께 통제 불능에 빠진다 — 그래서 스케줄러에도 인스턴스 로컬 제한 + 고정 강등 규칙이라는, 스케줄러 없이도 도는 로컬 하한책이 필요하다.

접속 계층의 세 관문과 요청 메타데이터 표

접속 계층에서 세 관문을 차례로 좁혀 간다: 전역 제한 → 사용자별 제한 → 단건 길이 검사. 세 번째 관문이 "요청 수 기준 제한으로는 초장문 컨텍스트를 식별할 수 없다"는 구멍을 정확히 메운다. 초장문·초고빈도 요청은 직접 차단하거나 경량화 모델로 우회시킨다.

이 모든 계층(제한·스케줄링·강등·격리)이 하나의 요청 메타데이터 표를 함께 읽고 쓸 때만 협업이 성립한다.

필드역할
request_id전 링크 추적과 위치 파악
업무 우선순위스케줄링·강등의 근거
모델 버전주모델 / 증류모델 구분
자원 쿼터메모리·연산력 상한
처리 상태대기 / 실행 / 타임아웃 종료

자원 격리도 이 표에 근거한다: 단건 요청의 메모리·실행 시간에 상한을 두고, 초과 시 자동 종료해 자원을 즉시 해제한다. 이상 요청이 멈춰 자원을 계속 붙잡는 문제의 유일한 하한책이다.

핵심 통찰

  1. GPU 사용률이 낮다는 것은 병목이 없다는 뜻이 아니다 — 사고 현장의 사용률은 60%, 메모리는 70%였지만 이미 눈사태가 났다. 병목이 "연산력 이전"의 진입 큐에 있었기 때문이다. 고동시성 문제를 진단할 때는 반드시 대기 시간과 추론 시간을 먼저 분리해야 하며, 그러지 않으면 모든 증설이 허공을 친다.
  2. 증설은 처리량을 늘릴 뿐 스케줄링을 고치지 못한다 — 3배 트래픽에 GPU를 2배 늘렸는데 타임아웃률이 오히려 치솟았다는 것은, 스케줄링 입도가 어긋난 상태에서는 늘어난 연산력이 대기 중인 요청에 올바르게 분배되지 못한다는 뜻이다. 기계를 늘리는 것은 "스케줄링 자체가 맞다"는 전제 위에서만 유효하다.
  3. 타임아웃은 스스로 새 트래픽을 만든다 — 사용자가 로딩 화면을 보면 새로고침하고 재질문한다. 즉 LLM류 대화형 제품의 과부하는 선형 악화가 아니라 정방향 피드백형 붕괴이며, 모든 강등 전략은 사용자가 재시도를 시작하기 전에 발효되어야 한다.
  4. 추론 요청은 태생적으로 동등하지 않으므로 "건수" 단위 통치는 반드시 왜곡된다 — 초장문·대량 출력 요청 하나의 연산량과 메모리 점유는 짧은 문답 수십 건과 맞먹을 수 있다. 제한 기준은 "요청 수"에서 "입출력 token 총량 / 예상 계산량"으로, 격리 전략은 "균등 분배"에서 "대형 요청 단독 스케줄링"으로 바뀌어야 한다.
  5. 지연과 처리량은 먼저 가격이 매겨져야 스케줄링될 수 있다 — 고우선 실시간 큐의 원칙은 "처리량 일부를 희생해서라도 체감을 지킨다", 저우선 배치 큐의 원칙은 "시간으로 비용을 산다"다. 같은 GPU 위에서 상충하는 두 목표를 돌리는 유일한 방법은 어느 링크가 무엇을 희생할지 먼저 선언하는 것이다.
  6. 강등에는 계단이 있어야 하며, 눈사태는 계단 없는 강등이다 — "경량화 모델 → 출력 길이 축소 → 핫 캐시 답변" 사슬은 가용성을 여러 등급으로 쪼개 단계적으로 포기할 수 있게 만든다. 계단이 없는 시스템은 "전부 좋음"과 "전부 붕괴" 두 상태만 갖는다.
  7. 가장 싼 연산력은 소모하지 않는 연산력이다 — 핫 문답 캐시는 모델 호출 자체를 없애고, KV 캐시 재사용은 단일 턴 비용을 절반 이상 줄인다. 비용 제약 아래에서는 호출을 아끼는 것이 호출을 최적화하는 것보다 언제나 우선한다.
  8. 용량 계획은 피크가 오기 전에 끝나 있어야 한다 — 자동 확장에는 분 단위 지연이 있고, 사고 중 자원 선점은 이미 늦다. 사전에 전용 자원 풀을 예약하고 비핵심 배치 작업을 꺼두는 것만이 유일하게 유효한 사전 조치다.
  9. 장애는 2차 장애를 만든다 — 노드 장애로 일부 인스턴스가 탈락하면 잔여 인스턴스 압력이 급증해 2차 과부하가 온다. 같은 이유로 중앙화된 스케줄러 자체가 멈추면 전체 클러스터가 함께 통제 불능에 빠지므로, 모든 중앙 구성 요소에는 "그것이 멈추면 어떻게 되는가"를 물어야 한다.
  10. 요청 메타데이터 한 장이 있어야 여러 전략이 하나의 체계로 묶인다request_id, 업무 우선순위, 모델 버전, 자원 쿼터, 처리 상태 다섯 필드가 없으면 우선순위 스케줄링은 누구를 우선할지, 자원 격리는 누구를 제한할지, 강등은 누구를 지킬지 알 수 없다. 이 표가 "개별 전략들"을 "하나의 체계"로 바꾸는 지점이다.

엔지니어링 실전 Tips

  • 큐를 먼저 보고, 카드는 그다음에 볼 것: 진입 큐 길이와 대기 시간을 GPU 사용률과 나란히 1급 지표로 만들 것. 사고의 모든 단서가 "요청이 진입 큐에 쌓였다"는 한 줄에 있었다.
  • "숨은 과부하"를 모니터링 지표에 넣을 것: 트래픽이 불균등할 때 요청 수는 정상인데 token 총량이 폭증할 수 있다. 요청 수 기준 제한 임계값은 이때 완전히 무력화된다.
  • 고우선 큐의 독점은 반드시 물리적 격리여야 한다: 우선순위 정렬만으로는 배치 작업이 여전히 카드를 가득 채울 수 있다. "일부 GPU 연산력 독점 배정"이라는 표현 그대로 하드 격리로 구현할 것.
  • 대형 요청의 위해는 "느림"이 아니라 "차단"이다: 대형 요청 하나가 카드 메모리를 다 먹으면 대가는 자신의 지연이 아니라 그것에 막힌 요청의 수로 계산해야 한다.
  • 연속 배치의 두 파라미터는 시소 관계다: 최대 대기 시간을 늘리면 처리량은 오르고 지연도 오른다. 최대 배치 크기를 늘리면 처리량은 오르고 단일 배치 메모리 압박도 오른다. 먼저 지연 SLA를 정하고 그 제약 안에서 처리량 최댓값을 구할 것.
  • 강등은 자동으로 발동해야 한다: 임계값 초과 시 사람이 스위치를 누르는 방식은 눈사태 속도 앞에서 의미가 없다.
  • 단건 요청에 실행 시간 상한을 걸고 강제 종료할 것: 이상 정지의 유일한 하한책이다. 강제 종료가 없으면 멈춘 요청이 메모리를 계속 붙잡아 어떤 제한도 소용없다.
  • 스케줄러 자체에도 의존성 없는 로컬 하한책을 둘 것: 인스턴스 로컬 제한 + 고정 강등 규칙, 원격 설정을 읽지 않는 형태로. 중앙화 스케줄링은 성능 최적해이고 로컬 하한책은 가용성 최저선이며 둘 다 있어야 한다.