Published on

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

Authors

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가지 중 어디를 먼저 파야 하는지 우선순위로 좁혀서 체크리스트를 더 구체화해 드릴게요.