[V1] Vertex AI 부하 상황공유 - 100-hours-a-week/6-nemo-ai GitHub Wiki
Vertex AI 호출 안정화 회고
📌 목적
우리는 FastAPI 기반 AI 서버에서 Google Vertex AI를 통해 모임 설명, 커리큘럼 등을 생성하는 기능을 개발하고 있다.
이 과정에서 Google의 QPS 제한, API 에러, 느린 응답 등으로 인해 다양한 기술적 문제를 겪었고,
그에 따라 구조적 개선을 반복해왔다.
✅ 문제 요약
번호 |
내용 |
1 |
Vertex AI에 다수 요청 시 QuotaExceeded , DeadlineExceeded 오류 발생 |
2 |
모델 호출 실패 시 예외가 사용자에게 노출됨 |
3 |
과도한 요청이 몰릴 경우 응답 지연 또는 일부 실패 발생 |
4 |
짧은 시간에 많은 요청이 몰릴 경우 처리가 지연되거나 튕겨나감 |
5 |
AI 응답에 필수 항목이 없으면 빈 문자열이 반환됨 |
🧪 시도한 해결 방법
번호 |
해결 시도 |
1 |
ThreadPoolExecutor 를 도입하여 동기 Vertex API를 병렬 처리 |
2 |
generate_content() 내부에 try/except + fallback 문구([EMPTY] , [ERROR] ) 리턴 |
3 |
smart_generate() 에서 RateLimitedExecutor 로 요청 빈도와 병렬 처리 제한 |
4 |
QueuedExecutor 를 설계해 요청을 줄 세우고 천천히 실행하도록 구조 설계 착수 |
5 |
파싱 실패 시 빈 문자열 리턴 + 로깅 처리 |
🧰 구현한 것들 (2025.05.14 기준)
1. smart_generate()
통합 설계
- 하나의 함수(
smart_generate
)로 동기/비동기 환경 모두에서 Vertex 호출을 처리하도록 설계
- 내부적으로
run_in_executor()
를 사용해 동기 호출을 비동기화
python
CopyEdit
async def smart_generate(prompt: str):
return await loop.run_in_executor(vertex_executor, generate_content, prompt)
2. RateLimitedExecutor
설정
- 동시에 실행 가능한 요청 수와 초당 허용 요청 수를 제한하기 위해 설정
python
CopyEdit
vertex_executor = RateLimitedExecutor(max_workers=3, qps=1)
- 그러나 쓰레드 기반이라 완전한 직렬 처리에는 실패
3. generate_description
, generate_plan
, extract_tags
전체 비동기화
- 모든 AI 호출 관련 함수들을
async def
로 바꾸고 내부에서 await smart_generate(...)
를 사용하도록 일괄 리팩터링
- 파싱 실패 시 빈값을 반환하고 에러 로그를 기록하여 서버 장애를 방지
4. build_meeting_data()
리팩터링
- 동기 방식으로 동작하던
build_meeting_data()
를 async def
로 전환
- 내부 모든 서비스 호출에
await
적용
- 테스트용 실행 구조도
asyncio.run(...)
으로 변경
5. FastAPI 라우터 리팩터링
- 라우터 함수
create_meeting()
에서 await build_meeting_data(...)
적용
- 유해성 검증, 에러 핸들링 구조도 예외 누수 없이 감싸도록 정비
🔍 현재 구조의 흐름 (Mermaid 다이어그램)
flowchart TD
A[User Request (FastAPI)] --> B[Router (async def)]
B --> C[await build_meeting_data()]
C --> D[generate_description / plan / tags]
D --> E[await smart_generate(prompt)]
E --> F[run_in_executor]
F --> G[RateLimitedExecutor (max_workers=3, qps=1)]
G --> H[generate_content(prompt)]
H --> I[Call Vertex AI (sync)]
I --> J[Vertex AI Response]
J --> K[Parse & return result]
K --> L[Return APIResponse to user]
❗ 여전히 발생하는 문제
RateLimitedExecutor
는 "줄을 세우는 구조"가 아님
- 여러 쓰레드가 동시에 Vertex AI에 요청을 보내는 구조임
- Google은 **"실제로 동시에 도달한 요청"**만 가지고 판단함
- 따라서
QuotaExceeded
, DeadlineExceeded
는 여전히 발생 가능함
🧠 쉽게 정리하면
- 우리는 요청을 줄 세운다고 생각했지만,
- 실제로는 3명이 동시에 Vertex AI에 요청을 보낸 것
- Google은 한 명씩만 들어오라고 제한함 → 결과:
QuotaExceeded
🚫 제외한 방법
- GCP 콘솔에서 쿼터 상향 요청은 비용/절차/유연성 문제로 제외
✅ 다음 단계 계획
항목 |
설명 |
직렬 큐 구조 |
asyncio.Queue 기반으로 요청을 실제로 순차 처리 |
단일 워커 설계 |
단 하나의 워커만 초당 1건씩 Vertex 호출 |
응답 재시도 로직 |
QuotaExceeded 발생 시 지연 후 자동 재시도 |
모니터링 추가 |
현재 요청 수, 대기열 수, 실패 수 확인 API 도입 |
🔚 현 상태 평가
항목 |
결과 |
안정성 |
구조적 안정성은 확보됨 (fallback, 예외 처리) |
응답 속도 |
모델 호출 포함 평균 14~18초 |
요청 성공률 |
제한 내에서는 안정적, 초과 시 실패 |
리팩터링 여지 |
큐 직렬화, 재시도, 캐시 전략 등 여지 많음 |