06. Serverless Inference: 인프라 관리 없이 유휴 비용 0¶
GPU가 필요하지 않은 workload의 유휴 비용을 없앨 수 있나요?
Serverless Inference를 사용할 수 있습니다. ServerlessConfig로 구성하며 GPU는 지원하지
않습니다. 이 실험에서 cold start는 on-demand 43초, Provisioned Concurrency 적용 후
9.7초였습니다.
메모리를 넉넉히 주세요
이 실험에서 MemorySizeInMB: 2048은 Worker died 오류가 발생했고 6144(상한)에서
동작했습니다. model file 외에 container와 runtime이 사용하는 memory도 고려해야 합니다.
05와의 차이¶
두 방식 모두 유휴 인스턴스 비용을 없애지만 구성과 cold start가 다릅니다.
| 05 scale to zero | 06 serverless | |
|---|---|---|
| 설정 | ManagedInstanceScaling + AAS 등록 + TargetTracking + StepScaling + CloudWatch 알람 |
ServerlessConfig 하나 |
| GPU | ✅ | ❌ |
| inference component | 필수 | 불필요 |
| 인스턴스 타입 선택 | 필요 | 불필요 (메모리만) |
| 유휴 과금 | 인스턴스 0 까지 계속 (실측 27분) | 즉시 0 |
| 콜드 스타트 | 471초 | 43초 |
| 콜드 스타트 단축 | MinInstanceCount: 1 (유휴 과금 복귀) |
Provisioned Concurrency (실측 43초 → 9.7초) |
05는 GPU가 필요한 instance-based endpoint를 0까지 축소하는 방식입니다. GPU가 필요하지 않고 요청이 산발적이라면 Serverless Inference를 먼저 비교할 수 있습니다. latency와 cold start 요구사항이 엄격하면 CPU real-time endpoint도 함께 측정하세요.
실측 결과¶
MemorySizeInMB: 6144, MaxConcurrency: 10, mdeberta ONNX, us-east-1 기준입니다.
Latency (max_len 512 고정)¶
| 지표 | 값 |
|---|---|
| p50 | 116.5 ms |
| p90 | 137.1 ms |
| p95 | 171.7 ms |
| p99 | 253.3 ms |
| throughput | 8.2 req/s |
| NLI 정확도 | 4/4 (샘플 4건) |
02(CPU real-time, ONNX)의 p50 58.3ms 대비 2배입니다. 같은 ONNX 모델인데 차이가 나는 이유는 서버리스가 워커 1개로 제한되고 컴퓨트 자원도 메모리에 비례해 배정되기 때문입니다.
warmup 조건이 p99에 영향을 줍니다
이 실험에서 warmup 5회는 p99가 31초였고, warmup 15회는 253ms였습니다. benchmark 조건에 warmup 횟수를 함께 기록하세요.
| warmup | p99 |
|---|---|
| 5 | 31,478 ms |
| 15 | 253 ms |
콜드 스타트 (온디맨드)¶
15분 유휴 후 첫 요청입니다.
| 구간 | 실측 |
|---|---|
| warm p50 | 118.6 ms |
| 콜드 스타트 (E2E) | 43,210 ms (43초) |
| warm 대비 | 364배 |
OverheadLatency |
13,658 ms |
E2E 43초 중 OverheadLatency는 13.7초였습니다. 나머지 시간의 세부 구간은 확인할 수
없었으며, 622MB artifact 다운로드와 model load가 포함된 것으로 추정합니다. AWS blog의
약 6초 예시는 다른 model과 설정을 사용하므로 직접 비교할 수 없습니다.
botocore 기본 read timeout을 넘습니다
콜드 스타트 43초는 기본 60초 안이지만, 첫 배포 직후에는 그보다 길어 ReadTimeoutError
가 났습니다. 재현 코드는 read_timeout을 300초로 둡니다.
Provisioned Concurrency¶
Provisioned Concurrency는 compute capacity를 미리 준비해 cold start를 줄입니다. 이 실험에서는 43초에서 9.7초로 낮아졌습니다.
"ServerlessConfig": {
"MemorySizeInMB": 6144,
"MaxConcurrency": 10,
"ProvisionedConcurrency": 1, # ← MaxConcurrency 이하
}
Provisioned Concurrency를 사용하면 프로비저닝한 memory, 시간, concurrency를 기준으로 유휴 상태에도 비용이 발생합니다.
실측: 콜드 스타트가 줄지만 사라지지는 않았습니다¶
같은 endpoint를 ProvisionedConcurrency: 1로 전환하고 같은 조건(15분 유휴)으로 다시
쟀습니다.
| 온디맨드 | Provisioned Concurrency = 1 | 변화 | |
|---|---|---|---|
| warm p50 | 118.6 ms | 120.3 ms | 같음 |
| 15분 유휴 후 첫 호출 | 43,210 ms | 9,741 ms | 4.4배 단축 |
| warm 대비 배수 | 364배 | 81배 | |
| Provisioned Concurrency 적용에 걸린 시간 | - | 323초 |
describe_endpoint에서 ProvisionedConcurrency: 1 적용을 확인했지만 첫 호출은 9.7초가
걸렸습니다.
Provisioned Concurrency가 콜드 스타트를 완전히 없애지는 못했습니다
AWS 블로그는 Provisioned Concurrency로 콜드 스타트가 약 200ms 로 줄었다고 합니다 (온디맨드 약 6초 대비 30배). 이 repository에서는 같은 조건에서 9.7초가 측정됐습니다.
차이의 원인으로 model size를 추정합니다. blog example보다 artifact가 크고 (622MB tar.gz, ONNX 1137MB), memory 상한인 6144MB를 사용합니다. Provisioned Concurrency가 compute를 예약해도 15분 유휴 뒤에는 model을 다시 load하는 것으로 보이지만, SageMaker 내부 동작은 문서로 확인하지 못했습니다.
Provisioned Concurrency 적용 후 latency는 model size와 idle interval에 따라 달라질 수
있습니다. 목표 SLO는 실제 model과 traffic pattern으로 측정하세요.
compare_provisioned.py가 이 비교를 실행합니다.
uv run python 06_cpu_serverless/compare_provisioned.py \
--endpoint <ep> --provisioned 1 --idle-wait 900
유휴 간격을 줄이면(예: 5분) 9.7초가 안 나올 수도 있습니다. 트래픽 패턴에 맞춰
--idle-wait을 바꿔 재보는 것이 실제 판단에 가깝습니다.
# 기존 온디맨드 endpoint를 Provisioned Concurrency로 전환하고 콜드 스타트 재측정
uv run python 06_cpu_serverless/compare_provisioned.py \
--endpoint <ep> --provisioned 1 --idle-wait 900
# Provisioned Concurrency 오토스케일링까지
uv run python 06_cpu_serverless/compare_provisioned.py \
--endpoint <ep> --provisioned 1 --autoscale-max 5
Provisioned Concurrency auto scaling 대상¶
둘 다 Application Auto Scaling을 쓰지만 조절하는 것이 다릅니다.
| 05 inference component | 06 서버리스 Provisioned Concurrency | |
|---|---|---|
| 차원 | sagemaker:inference-component:DesiredCopyCount |
sagemaker:variant:DesiredProvisionedConcurrency |
| 조절 대상 | 모델 사본 수 | Provisioned Concurrency 값 자체 |
| 지표 | ...ConcurrentRequestsPerCopyHighResolution |
SageMakerVariantProvisionedConcurrencyUtilization |
MinCapacity 0 |
✅ 가능 | ❌ 1 이상 |
이 scalable dimension의 MinCapacity는 1 이상이어야 합니다. Provisioned Concurrency를
0으로 바꾸려면 auto scaling이 아니라 새 EndpointConfig로 endpoint를 update해야 합니다.
aas.register_scalable_target(
ServiceNamespace="sagemaker",
ResourceId=f"endpoint/{ep}/variant/AllTraffic",
ScalableDimension="sagemaker:variant:DesiredProvisionedConcurrency",
MinCapacity=1, # 0 은 안 됩니다
MaxCapacity=5,
)
aas.put_scaling_policy(
PolicyType="TargetTrackingScaling",
TargetTrackingScalingPolicyConfiguration={
"PredefinedMetricSpecification": {
"PredefinedMetricType": "SageMakerVariantProvisionedConcurrencyUtilization",
},
"TargetValue": 0.5, # 프로비저닝한 concurrency의 50% 가 쓰이면 늘림
},
)
예측 가능한 트래픽 변화에는 scheduled scaling을 검토할 수 있습니다.
제약¶
| 항목 | 값 |
|---|---|
MemorySizeInMB |
1024, 2048, 3072, 4096, 5120, 6144 (이 6개만) |
MaxConcurrency |
1~200 (계정 전체는 리전당 500 또는 1000) |
ProvisionedConcurrency |
1~MaxConcurrency |
| endpoint 수 | 리전당 50 |
| GPU | ❌ |
| MME, 멀티컨테이너, VPC, inference pipeline, Model Monitor | ❌ |
| 컨테이너 이미지 | 최대 10GB |
| 임시 디스크 | 5GB (메모리 설정과 무관하게 고정) |
real-time endpoint에서 Serverless로 직접 전환할 수 없습니다
update_endpoint로 전환하면 ValidationError가 발생합니다. Serverless에서 real-time
endpoint로 전환한 뒤에는 같은 endpoint를 다시 Serverless로 되돌릴 수 없습니다.
워커를 1개로 두어야 합니다
AWS 문서가 서버리스에서는 "컨테이너에 워커 하나만 만들고 모델 사본도 하나만 로드"하도록
권장합니다. real-time 컨테이너는 vCPU 마다 워커를 띄우는 경우가 있는데, 메모리 상한이
6GB 라 금방 넘칩니다. ONNX 세션이 워커마다 모델을 따로 로드하는 특성과도 맞습니다.
deploy.py가 기본값으로 처리합니다.
실행¶
ONNX artifact가 필요합니다. 02에서 이미 만들었다면 deploy.py가 기본 bucket에서 자동으로
찾습니다.
# ONNX artifact가 없다면 먼저 (02 의 스크립트)
uv run python 02_cpu_cohost_multicontainer/scripts/export_onnx.py --models mdeberta
# endpoint compute를 생성하지 않고 ServerlessConfig validation
uv run python 06_cpu_serverless/deploy.py --dry-run
# 온디맨드 (유휴 과금 없음)
uv run python 06_cpu_serverless/deploy.py
# 호출과 벤치마크
uv run python 06_cpu_serverless/invoke.py --mode cloud --endpoint <ep>
uv run python -m benchmark.run --mode cloud --endpoint <ep> \
--pad-to-max --warmup 15 -n 60
# 콜드 스타트 실측 (약 20분, 유휴 대기 포함)
uv run python 06_cpu_serverless/measure_cold_start.py --endpoint <ep> \
--idle-wait 900 -o results/06_serverless_cold_start.json
# Provisioned Concurrency로 전환해 콜드 스타트 재측정
uv run python 06_cpu_serverless/compare_provisioned.py \
--endpoint <ep> --provisioned 1 --idle-wait 900
uv run python -m common.cleanup --delete-all # Provisioned Concurrency와 endpoint 삭제
호출 방식은 real-time과 같습니다¶
추가 파라미터가 없습니다. TargetContainerHostname(02), InferenceComponentName(03),
TargetModel(04) 같은 것을 쓰지 않습니다.
rt = boto3.client("sagemaker-runtime")
rt.invoke_endpoint(
EndpointName=ep,
ContentType="application/json",
Body=json.dumps({"inputs": {"text": premise, "text_pair": hypothesis}}),
)
Payload가 동일하므로 common/payload.py와 benchmark/ 코드를 함께 사용합니다.
콜드 스타트 측정 방법이 05 와 다릅니다¶
Serverless Inference는 describe_endpoint에서 현재 compute 수를 확인할 수 없습니다.
CurrentInstanceCount field가 없으므로 다음 방식으로 cold start를 측정합니다.
- Warm latency: 연속 호출로 기준값
- 유휴 대기: 트래픽 없이 N 분 (온디맨드는 이 구간 과금 없음)
- 콜드 스타트: 유휴 후 첫 요청의 E2E latency
OverheadLatency: CloudWatch 지표. 컴퓨트를 새로 띄운 시간
OverheadLatency metric으로 Serverless overhead를 확인합니다. metric 발행 지연을 고려해
Script가 최대 180초 기다립니다.
유휴 compute 회수 시점은 공개되어 있지 않습니다. --idle-wait이 짧으면 warm 상태에서
측정할 수 있습니다. script는 첫 호출 latency가 warm latency의 1.5배 미만이면 경고합니다.
언제 무엇을 쓰나¶
| 상황 | 선택 |
|---|---|
| 트래픽이 드물고 cold start를 허용 | on-demand Serverless |
| Provisioned Concurrency 비용을 허용하고 cold start 단축 필요 | Serverless와 Provisioned Concurrency |
| 첫 요청부터 일정한 latency 필요 | real-time endpoint(02)와 직접 비교 |
| 트래픽이 꾸준하고 많음 | real-time endpoint (01, 02) |
| GPU 필요 | 05 scale to zero |
| 여러 모델을 endpoint 하나에 배포 | Serverless는 MME 미지원. 02, 03, 04 비교 |
다음으로 읽을 것¶
- CPU real-time과 비교 → 02 CPU co-host (멀티컨테이너)
- GPU가 필요하면 → 05 Scale to zero
막힌 경우 → 트러블슈팅