10/6 정각 피크 agent 슬롯 만석 — CPU 기준 오토스케일 지연으로 AI 세션 배정 실패 분석수정 미적용

마지막 업데이트 2026-10-07

2026-10-07 사건 2026-10-06 18:03–18:18, 19:02–19:18 apps/livekit-agent · apps/web · ECS ppi-livekit-prod-agent · LiveKit 서버

요약

최종 세션 실패 17건(로그 확정) + 1건(로그 유실, 서버 로그로 추정) = 18건, 아동 13명. AI 스텝 시작이 자동 재시도 1회까지 실패해 포기된 건수다. 배정 실패한 시도는 130건이었고, 그중 87%는 재시도나 다음 시작으로 회복됐다.

원인: 18시·19시 정각 수업 시작으로 AI job 수요가 슬롯 상한에 닿았다(19시: 아동 36명 vs 슬롯 34개 = 태스크 17대 × 워커당 2). 워커가 새 job을 받을지는 job 수로 정해지는데, 오토스케일은 CPU 평균에 반응하는 것으로 보인다. 슬롯이 다 차도 CPU 평균은 40%대라 증설 결정이 13~14분 늦었다(18:16, 19:17). 태스크 기동 자체는 2분이었다.

8/25 사건과의 차이: 8/25는 LiveKit 노드 하나에 워커가 쏠린 "노드 단위 포화"였다. 10/6은 주요 노드 3개가 같은 워커 23대를 공유했고 실패도 고르게 퍼져 있어(41/26/27) 풀 전체 포화다. 좀비 job(1시간 초과)은 0개, CPU 최대치는 87%로 CPU 포화도 없었다.

시각화: 동시 job vs 슬롯 / 슬롯 사용률 vs CPU 평균 차트

1. 시간순 인과

// 19시 구간 (KST). 18시도 같은 패턴: 18:03 첫 실패 → 18:16 증설 결정 → 18:18 반영 → 실패 종료
18:16  CPU 평균 35~39%가 13분 지속 → DesiredTaskCount 12 → 17   (슬롯 34)
18:35  18:30 수업 job 20개 → 17대로 버팀 (실패 1건)
19:00  정각 수업 동시 시작 (아동 36명, 그룹 12개 포함) → 동시 job 0 → 32 (19:04)
19:03  워커 17대 중 15대 "worker is at full capacity" (load 0.99 = 2/2 × 0.99)
19:02~19:18  방 생성 순간 빈 슬롯이 없으면 no workers with sufficient capacity
             → 그 방은 끝까지 미배정 → 클라이언트 10초 readiness timeout → 재시도 1회 (새 방)
             → 재시도도 지면 Failed to start session (handler) = 최종 실패
19:13  워커 17/17 전원 만석, 동시 job 34/34
19:05~19:16  슬롯 사용률 94~100% / CPU 평균 41~47%  ← 오토스케일이 보는 값은 여유
19:17  DesiredTaskCount 17 → 23  (pending 6)
19:18  worker registered ×6 (19:18:08~19:18:20)
19:19  RunningTaskCount 23 (슬롯 46) → 이후 배정 실패 0건

2. 증상과 범위

최종 실패 18건 (아동 13명)

아동회차건수시각진행자 기록
김단31319:10, 19:11, 19:15스텝 전환 후 동작 없음, 강제퇴장
백찬혁44219:08, 19:14*no message yet, 강제퇴장 2회, 수업 중단
김하준1219:14, 19:18대화시작 안 나와 이전 활동 갔다 복귀
박하연14219:07, 19:16-
정유건14118:15티키 멘트 시작 안 됨
김가빈61119:13세션 자동/수동 실행 안 됨
백선우16119:11미배정 뒤 19:12 재배정 복구
손경훈34119:07-
박주안10119:09-
김도현**21119:09-
김권후6119:13-
김재율6119:17-
안지훈20119:17***-

* 재시도 예약(19:14:34) 직후 세션 로그가 끊겨 handler 로그 없음. 서버 로그에서 재시도 방도 no workers with sufficient capacity로 실패하고 19:14:46 퇴장 확인 → 추정 1건. ** 21회차 후보가 2명(ddab728c, cd1cf2ef)이라 매칭 미확정, 최종 실패는 cd1cf2ef. *** 세션 로그에는 19:20:14로 찍혔지만 아동 단말 시계가 3분 빨랐다(서버 로그상 두 방 실패 19:16:51·19:17:03, 퇴장 19:17:14). 표의 시각은 모두 서버 시각 기준으로 맞췄다.

진행자 기록 7명 중 6명이 최종 실패 아동이다. 나머지 1명(송지은 15회차, "티키 말 안 함")은 시도 실패 2건이 모두 재시도로 성공했다. 재시도로 생긴 약 11초 공백이 체감됐을 가능성이 있다(추정).

배정 실패 130건의 분해 (18:00~20:00)

배정 실패한 시도 130 (세션 로그 129 + 로그 미수집 1)
 ├  41  영상 스텝 중 미리 시작(prepare) 실패 → AI 스텝에서 다시 시작 (지연만)
 ├  69  AI 스텝 1차 실패 → 자동 재시도
 │      ├ 51 재시도 성공 (약 11초 지연)
 │      ├ 17 재시도도 실패 → 최종 실패
 │      └  1 백찬혁 19:14 (로그 유실, 서버 로그로 최종 실패 추정)
 ├  17  위 재시도 시도 자체의 실패
 └   2  새 요청으로 교체(superseded) → 이어서 성공

세션 시도(방) 1,193 / 배정 실패 130 (10.9%) / 실패 겪은 아동 46 / 123명
30분 구간: 18:00 시도 272 실패 27 · 18:30 190/1 · 19:00 446/102 · 19:30 285/0

3. 코드 경로 — 배정 실패가 최종 실패가 되는 과정

위치동작
apps/livekit-agent/agent.py:367 _ppi_agent_loadload = active_jobs / PPI_AGENT_MAX_JOBS_PER_WORKER(2) × 0.99, threshold 0.99. CPU 실측은 load에 들어가지 않는다 → job 2개면 무조건 만석
LiveKit 서버 agentservice.go방 생성 시 job 배정을 1회 시도. 후보가 바닥나면 no worker available to handle job으로 폐기, 나중에 슬롯이 비어도 그 방에는 다시 배정하지 않는다(실패 방 104개 중 이후 배정 0)
apps/web/lib/voice-agent/livekit-client-session.ts:80LIVEKIT_AGENT_READY_TIMEOUT_MS = 10000 — agent 대기 10초
apps/web/lib/voice-agent/livekit-readiness-retry.ts:1READINESS_RETRY_MAX_ATTEMPTS = 1, 대기 300~1200ms. readiness timeout일 때만 새 방으로 재시도 → 최악 약 21초
apps/web/entities/guest-session/model/use-ai-session.ts:2926 handleStartSession재시도 소진 시 readiness_retry_exhausted → Failed to start session (handler) + alert("세션 시작에 실패했습니다.")(:2991). 이후 그 스텝은 진행자 수동 시작이나 스텝 이동 전까지 재시도하지 않는다
영상 스텝 prepare 경로재시도 없이 AI session prepare failed로 끝나고, AI 스텝 진입 시 위 정식 시작을 다시 한다

4. 왜 워커가 안 늘었나

시각DesiredPendingRunningCPU 평균의미
18:03~18:151201234~39%만석 워커 9~11/12인데 결정 없음
18:161701235.7%증설 결정
18:18170172분 뒤 기동
19:03~19:161701741~47%만석 워커 14~17/17인데 결정 없음
19:172361743.8%증설 결정
19:19230232분 뒤 기동

job 1개 ≈ 태스크 CPU 22%(19:13 job 34 / 태스크 17 / CPU 평균 45%). 그래서 슬롯이 다 차도 CPU 평균은 50%를 넘기 어렵다. CPU 기준 정책은 만석을 "여유 있음"으로 본다.

AWS 기본 target tracking이면 보통 3분 안에 반응한다. 13~14분은 평가 기간이 긴 알람이나 step scaling일 가능성이 있다(추정, 정책 원문 미확인).

수업 수가 많았던 날인가

19시 아동19시 직전 슬롯19:00~19:20 배정 실패
9/29 (화)약 32명40 (20대)0건
10/6 (화)36명 (그룹 12개 포함)34 (17대)70건

수요가 슬롯보다 적으면 실패가 없었다. 19시 직전 태스크 수는 그 전 시간대 CPU로 결정돼, 수업 일정과 무관하게 운에 따라 모자랄 수 있다. 그룹 수업도 아동마다 AI 방을 따로 쓴다.

5. 기각된 가설

가설판정근거
좀비 job 슬롯 점유 (9/15형)기각1시간 넘게 산 job 0개 (job 1,433개 시작·종료 전수 대조)
CPU 포화기각단일 태스크 최대 87%, VAD inference is slower than realtime 27건(최대 2.4초), 백찬혁 방 0건
노드 단위 포화 (8/25형)기각주요 노드 3개 모두 같은 워커 23대에 배정, 실패 41/26/27로 고르게 분포
사후 점유 20초 (8/25 기여 요인)해당 없음PR #1027의 requesting job shutdown after session close가 prod에 찍힘 → 이미 반영
agent 프로세스 기동 지연기각process initialized 중앙값 0.5초, 최대 3.8초
아동 단말 문제기각실패 방은 agent에 job 요청 자체가 도착하지 않음(서버 배정 단계)
함정: not dispatching agent job since no worker is available은 JT_PUBLISHER 타입이라 정상 방에서도 매번 찍힌다. 판별자로 쓰면 안 된다. 진짜 신호는 JT_ROOM의 no worker available to handle job이다. failed to assign job to worker(124줄)도 방 115개 중 32개는 다른 워커로 재시도해 성공했으므로 실패 건수로 세면 안 된다.

6. 개선 제안 (미적용)

  1. 시간 예약 스케일링: 정각 수업 전(예: :55)에 태스크 수를 "예상 아동 수 ÷ 2 + 여유분"으로 올린다. 그룹 수업이 몰린 날이 특히 위험하다.
  2. 스케일링 기준 변경: CPU 대신 "활성 job ÷ 슬롯" 커스텀 메트릭으로 바꾼다.
  3. CPU 정책을 유지한다면 목표값을 20~25%대로 낮추고 평가 기간을 줄인다.
  4. 워커당 슬롯 2 → 3: CPU 최대 87%라 여유가 크지 않아 부하 검증이 먼저 필요하다.
  5. 관측: 최종 실패는 아동 브라우저 로그에만 남는다(서버 로그 3종 0건). relayLiveKitDiagnosticToSocket처럼 소켓으로 보내면 서버에서 바로 집계할 수 있다.

7. 판별 방법과 데이터 출처

미확인: 오토스케일 정책 원문(대상 메트릭·목표값·평가 기간)은 레포에 없고 viewer 권한으로 조회되지 않는다. ECS 서비스 ppi-livekit-prod-agent의 Auto Scaling 탭에서 확인해야 확정된다. 이윤건(20:10 실패)은 로그 범위 밖이라 확인하지 않았다.