- Published on
SageMaker 서버리스 429 스로틀링 해결 가이드
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버리스 추론 엔드포인트를 붙이고 부하 테스트를 돌리면, 생각보다 빨리 429 Too Many Requests(또는 SDK 예외로 ThrottlingException)를 만나는 경우가 많습니다. 특히 SageMaker Serverless Inference는 “서버를 관리하지 않는다”는 장점과 맞바꿔, 순간 트래픽(버스트) 과 동시성(Concurrency) 에 민감합니다.
이 글은 429를 단순히 “리트라이 넣으면 됨”으로 끝내지 않고, 왜 발생하는지, 어떤 레버를 어떤 순서로 조정해야 하는지, 재발을 막기 위한 관측/아키텍처 패턴까지 실전 기준으로 정리합니다.
1) 429가 의미하는 것: 어디서 스로틀링되나
SageMaker Serverless에서 429는 크게 두 층에서 발생합니다.
런타임(Invoke) 계층 스로틀링
InvokeEndpoint호출이 서버리스 용량(동시 실행) 한도를 초과- 또는 순간적인 용량 확장이 따라오지 못해 큐잉/거절
AWS API(제어 플레인) 스로틀링
- 엔드포인트 생성/업데이트, 설정 조회 등 관리 API 호출이 과도
- 일반적인 추론 트래픽에서는 보통 1번이 핵심입니다.
실무에서는 429가 뜰 때 CloudWatch 지표와 응답 헤더/로그로 “Invoke 쪽인지, API 쪽인지”를 먼저 가릅니다. Invoke 쪽이면 동시성/버스트/워크로드 형태가 원인인 경우가 대부분입니다.
2) 서버리스 모델: 동시성·버스트·콜드스타트의 삼각관계
SageMaker Serverless Inference는 요청이 들어올 때 컨테이너를 띄워 처리합니다. 여기서 429가 잘 나는 패턴은 다음과 같습니다.
- 짧은 시간에 요청이 몰림: 초당 요청 수(RPS)는 낮아도, 특정 1~2초 구간에 몰리면 동시성이 순간적으로 튀어 429가 발생합니다.
- 요청 처리 시간이 길다: 모델 추론이
p95기준 2초라면,RPS 50일 때 필요한 동시성은 대략50 * 2 = 100입니다. 처리 시간이 길수록 같은 RPS에서도 동시성 요구량이 커집니다. - 콜드스타트: 새 컨테이너가 뜨는 동안 기존 용량으로만 처리하다가 거절이 발생할 수 있습니다.
즉, 429는 “초당 몇 건”보다 동시성 요구량과 버스트 형태가 본질입니다.
3) 진단 체크리스트: 원인을 수치로 확정하기
아래 순서로 원인을 좁히면 빠릅니다.
3.1 CloudWatch에서 확인할 지표
Invocations/Invocation4XXErrors/Invocation5XXErrorsModelLatency/OverheadLatency- (가능하다면) 429 발생 시각의
p95~p99지연
해석 팁:
ModelLatency가 길수록 동시성 요구량이 커져 429가 늘어납니다.OverheadLatency가 튀면 콜드스타트나 플랫폼 오버헤드가 의심됩니다.
3.2 클라이언트에서 동시에 몇 개를 날리는지 확인
부하 테스트 도구나 애플리케이션 레벨에서 다음이 흔한 원인입니다.
- 워커 수가 과도함(예: FastAPI
gunicorn워커*스레드*비동기 호출이 곱으로 증가) - 배치 작업이 크론에 의해 같은 시각에 한꺼번에 실행
- 재시도 로직이 동기화된 폭풍을 만들어 429를 더 키움(동일한 backoff, 동일한 타이밍)
3.3 “요청당 처리시간”으로 필요한 동시성 계산
대략적으로:
- 필요한 동시성
C ≈ RPS * p95_latency_seconds
예:
RPS = 30,p95 = 1.8s면C ≈ 54- 여기에 버스트(피크)와 콜드스타트 여유를 더하면 더 필요합니다.
이 계산이 나오면, 이제 해결은 동시성 공급을 늘리거나, 동시성 수요를 줄이거나, 버스트를 완화하는 쪽으로 정리됩니다.
4) 해결 전략 1: 동시성 공급 늘리기(가장 직관적)
4.1 Provisioned Concurrency를 설정
서버리스에서 429를 가장 확실히 줄이는 방법은 Provisioned Concurrency로 “항상 떠있는” 용량을 확보하는 것입니다. 콜드스타트도 줄고, 버스트도 흡수됩니다.
다만 비용이 증가하므로, 아래처럼 접근하는 게 안전합니다.
- 평소 트래픽의
p50~p75수준을 provisioned로 커버 - 피크는 리트라이/큐잉/레이트리밋으로 완충
AWS CLI 예시
아래는 개념 예시입니다(실제 파라미터/명칭은 환경에 맞게 조정).
aws sagemaker update-endpoint \
--endpoint-name my-serverless-endpoint \
--endpoint-config-name my-config-with-provisioned
엔드포인트 설정에서 provisioned 값을 올렸는데도 429가 계속되면, 다음 두 가지를 의심하세요.
- 애플리케이션이 순간적으로 provisioned를 훨씬 초과하는 동시성을 만든다
- 요청 처리 시간이 길어 동시성 요구량이 계산보다 커졌다(입력 토큰 증가, 프롬프트 길이 급증 등)
4.2 모델 최적화로 처리시간을 줄여 “동시성 수요”를 낮추기
동시성을 늘리는 게 비용이라면, 처리시간 감소는 비용과 안정성을 동시에 잡는 경우가 많습니다.
- 입력/출력 토큰 상한 설정
- 프롬프트 템플릿 슬림화
- 모델 경량화(quantization 등)
- 배치 추론이 가능하면 micro-batching(서버리스 특성상 제약이 있을 수 있음)
LLM 계열이라면 “응답을 길게 생성할수록 동시성 요구량이 폭증”합니다. 토큰 상한을 두는 것만으로도 429가 눈에 띄게 줄어듭니다.
5) 해결 전략 2: 버스트를 완화하기(429의 핵심)
5.1 애플리케이션 레벨 레이트 리미터
서버리스는 “무한 확장”이 아니라 “제한된 속도로 확장”합니다. 따라서 클라이언트에서 동시 요청 수를 제한하는 게 매우 효과적입니다.
Node.js에서 세마포어로 동시성 제한 예시:
import { Semaphore } from "async-mutex";
const sem = new Semaphore(20); // 동시 20개로 제한
async function invokeWithLimit(fn) {
const [value, release] = await sem.acquire();
try {
return await fn();
} finally {
release();
}
}
이때 포인트는 “RPS 제한”이 아니라 동시성 제한입니다. 지연이 늘면 동시성이 자연히 늘어나기 때문에, 동시성을 직접 잡는 편이 429에 더 직결됩니다.
5.2 큐 기반 흡수(SQS 등)
웹 트래픽이 아니라 배치/이벤트성 요청이라면, SQS로 흡수하고 워커가 일정 속도로 처리하는 패턴이 가장 안정적입니다.
- Producer: 요청을 SQS에 적재
- Consumer: 워커가 메시지를 꺼내
InvokeEndpoint - 워커 수 혹은 워커 내부 동시성으로 처리량 제어
이 패턴은 429뿐 아니라 다운스트림 장애에도 강합니다.
6) 해결 전략 3: 리트라이를 “제대로” 넣기(재시도 폭풍 방지)
429는 재시도로 회복되는 경우가 많지만, 잘못 넣으면 오히려 429를 증폭시킵니다.
핵심 규칙:
- 지수 백오프 + 지터(jitter) 를 반드시 적용
- 최대 재시도 횟수/최대 대기 시간을 둔다
- 429가 연속되면 서킷 브레이커로 빠르게 실패시키고 상위에서 큐잉/대체 경로로 전환
Python boto3에서 Config로 재시도 설정 예시:
import boto3
from botocore.config import Config
config = Config(
retries={
"max_attempts": 8,
"mode": "standard",
},
read_timeout=60,
connect_timeout=5,
)
smr = boto3.client("sagemaker-runtime", config=config)
def invoke(payload: bytes):
return smr.invoke_endpoint(
EndpointName="my-serverless-endpoint",
ContentType="application/json",
Body=payload,
)
여기에 애플리케이션 레벨 지터를 추가하면 더 안전합니다.
import random
import time
def backoff_sleep(attempt: int):
base = 0.2
cap = 5.0
sleep = min(cap, base * (2 ** attempt))
sleep = sleep * (0.5 + random.random())
time.sleep(sleep)
리트라이가 길어질수록 상위 타임아웃(예: API Gateway, ALB, 클라이언트 타임아웃)과 충돌할 수 있으니, 전체 요청 예산을 먼저 정하고 그 안에서 재시도를 설계해야 합니다. 스트리밍 응답을 쓰는 경우 프록시 타임아웃/버퍼링 이슈도 함께 보게 되는데, 이 부분은 LLM SSE 스트리밍 499 502 급증과 응답 끊김을 잡는 프록시 튜닝 체크리스트도 같이 참고하면 좋습니다.
7) 해결 전략 4: 캐싱과 중복 제거로 호출 자체를 줄이기
서버리스 429는 “처리량 부족”이기도 하지만, 애초에 불필요한 호출이 많아서 생기는 경우도 많습니다.
- 동일 입력에 대한 결과 캐싱(예: Redis)
- idempotency key 기반 중복 요청 제거
- 프론트엔드에서 디바운스/스로틀 적용
특히 LLM/텍스트 생성은 입력이 같으면 출력이 같도록 설정(온도, 시드 등)할 수 있는 경우가 많아 캐싱 효율이 좋습니다.
8) 운영 관점: 429를 재현 가능하게 만들고, 알람을 설계하기
8.1 부하 테스트는 “RPS”가 아니라 “동시성 프로파일”로
테스트 시나리오를 두 개로 나누세요.
- Steady state: 일정 RPS로 10~30분 유지
- Burst: 1~5초에 몰아서 요청(실서비스와 비슷한 burst 크기)
서버리스의 취약점은 두 번째에서 드러납니다.
8.2 알람 지표
Invocation4XXErrors중 429 비율ModelLatency p95/p99- 애플리케이션에서 “재시도 횟수”와 “최종 실패율”
429 자체는 일시적일 수 있지만, 재시도 후에도 실패하는 비율이 올라가면 사용자 영향이 확정입니다.
9) 자주 하는 실수 5가지
- 동시성 제한 없이 비동기 호출을 무한히 날림
- 429 재시도에 지터가 없어 재시도 폭풍이 발생
- 타임아웃 예산 없이 재시도만 늘려 전체 실패율이 증가
- 모델 지연이 늘었는데(입력 토큰 증가) 동시성 계산을 업데이트하지 않음
- 버스트 트래픽을 고려하지 않고 평균 RPS만 보고 용량을 맞춤
10) 추천 해결 순서(현업에서 가장 빨리 먹히는 플랜)
- 클라이언트 동시성 제한을 먼저 건다(즉시 효과)
- 지수 백오프 + 지터로 재시도를 정비한다(2차 방어)
p95 latency기반으로 필요한 동시성을 계산하고, Provisioned Concurrency를 최소 비용으로 맞춘다- 여전히 피크가 아프면 큐(SQS)로 흡수하거나, 트래픽 셰이핑(레이트리밋)을 API 게이트웨이/프록시 계층에 둔다
- 장기적으로는 모델 지연을 줄이는 최적화와 캐싱으로 호출량 자체를 줄인다
권한/정책 문제로 호출이 실패하며 4XX가 섞여 보인다면(특히 테스트 환경에서) 429와 별개로 IAM을 먼저 정리해야 합니다. 이때는 Terraform apply 후 403? AWS IAM 권한추적 8단계처럼 “정확히 어디서 막히는지”를 추적하는 루틴이 도움이 됩니다.
결론
SageMaker 서버리스의 429는 단순 용량 문제가 아니라, 동시성 수요(지연 포함) 와 버스트 형태가 만드는 시스템적 현상입니다. 가장 빠른 처방은 동시성 제한과 올바른 재시도(지터)이고, 가장 확실한 처방은 provisioned 용량과 큐 기반 흡수입니다.
다음 단계로는 현재 워크로드의 p95 latency와 피크 버스트를 기준으로 “필요 동시성”을 산출하고, 그 수치를 중심으로 provisioned/레이트리밋/큐를 조합해 안정성과 비용을 함께 최적화하는 것을 권합니다.