~ / posts / developer
💻 개발자

람다 콜드 스타트 줄이기, 프로비저닝된 동시성·SnapStart 고르는 법

람다 API의 p99 지연이 가끔 1초를 넘어서 프로비저닝된 동시성을 켜야 하나, SnapStart로 충분한가, 웜업 핑이면 되나 고민하는 경우가 많아요. 결론부터 말하면 먼저 로그의 Init Duration으로 콜드 스타트가 실제로 몇 번, 얼마나 길게 나는지 재요. 그다음 사용자 대면 API처럼 지연 기준이 엄격하면 프로비저닝된 동시성, 런타임이 Java·Python·.NET이고 초기화가 무거우면 SnapStart, 둘 다 아니면 초기화 코드부터 줄여요. 웜업 핑은 동시 요청이 몰릴 때 대부분 소용이 없어요.

이 글은 실무 판단을 다뤄요. 상황별 판단표, 수량 공식, 미국 동부 요금으로 직접 계산한 손익분기, 증상별 확인 순서, 도입 전 체크리스트를 정리했어요.

광고

먼저 재 보기 — 콜드 스타트가 정말 병목인가

람다 실행 환경 생명주기 문서는 콜드 스타트가 보통 호출의 1% 미만에서 나고, 길이는 100ms 미만부터 1초 넘게까지 다양하다고 적어요. 호출이 드문 개발 환경의 첫 호출 지연만 보고 운영 설정을 바꾸면 돈만 더 쓰기 쉬워요.

콜드 스타트가 난 호출은 CloudWatch 로그의 REPORT 줄에 Init Duration 항목이 붙어요. CloudWatch Logs Insights에서 함수 로그 그룹을 골라 시간대별로 세어 봐요.

filter @type = "REPORT"
| stats count(*) as calls,
        count(@initDuration) as cold,
        pct(@initDuration, 50) as init_p50,
        pct(@initDuration, 99) as init_p99,
        pct(@duration, 99) as dur_p99
  by bin(1h)

count(@initDuration)은 값이 있는 줄만 세서 콜드 스타트 수가 돼요. 이 세 숫자로 판단해요.

JSON 로그 형식이면 platform.initReport 같은 이벤트가 한 줄 JSON으로 찍혀요. JSON Formatter에 붙여 펼치면 읽기 쉬워요.

상황별 판단표 — 무엇부터 손대나

상황먼저 고를 것고르지 않는 이유가 있는 것
사용자 대면 API, p99 기준이 엄격함프로비저닝된 동시성 + 별칭웜업 핑: 동시 요청 수만큼 데우지 못해요
Java·Python 3.12+·.NET 8+, 프레임워크 로딩이 수 초SnapStart프로비저닝된 동시성과 함께 켤 수 없어요
Node.js·Ruby·OS 전용 런타임, 초기화가 무거움패키지·import 다이어트, 그다음 프로비저닝된 동시성SnapStart 미지원 런타임이에요
큐·배치 같은 비동기 처리아무것도 켜지 않음지연에 둔감해서 추가 요금만 늘어요
출근 시간처럼 피크가 예측됨예약(scheduled) 스케일링으로 시간대만 켜기24시간 상시 수량은 밤에 이용률이 떨어져요
하루 몇 번만 불리는 함수그대로 두기SnapStart도 드문 호출에는 효과가 작다고 문서가 적어요

SnapStart 문서는 SnapStart로 몇 초 걸리던 초기화를 최적의 경우 1초 미만까지 줄일 수 있다고 소개해요. 동시에, SnapStart로 못 맞추는 엄격한 지연 요구에는 프로비저닝된 동시성을 쓰라고 적어요. 두 자릿수 ms 응답을 목표로 설계된 쪽이 프로비저닝된 동시성이에요.

콜드 스타트 대응을 고르는 순서도. Init Duration을 재고, 사용자 응답 경로인지, 지연 기준이 엄격한지, 런타임이 SnapStart 지원인지에 따라 그대로 두기·초기화 줄이기·SnapStart·프로비저닝된 동시성으로 갈라져요
재고 나서 고르고, 고르기 전에 초기화 코드부터 줄여요

어느 길이든 초기화 코드 다이어트는 공통이에요. 생명주기 문서도 실행 전 지연의 가장 큰 몫을 초기화 코드로 꼽아요.

쓰지 않는 라이브러리는 빼고, 한 경로에서만 쓰는 무거운 클라이언트는 처음 필요할 때 만들고, 재사용할 연결만 핸들러 바깥에 둬요.

프로비저닝된 동시성 수량과 설정값 정하기

프로비저닝된 동시성 문서가 주는 공식은 하나예요.

필요 동시성 = 평균 초당 요청 수 × 평균 실행 시간(초)

초당 50건, 평균 200ms라면 50 × 0.2 = 10이에요. 문서는 평소 피크에 10% 여유를 더하라고 권해요. 피크 동시성이 20이면 22개를 잡는 식이에요. 초당 요청 수는 Invocations, 실행 시간은 Duration 지표로 구해요.

설정정할 값이유
붙일 대상버전을 가리키는 별칭$LATEST에는 붙지 않아요. API Gateway·이벤트 소스도 그 별칭을 가리켜야 해요
수량 상한미예약 동시성 − 100계정 한도 1,000이면 함수 하나에 최대 900이에요
자동 조절 방식예측 가능하면 예약, 아니면 목표 추적문서가 이렇게 나눠 권해요
목표 추적 목표값0.7 부근(10~90% 사이)70%를 넘으면 늘리고, 63%(목표의 90%) 아래면 줄여요
경보 통계짧은 폭주가 많으면 Maximum기본 Average는 3분 넘게 이어진 부하에만 반응해요
최소 용량1 이상 직접 지정처음 값이 없으면 자동 조절이 제대로 동작하지 않을 수 있어요

조심할 점이 하나 더 있어요. ProvisionedConcurrencyUtilization 지표는 함수가 요청을 받을 때만 나와요. 요청이 끊기면 경보가 INSUFFICIENT_DATA가 되고 수량이 줄지 않아요. 밤새 요청이 없는 함수라면 목표 추적만 믿지 말고 예약 스케일링으로 밤에는 내려 둬요.

숫자로 보는 비용 — 이용률 60%가 손익분기

미국 동부(버지니아 북부) x86 요금을 AWS Lambda 요금 페이지에서 확인했어요(2026년 10월 기준, 바뀔 수 있어요).

프로비저닝된 동시성 1개를 한 달(730시간) 켜 두고, 그중 비율 u만큼 실제로 요청을 처리한다고 해 봐요. 메모리 1GB 기준으로 계산하면 이래요.

두 식이 같아지는 u는 0.0000041667 ÷ (0.0000166667 − 0.0000097222) = 0.60이에요. 이용률이 60%를 넘으면 프로비저닝된 동시성이 오히려 싸고, 그 아래면 지연을 사는 값을 내는 셈이에요.

이용률별 월 비용 — 프로비저닝된 동시성 1개(1GB) vs 온디맨드
온디맨드프로비저닝된 동시성
0102030405020%40%60%80%100%20% · 온디맨드 8.8달러40% · 온디맨드 17.5달러60% · 온디맨드 26.3달러80% · 온디맨드 35달러100% · 온디맨드 43.8달러43.820% · 프로비저닝된 동시성 16.1달러40% · 프로비저닝된 동시성 21.2달러60% · 프로비저닝된 동시성 26.3달러80% · 프로비저닝된 동시성 31.4달러100% · 프로비저닝된 동시성 36.5달러36.5

두 선은 이용률 60%에서 만나요. Init 요금과 넘친 트래픽 요금은 넣지 않았어요.

단위: 달러 · 자료: AWS Lambda 요금(미국 동부 x86, 2026년 10월 확인)으로 직접 계산 · 730시간 · 요청 요금 제외
표로 보기
구분온디맨드프로비저닝된 동시성
20%8.816.1
40%17.521.2
60%26.326.3
80%3531.4
100%43.836.5

이번에는 앞의 예시(초당 50건, 평균 200ms, 1GB)를 한 달 돌린다고 해 봐요. 실행량은 50 × 0.2 × 2,628,000 = 2,628만 GB-초이고 요청 요금은 어느 쪽이든 $26.28이에요.

초당 50건·200ms·1GB 함수의 월 실행 비용 비교
온디맨드온디맨드 · 438달러438달러평균에 맞춘 10개 상시평균에 맞춘 10개 상시 · 365달러365달러피크+10%인 22개 상시피크+10%인 22개 상시 · 496달러496달러22개를 하루 12시간만22개를 하루 12시간만 · 376달러376달러

10개는 모든 요청이 데운 환경에서 돈다고 본 하한값이에요. 실제로는 피크에 넘친 몫이 온디맨드 요금으로 붙어요.

단위: 달러 · 자료: AWS Lambda 요금(미국 동부 x86, 2026년 10월 확인)으로 직접 계산 · 요청 요금 $26.28 별도
표로 보기
구분월 비용
온디맨드438
평균에 맞춘 10개 상시365
피크+10%인 22개 상시496
22개를 하루 12시간만376

22개를 24시간 켜면 이용률이 10 ÷ 22 = 45%라 온디맨드보다 $58.40 비싸요. 같은 22개를 낮 12시간만 켜면 유지비가 절반($120.45)으로 줄어요. 밤에 꺼 둔 동안의 실행은 온디맨드 단가라 실제 값은 조금 더 나와요. 정리하면 "피크 기준 상시 수량"이 가장 흔하게 돈이 새는 설정이에요.

콜드 스타트 요금도 따져 봐요. 콜드 스타트가 호출의 1%(월 131만 4,000번)이고 Init이 0.8초라면 131만 4,000 × 0.8 GB-초 × 0.0000166667 = 약 $17.52예요. 설정 실수 한 번이 이보다 훨씬 큰 돈이에요.

SnapStart는 Java 관리형 런타임이면 추가 요금이 없어요. Python·.NET은 두 가지가 붙어요.

캐시 요금은 SnapStart를 켠 게시 버전마다 붙어요. 옛 버전을 지우지 않으면 쌓여요.

확장 속도와 한도 — 설정보다 먼저 막히는 곳

람다 확장 동작 문서에 따르면 함수 하나는 리전마다 10초에 실행 환경 1,000개까지 늘어나요. 쓰지 않은 몫은 쌓이지 않아요. 이 속도보다 요청이 빨리 늘거나 계정 동시성 한도에 닿으면 429 스로틀 오류가 나요.

예를 들어 평소 동시성 200인 함수에 이벤트로 동시성 5,000이 필요한 트래픽이 갑자기 오면, 계정 한도가 넉넉해도 늘어나는 데 대략 40~50초가 걸려요. 그동안 새 환경은 전부 콜드 스타트이고 일부는 429를 받아요. 일정이 보이면 그 전에 예약 스케일링으로 올려 둬요.

한도 계산도 함께 해요. 계정 한도가 1,000이고 함수 A에 프로비저닝된 동시성 100을 잡으면, 다른 함수들은 나머지 900을 나눠 써요. A가 100을 다 안 써도 마찬가지라, 크게 잡으면 다른 함수가 스로틀될 수 있어요.

운영에서 자주 나는 증상과 확인 순서

증상별 확인 순서 카드. 프로비저닝된 동시성을 켰는데도 콜드 스타트, 갑자기 429, Duration이 이상하게 김, VPC 함수 첫 호출 실패, SnapStart 뒤 중복 ID의 다섯 가지 증상과 각각 먼저 볼 곳
증상마다 가장 흔한 원인부터 확인해요

프로비저닝된 동시성을 켰는데 Init Duration이 계속 찍혀요

  1. 호출하는 쪽이 별칭이나 버전 ARN을 쓰는지 봐요. $LATEST나 한정자 없는 이름이면 데운 환경을 거치지 않아요.
  2. 별칭이 가리키는 버전과 프로비저닝 설정이 붙은 버전이 같은지 봐요. 새 버전을 배포하고 별칭만 옮겼다면 설정을 다시 확인해요.
  3. ProvisionedConcurrencySpilloverInvocations 지표로 넘친 호출이 있는지 봐요. 있으면 수량이 피크보다 적은 거예요.
  4. 함수 안에서 환경 변수 AWS_LAMBDA_INITIALIZATION_TYPE(provisioned-concurrency 또는 on-demand)을 로그로 남겨 확정해요.

갑자기 429가 나요

계정 ConcurrentExecutions가 한도에 닿았는지, 다른 함수의 예약·프로비저닝 수량이 풀을 잠식했는지, 10초당 1,000개 속도에 걸렸는지 봐요. 짧은 폭주라면 마지막 경우가 많아요.

REPORT의 Duration이 START~END 시간 차이보다 길어요

이전 호출이 오류나 타임아웃으로 끝나 환경이 재설정되면, 다음 호출에서 Init을 다시 도는 'suppressed init'이 생겨요. 이때는 Init 줄이 따로 없고 Duration에 Init 시간이 섞여요. 직전 호출의 오류부터 찾아요. 응답 지연 일반은 API 응답 디버깅 순서도 참고해요.

온디맨드 Init이 10초를 넘겨 첫 호출이 느려요

온디맨드 Init 제한은 10초이고, 넘기면 첫 호출 때 함수 타임아웃으로 다시 시도해요. 프로비저닝된 동시성·SnapStart는 130초와 함수 타임아웃(최대 900초) 중 큰 값까지 허용돼요.

VPC에 붙인 함수의 첫 호출이 실패해요

VPC 연결 문서에 따르면 14일 동안 쉬면 Hyperplane ENI가 회수되고 함수가 Inactive가 돼요. 다음 호출은 실패하고 ENI가 다시 준비될 때까지 Pending이에요. 월말에만 도는 함수라면 호출 쪽에 재시도를 둬요. 호출마다 ENI를 만드는 구조는 아니에요.

SnapStart를 켠 뒤 ID·토큰이 겹쳐요

Init에서 만든 UUID, 난수 시드, 임시 자격 증명은 같은 스냅숏에서 복원된 환경끼리 같아져요. 고유값은 핸들러 안이나 복원 뒤(after-restore) 런타임 훅에서 만들어요. UUID 충돌 확률 계산이 "정말 무작위일 때"를 전제한다는 점은 UUID v4 v7 구조와 충돌 확률에서 다뤘어요. 스냅숏 복제는 그 전제를 깨요.

도입 전에 따질 것 — 체크리스트

자주 묻는 질문

웜업 핑을 5분마다 보내면 프로비저닝된 동시성이 필요 없나요?

핑 한 번이 데우는 실행 환경은 하나예요. 동시 요청이 10개 몰리면 나머지 9개는 새 환경을 만들어요. 실행 환경도 계속 호출되는 함수조차 몇 시간마다 교체돼요. 예측 가능한 지연은 프로비저닝된 동시성으로 사는 편이 맞아요.

프로비저닝된 동시성은 몇 개로 시작하나요?

Invocations와 Duration 지표로 "초당 요청 수 × 평균 실행 초"를 구하고, 평소 피크 동시성에 10%를 더해요. 비용이 걱정되면 평균 동시성으로 시작해 넘친 호출 지표를 보며 올려요. 이용률이 60% 아래로 오래 머물면 수량을 줄이거나 시간대를 나눠요.

SnapStart와 프로비저닝된 동시성을 같이 켤 수 있나요?

안 돼요. SnapStart 문서에 지원하지 않는 기능으로 프로비저닝된 동시성이 적혀 있어요. 두 자릿수 ms가 필요한 경로는 프로비저닝된 동시성, 수 초짜리 초기화를 1초 안팎으로 줄이면 되는 경로는 SnapStart가 맞아요.

콜드 스타트 요금은 얼마나 나오나요?

2025년 8월부터 모든 구성에서 Init 시간이 Billed Duration에 들어가요. 1GB 함수의 Init이 0.8초면 콜드 스타트 한 번에 0.8 GB-초, 미국 동부 x86 단가로 약 $0.0000133이에요. 월 100만 번 나도 약 $13이라, 대부분은 요금보다 지연이 더 큰 문제예요.

정리

참고한 자료

#람다 콜드 스타트#프로비저닝된 동시성 비용#Lambda SnapStart#Init Duration#람다 동시성 계산#람다 웜업#서버리스 지연 시간
← 이전 글한국어 LLM 토큰 비용 비교, 토크나이저별 1,000자 토큰 수와 세는 법다음 글 →LLM temperature 설정 고르기, API별 지원표와 다중 샘플 비용 계산

더 둘러보기

💻 개발자 다른 도구

🔥 많이 찾는 도구

🔮 재미로 한 번 더

📝 읽어 볼 만한 글

글 전체 보기 →