Published on

SageMaker 엔드포인트 5xx·타임아웃 원인 9가지

Authors
Binance registration banner

SageMaker 실시간 엔드포인트는 InvokeEndpoint 호출이 성공하더라도 내부적으로는 컨테이너, 모델 로딩, 오토스케일링, 네트워크, 그리고 애플리케이션 코드까지 여러 계층을 통과합니다. 그래서 5xx 와 타임아웃은 “한 가지 원인”이 아니라, 요청 경로 어디에서 병목이나 실패가 발생했는지를 좁혀가는 방식으로 접근해야 합니다.

이 글에서는 운영에서 자주 마주치는 SageMaker 엔드포인트 5xx·타임아웃 원인 9가지를 증상, 확인 지표, 해결책 중심으로 정리합니다.

참고로 타임아웃 이슈는 로드밸런서나 게이트웨이의 유휴 타임아웃과도 결합됩니다. EKS 환경에서 504 가 60초로 끊기는 패턴은 이 글도 함께 보면 좋습니다: EKS ALB Ingress 504(60초) idle_timeout 해결

먼저: 5xx·타임아웃을 어디서 확인하나

진단은 “요청이 어디서 실패했는지”를 분리하는 것부터 시작합니다.

  • CloudWatch 지표(엔드포인트)
    • Invocations, Invocation4XXErrors, Invocation5XXErrors
    • ModelLatency, OverheadLatency
    • CPUUtilization, MemoryUtilization, GPUUtilization(GPU 인스턴스)
  • CloudWatch Logs
    • /aws/sagemaker/Endpoints/엔드포인트명
    • 컨테이너 stdout, stderr, 프레임워크별 서버 로그(TorchServe, MMS, custom gunicorn 등)
  • SageMaker DescribeEndpoint / DescribeEndpointConfig
    • 인스턴스 타입, 초기 인스턴스 수, 모델 아티팩트, ContainerStartupHealthCheckTimeoutInSeconds

아래 코드는 엔드포인트 지표를 빠르게 조회하는 예시입니다.

import boto3
from datetime import datetime, timedelta, timezone

cw = boto3.client("cloudwatch")
endpoint = "my-endpoint"

end = datetime.now(timezone.utc)
start = end - timedelta(hours=1)

def metric(name, stat="Sum", period=60):
    return cw.get_metric_statistics(
        Namespace="AWS/SageMaker",
        MetricName=name,
        Dimensions=[{"Name": "EndpointName", "Value": endpoint}],
        StartTime=start,
        EndTime=end,
        Period=period,
        Statistics=[stat],
    )["Datapoints"]

print("5XX:", metric("Invocation5XXErrors"))
print("Latency:", metric("ModelLatency", stat="Average"))
print("Overhead:", metric("OverheadLatency", stat="Average"))
  • ModelLatency 가 높으면 “모델 추론 자체”가 느린 경우가 많습니다.
  • OverheadLatency 가 높으면 “컨테이너 바깥 오버헤드” (직렬화, 라우팅, 큐잉, 네트워크, 런타임 준비 등) 가능성이 큽니다.

원인 1) 콜드 스타트: 모델 다운로드·로딩이 느림

대표 증상

  • 배포 직후 또는 오토스케일로 새 인스턴스가 붙은 직후 5xx 또는 타임아웃
  • 특정 시간대(트래픽 스파이크 직후)에만 오류가 집중

확인 방법

  • 엔드포인트 로그에서 모델 로딩 메시지가 오래 지속
  • ModelLatency 보다 OverheadLatency 가 비정상적으로 치솟기도 함

해결책

  • 모델 아티팩트 크기 축소(불필요 파일 제거, 압축 최적화)
  • 컨테이너 시작 시 가중치 로딩을 지연시키지 말고, readiness 이전에 끝내기
  • ContainerStartupHealthCheckTimeoutInSeconds 를 늘려 “헬스체크 실패로 교체 반복”을 막기
import boto3
sm = boto3.client("sagemaker")

# 엔드포인트 설정에서 startup health check timeout을 늘리는 예시
# 실제로는 EndpointConfig를 새로 만들고 UpdateEndpoint 해야 합니다.

print(sm.describe_endpoint_config(EndpointConfigName="my-endpoint-config"))

원인 2) 오토스케일링 지연: 트래픽 급증에 비해 스케일이 늦음

대표 증상

  • 평소엔 정상인데 피크에 Invocation5XXErrors 증가
  • InvocationsPerInstance 가 급격히 상승
  • ModelLatency 가 계단식으로 증가한 뒤 실패

확인 방법

  • Application Auto Scaling 이벤트, CloudWatch 알람 히스토리
  • 스케일 아웃이 발생했는데 실제 인스턴스 준비까지 시간이 오래 걸림

해결책

  • 최소 인스턴스 수를 1 이상으로 유지(항상 웜 상태)
  • 스케일 정책을 보수적으로(더 빨리) 조정
  • 트래픽 패턴이 예측 가능하면 스케줄 기반 스케일링 고려

원인 3) 인스턴스 리소스 부족: CPU·메모리·GPU OOM

대표 증상

  • 간헐적 500 또는 컨테이너 재시작
  • 특정 입력에서만 실패(큰 이미지, 긴 시퀀스 등)
  • 로그에 Killed, OOM, CUDA out of memory 류 메시지

확인 방법

  • CloudWatch MemoryUtilization 급등 후 오류
  • GPU 사용률과 GPU 메모리(프레임워크 로그 또는 커스텀 지표) 확인

해결책

  • 배치 크기 축소, 입력 크기 제한, 토큰 길이 제한
  • 모델 양자화, mixed precision
  • 더 큰 인스턴스 또는 GPU 인스턴스로 변경

이미지/생성 모델 계열은 VRAM 이슈가 특히 잦습니다. 유사한 최적화 아이디어는 이 글도 참고할 만합니다: Stable Diffusion VRAM OOM, xFormers·Tiled VAE로 해결

원인 4) 컨테이너 서버 설정 문제: 워커 수·타임아웃·큐잉

SageMaker는 컨테이너 내부에 웹 서버(예: gunicorn, uvicorn, TorchServe 등)가 떠서 요청을 처리합니다. 여기서 워커 수가 과하거나, 타임아웃이 너무 짧거나, 동시성 제어가 없으면 스레드/프로세스 경합으로 타임아웃이 터집니다.

대표 증상

  • ModelLatency 가 들쭉날쭉하고 tail latency가 큼
  • 동시 요청에서만 실패

확인 방법

  • 컨테이너 로그에서 request queueing, worker timeout, restart 흔적
  • CPU utilization 이 100% 근처에서 고정

해결책

  • 워커 수를 CPU 코어에 맞게 조정
  • 요청당 최대 처리 시간에 맞게 서버 타임아웃 조정
  • 동시 처리량을 제한하고(세마포어), 과부하 시 빠르게 429 로 실패시키는 전략도 고려
# 예: FastAPI에서 동시 처리 제한(간단한 세마포어)
import asyncio
from fastapi import FastAPI, HTTPException

app = FastAPI()
sem = asyncio.Semaphore(4)

@app.post("/invocations")
async def invocations(payload: dict):
    if sem.locked():
        raise HTTPException(status_code=429, detail="busy")
    async with sem:
        # heavy inference
        return {"ok": True}

과부하 제어(레이트리밋, 오토스케일링) 관점은 BentoML 사례지만 패턴은 동일합니다: BentoML 서빙 429 폭주 대응 - 오토스케일·레이트리밋

원인 5) 요청/응답 페이로드 문제: 크기 제한·직렬화 실패

대표 증상

  • 특정 요청만 5xx 또는 4xx 로 실패
  • base64 이미지나 대용량 JSON에서 오류 급증

확인 방법

  • 클라이언트에서 전송한 Content-Type, Content-Length 확인
  • 컨테이너 로그에 JSON decode error, payload too large 등

해결책

  • 입력을 S3에 두고 키만 전달하는 방식으로 변경
  • 압축, 바이너리 포맷(예: application/x-npy) 고려
  • 서버에서 입력 검증을 선제적으로 수행하고 명확히 4xx 로 반환

원인 6) 네트워크/VPC 설정: NAT, 보안그룹, 엔드포인트 접근

VPC에 붙인 엔드포인트가 외부 리소스(S3, ECR, 외부 API)를 호출할 때 NAT 게이트웨이, VPC 엔드포인트, 보안그룹이 꼬이면 “처음엔 되다가” 갑자기 타임아웃이 납니다.

대표 증상

  • 모델 다운로드(S3) 또는 이미지 pull(ECR) 단계에서 멈춤
  • 외부 API 호출이 있는 경우 특정 AZ에서만 타임아웃

확인 방법

  • VPC Flow Logs, NAT 게이트웨이 지표
  • 컨테이너 로그에서 DNS resolve failure, connect timeout

해결책

  • S3 Gateway Endpoint, ECR Interface Endpoint 구성
  • 보안그룹 egress, NACL 점검
  • 외부 호출이 꼭 필요하면 타임아웃/재시도/서킷브레이커 구현

원인 7) 멀티모델 또는 모델 캐시 문제: 디스크 공간·eviction

Multi-Model Endpoint 또는 모델을 동적으로 로딩하는 패턴은 인스턴스 로컬 디스크(EBS) 사용량과 eviction 정책에 영향을 크게 받습니다.

대표 증상

  • 시간이 지날수록 5xx 증가
  • 특정 모델만 유독 느리거나 실패
  • 디스크 부족으로 로딩 실패

확인 방법

  • 로그에서 “no space left on device” 류 메시지
  • 인스턴스 스토리지 사용량(커스텀 지표 필요)

해결책

  • 모델 수를 줄이거나, 모델 크기 최적화
  • 캐시 디렉터리 정리, eviction 전략 설계
  • 엔드포인트를 모델별로 분리하는 것이 더 안정적인 경우도 많음

원인 8) 헬스체크/리드니스 미흡: 살아있지만 준비 안 됨

컨테이너가 프로세스는 떠 있지만 모델이 아직 로딩 중이거나, GPU 초기화가 끝나지 않았는데 트래픽이 들어오면 5xx 가 납니다. 특히 커스텀 컨테이너에서 /ping/invocations 구현이 부실할 때 자주 발생합니다.

대표 증상

  • 배포 직후 5xx 후 안정화
  • 로그에 초기화 중 예외

확인 방법

  • /ping 응답이 너무 빨리 200 을 반환하는지 확인
  • readiness 조건(모델 로딩 완료 여부)을 로그로 남기기

해결책

  • /ping 에서 “모델 로딩 완료”를 확인한 뒤에만 200 반환
  • 초기화 실패 시 빠르게 프로세스를 종료하여 교체가 일어나게 하기
# 간단한 readiness 플래그 예시(Flask)
from flask import Flask, Response, request

app = Flask(__name__)
ready = False

@app.route("/ping", methods=["GET"])
def ping():
    return Response(status=200 if ready else 503)

@app.route("/invocations", methods=["POST"])
def invoke():
    if not ready:
        return Response("not ready", status=503)
    # inference...
    return {"ok": True}

def load_model():
    global ready
    # heavy init...
    ready = True

원인 9) 클라이언트 타임아웃·재시도 폭주: 실패를 증폭

서버가 느려지기 시작하면 클라이언트의 공격적인 재시도(특히 짧은 타임아웃)로 트래픽이 폭증하고, 결국 5xx 와 타임아웃이 “연쇄적으로” 증가합니다.

대표 증상

  • 트래픽이 실제 사용자 수보다 과도하게 튐
  • 서버가 회복 직전에 다시 과부하로 떨어짐

확인 방법

  • 클라이언트 로그에서 동일 요청의 반복
  • Invocations 대비 비즈니스 이벤트 수가 비정상

해결책

  • 클라이언트 타임아웃을 현실적으로 설정(모델 p95 기준)
  • 지수 백오프와 지터 적용
  • 멱등성 키를 도입하거나, 재시도 가능한 에러만 재시도
# boto3 호출에 타임아웃/재시도 정책을 명시하는 예시
import boto3
from botocore.config import Config

cfg = Config(
    read_timeout=70,
    connect_timeout=5,
    retries={"max_attempts": 3, "mode": "standard"},
)
rt = boto3.client("sagemaker-runtime", config=cfg)

resp = rt.invoke_endpoint(
    EndpointName="my-endpoint",
    ContentType="application/json",
    Body=b"{\"text\": \"hello\"}",
)
print(resp["Body"].read())

실전 체크리스트: 15분 안에 범위 좁히기

  1. Invocation5XXErrorsModelLatency / OverheadLatency 를 같은 시간축으로 비교
  2. 오류가 배포/스케일 이벤트와 맞물리는지 확인(콜드 스타트 의심)
  3. CPU·메모리·GPU 지표가 상한에 붙는지 확인(OOM 또는 경합)
  4. 컨테이너 로그에서 OOM , worker timeout, decode error 같은 “명확한 문자열”을 찾기
  5. 입력 크기/형식이 특정 케이스에서만 커지는지 확인
  6. VPC 환경이면 S3/ECR 접근 경로(NAT, VPC 엔드포인트)부터 점검
  7. 클라이언트 재시도 정책이 장애를 증폭시키는지 확인

마무리: 5xx는 결과, 원인은 계층별로 다르다

SageMaker 엔드포인트의 5xx 와 타임아웃은 대개

  • 콜드 스타트와 스케일 지연,
  • 리소스 부족(OOM 포함),
  • 컨테이너 서버의 동시성/타임아웃 설정,
  • 네트워크 경로,
  • 클라이언트 재시도 정책 중 하나(또는 복합)로 수렴합니다.

가능하면 다음을 기본값으로 가져가면 장애 빈도가 크게 줄어듭니다.

  • 최소 인스턴스 1 유지로 콜드 스타트 제거
  • p95 기준으로 서버·클라이언트 타임아웃 정렬
  • 입력 크기 상한과 빠른 4xx 처리
  • 과부하 시 429 로 빠르게 실패시키는 전략과 오토스케일 튜닝

원하시면 사용 중인 스택(예: PyTorch + TorchServe, Hugging Face DLC, custom FastAPI)과 인스턴스 타입, 평균/최대 입력 크기, 현재 타임아웃 값을 알려주시면 9가지 중 어디를 먼저 파야 하는지 우선순위로 좁혀서 체크리스트를 더 구체화해 드릴게요.