Reducing .NET AWS Lambda Cold Starts by Adding Memory
.NET AWS Lambda 메모리를 늘려 콜드 스타트 줄이기
AWS Lambda의 메모리 할당량을 512MB에서 1,769MB로 높이자 .NET 함수의 콜드 스타트와 웜 실행이 모두 약 3.2배 빨라졌습니다. Lambda는 메모리 설정에 따라 CPU도 함께 배정하므로, CPU 부족이 원인일 때 메모리 증량이 성능과 비용을 함께 개선할 수 있습니다.
- 주제
AI 요약
AWS Lambda로 대부분 구성한 제품에서 검색 기능의 응답 속도를 개선한 사례입니다. 해당 함수는 콜드 스타트에 약 7초가 걸렸습니다. 작성자는 기존 구조의 문제를 바로잡는 방법도 검토했지만, 이미 쌓인 기술적 제약 때문에 우선 Lambda 메모리 할당량을 조정했습니다.
메모리 설정과 CPU 성능
Lambda는 CPU를 직접 지정하지 않고 메모리를 설정합니다. AWS 기준으로 512MB는 약 0.29 vCPU에 해당하며, 1,769MB를 할당하면 1 vCPU 수준의 컴퓨팅 용량을 받습니다. 메모리는 128MB부터 10,240MB까지 1MB 단위로 설정할 수 있습니다. 함수가 CPU, 네트워크, 메모리에 묶여 있다면 메모리 설정을 높여 성능을 개선할 수 있습니다.
512MB 설정에서 콜드 실행 시간은 최소 5,089ms, 중앙값 6,569ms, 최대 7,990ms였습니다. 웜 실행은 각각 498ms, 577ms, 2,168ms였습니다. 1,769MB로 올린 뒤에는 콜드 실행이 1,777ms, 2,049ms, 2,757ms로 줄었고 웜 실행도 161ms, 181ms, 289ms로 짧아졌습니다. 두 경우 모두 중앙값 기준 약 3.2배 빨라졌습니다. 웜 실행도 빨라진 결과는 콜드 스타트뿐 아니라 평상시 실행에서도 CPU가 부족했다는 점을 보여줍니다.
작성자는 이 함수에 실제로 필요한 메모리는 128MB 미만일 수도 있다고 설명합니다. Lambda가 메모리와 CPU를 묶어 제공하므로 메모리를 더 배정하는 방식은 낭비처럼 보일 수 있습니다. 하지만 실행 시간이 줄면서 전체 비용은 오히려 낮아졌다고 합니다. 다만 결과에는 기존 설계 문제로 인한 오버헤드도 반영돼 있습니다. 작성자는 마이그레이션이 부실하게 진행됐고, 레거시 코드와 아키텍처를 Lambda로 옮기는 과정에서 불필요한 복잡성까지 더해졌다고 덧붙였습니다.
Reddit 반응
- @u/shoot_your_eye_out — 사람들은 지연 시간에 민감한 온디맨드 애플리케이션에 Lambda를 억지로 끼워 넣습니다. Lambda의 기본 실행 모델은 짧게 실행되는 이벤트 기반 함수입니다. 항상 실행되며 예측 가능한 지연 시간으로 요청을 처리하는 프로세스가 필요하다면 Fargate가 적합합니다.
- @u/ReallySuperName — 동의합니다. 저는 웹 서비스에는 보통 컨테이너를 씁니다. 서버리스라는 개념도 꽤 어리석다고 생각합니다. 하지만 이런 선택을 하는 임원은 제가 아니죠. 관련 프로젝트 하나는 Fargate에서 운영하는데 배포가 훨씬 쉬웠고, 그 뒤로 안정적으로 돌아가고 있습니다.
- @u/shoot_your_eye_out — 무슨 말인지 압니다. 우리 회사 임원들도 이 모델이 누구에게도 이득이 되지 않는다는 걸 알아줬으면 합니다. 특정 상황에서는 이 아키텍처가 쓸모 있을 수 있지만, 제가 일하는 곳에서는 엔지니어링 팀이 감당해야 하는 또 하나의 나쁜 아키텍처 결정일 뿐입니다.
- @u/lilgreenthumb — 장단점을 문서로 남기며 반대 의견을 제시하지 않고 임원 결정을 무작정 따랐다면, 그건 당신과 엔지니어링 조직 책임입니다.
- @u/ReallySuperName — 그렇게 말하고 나니 기분이 좀 나아졌나요? 글은 읽었나요? 제가 팀의 기능을 개선해 달라는 요청을 받았다는 내용입니다. 제가 그 아키텍처를 선택한 것처럼 들리나요?
- @u/shoot_your_eye_out — 좋은 이야기네요.
- @u/FIREstopdropandsave — 시작 코드를 핸들러가 아니라 init에 넣었나요? Lambda는 콜드 스타트의 init 단계에서 요청한 것보다 많은 CPU를 할당합니다.
- @u/brickman1444 — 글에서는 실제로 CPU가 병목이었다고 합니다. Lambda에서 CPU를 더 받으려면 메모리를 더 요청해야 합니다.
- @u/EastDrink2875 — JVM 사용자도 같은 문제를 겪고, 보통은 더 심합니다. Lambda에서 메모리는 CPU를 간접적으로 정하는 값입니다. CPU는 설정한 메모리에 대략 비례해 늘어나므로, “콜드 스타트를 고치려고 메모리를 늘린다”는 말은 실제로 “초기화 단계에 CPU를 더 투입한다”는 뜻입니다. 관리형 런타임에서는 클래스 로딩, 검증, JIT 준비가 첫 요청을 받기 전에 진행되고, 이런 작업은 CPU를 많이 씁니다. Java 쪽에서는 이미 준비된 JVM을 스냅샷에서 복원하는 SnapStart나, 리플렉션 설정이 맞는 경우 GraalVM native image를 쓸 수 있습니다. 초기 준비만 빠르게 하려고 메모리를 과하게 배정하는 것보다 대체로 낫습니다. 다만 앞서 나온 Fargate 의견처럼 초기화가 끝난 뒤에도 각 실행 환경에서 필요 이상의 메모리 비용을 계속 냅니다. 트래픽이 꾸준하다면 매 호출마다 메모리를 과다 할당하기보다 provisioned concurrency나 컨테이너가 비용 구조에 더 잘 맞습니다.
- @u/ReallySuperName — 아무도 ChatGPT 같은 답변을 요청하지 않았습니다.
원문: Lloyd Atkinson / 번역·요약: Trawling