AWS Lambda 해부: MicroVM부터 레이어까지, 서버리스의 속을 들여다보다
![]()
스프링 프레임워크로 커리어 대부분을 보낸 개발자가 서버리스 프로젝트에 합류하면 어떻게 될까요? CloudFront, S3, API 게이트웨이, Lambda — 익숙하지 않은 스택 앞에서 공부할 게 산더미죠. 블로거 Junhyunny가 바로 그 상황에서 Lambda를 정리했어요. 시장에 빠르게 진입할 수 있고 사용자가 적을 때 비용을 절약할 수 있다는 이유로 서버리스를 선택한 프로젝트라고 하는데, 정리가 꽤 꼼꼼하더라고요.
코드만 올리면 실행된다는 말의 실체
AWS Lambda. 서버를 직접 프로비저닝하거나 유지 관리할 필요 없이 코드를 실행할 수 있게 해주는 서버리스 컴퓨팅 서비스예요. OS, 컴퓨팅 용량, 스케일링, 보안 같은 인프라의 모든 측면을 AWS가 관리하니까, 개발자는 비즈니스 로직 작성에만 집중하면 되는 거죠. 간단히 말하면 코드만 업로드하면 Lambda가 알아서 실행해줘요.
핵심 특징을 정리하면:
이벤트 기반 실행 — 독립적으로 계속 돌아가는 게 아니라, API 게이트웨이를 통한 HTTP 요청, S3 파일 업로드, DynamoDB 테이블 업데이트 같은 이벤트가 발생했을 때 트리거돼요.
비용 효율성 — 코드가 실제로 실행된 시간(1ms 단위)과 요청 수에 대해서만 과금. 안 돌아가면 비용 제로. 프로비저닝된 서버에 트래픽이 없어도 돈 나가는 거랑 완전히 다른 구조죠.
자동 확장 — 트래픽에 맞춰 자동 스케일 아웃. 다중 가용 영역(AZ)에 걸쳐 고가용성이 기본 내장이라 로드 밸런싱이나 이중화 설계에 신경 쓸 필요가 없어요.
다양한 런타임 — Node.js, Python, Java, Ruby, Go, Rust, C# 등. 사용자 지정 런타임도 만들 수 있고요.
무상태 — 개별 요청마다 격리된 MicroVM에서 실행. 각 호출은 기본적으로 상태를 공유하지 않아요. 다만 성능 최적화를 위해 처리가 끝난 실행 환경을 일정 시간 유지하다가 재사용(웜 스타트)하기도 한다는 점은 알아둬야 해요.
편리하지만, 제약사항이 꽤 많다
Lambda를 선택하기 전에 반드시 체크해야 할 것들이 있어요. 솔직히 이 부분을 모르고 선택하면 나중에 후회하거든요.
컴퓨팅 리소스부터. 메모리는 128MB~10,240MB(10GB)까지 1MB 단위로 할당 가능. CPU는 직접 제어할 수 없고, 메모리 크기에 비례해서 자동 할당돼요. `/tmp` 임시 스토리지도 512MB~10GB. 그리고 가장 중요한 건 — 처리 시간 최대 15분(900초). 장기 실행 프로세스에는 안 맞아요.
실행 라이프사이클은 세 단계로 나뉘어요.
초기화(Init) — 환경 준비 단계. 확장 시작, 런타임 부트스트랩(런타임 인터프리터 메모리 로드, API 연결, 환경 변수/자격 증명 로드, 함수 코드 로드), 핸들러 외부의 정적 코드 실행. 기본 10초 이내 완료. '프로비저닝된 동시성'이나 'SnapStart'를 쓰면 130초~최대 15분까지 늘어나요. 크래시나 10초 초과 시, 다음 첫 함수 호출 때 초기화를 다시 시도하고요.
호출(Invoke) — 실제 비즈니스 로직 실행 단계. 타임아웃 한도 내에 코드와 확장 작업이 모두 완료돼야 해요. 타임아웃이나 크래시 발생 시 환경이 리셋되는데, `/tmp` 디렉터리의 임시 파일은 지워지지 않고 유지돼요. (이거 모르면 의외의 상황이 생길 수 있어요.)
종료(Shutdown) — 장기간 미사용 시 실행 환경을 완전히 파기하기 직전 단계. 최대 2초. 2초 내에 응답 안 하면 SIGKILL로 강제 종료. 냉정하죠.
배포 패키지 제약도 있어요. `.zip` 아카이브 최대 50MB, 압축 해제 후 총 250MB. 더 큰 게 필요하면 도커 컨테이너 이미지를 쓸 수 있는데, 이 경우 최대 10GB. 레이어는 함수당 최대 5개.
MicroVM 안에서 무슨 일이 벌어지나

Lambda는 가상 머신에서 실행돼요. 이 VM을 MicroVM이라 부르는데, 내부 구조를 보면:
실행 환경 — MicroVM 내부에서 생성되는 격리된 런타임 환경. 함수의 런타임과 확장들은 개별 프로세스로 실행되지만, 권한, 자격 증명, 환경 변수, 리소스는 서로 공유해요. 실행이 끝나면 환경을 즉시 파기하지 않고 동결한 채로 유지해서 다음 호출에 재사용(Warm Start). 핸들러 외부에 선언된 변수나 `/tmp` 데이터가 그대로 보존되는 이유가 이거예요. 근데 환경은 주기적으로 파기되니까, 코드는 근본적으로 무상태로 작성해야 해요.
람다 런타임 — 데이터 플레인과 함수 코드 사이에서 호출 이벤트, 컨텍스트, 응답을 중계하는 역할. HTTP 기반 런타임 API로 Lambda 서비스와 통신해요.
함수 코드 — 개발자가 작성한 실제 비즈니스 로직. `.zip`이나 컨테이너 이미지로 묶여서 업로드되고요.

Lambda Worker는 수많은 MicroVM을 관리하는 EC2 스팟 인스턴스예요. 각 워커는 최대 14시간의 임대 수명을 갖고, 수명이 다하면 더 이상 새 호출이 라우팅되지 않고 안전하게 종료돼요.
여기서 핵심 기술이 Firecracker. 아마존이 Rust로 직접 개발한 오픈 소스 가상화 기술인데, KVM을 활용해 수 밀리초 만에 부팅돼요. 각 MicroVM은 오직 단일 함수 호출에만 독점 할당되고요. 보안과 격리가 핵심인 거죠.
동기식 vs 비동기식 — 호출 방식에 따라 흐름이 달라진다

모든 Lambda 호출의 진입점은 프런트엔드 서비스가 담당해요. 호출 방식에 따라:
동기식 — 프런트엔드 서비스가 요청을 MicroVM으로 직접 라우팅. 즉각 처리.
비동기식 — 즉시 처리하지 않고 Lambda 내부 대기열에 보관. 이 큐잉 메커니즘이 이벤트를 사용 가능한 MicroVM에 효율적으로 분배하고, 트래픽 급증 시에도 로드 밸런싱을 유지해줘요.

Kinesis, DynamoDB Streams 같은 이벤트 소스 매핑 환경에서는 추가 구성 요소가 있어요. 폴링 컨슈머가 레코드를 읽어오고, 어그리게이터가 배치로 묶고, 인보커 프로세스가 프런트엔드 서비스에 동기식 호출을 시작하는 구조.

MicroVM 안의 함수는 런타임 HTTP API를 통해 Lambda 서비스와 통신해요. `AWS_LAMBDA_RUNTIME_API` 환경 변수에서 엔드포인트를 가져오고요. 핵심 메서드는:
- Next invocation (GET) — 다음 호출 이벤트 요청. 이벤트가 올 때까지 런타임이 동결 상태로 대기할 수 있어요.
- Invocation response (POST) — 실행 완료 후 결과 반환. 동기식이면 이게 클라이언트에 전달.
- Initialization error / Invocation error (POST) — 초기화나 실행 중 에러 보고.
레이어 — 종속성을 분리하는 실용적 방법

Lambda 레이어는 함수에 필요한 추가 코드나 데이터(라이브러리, 커스텀 런타임, 설정 파일 등)를 담은 `.zip` 아카이브예요. 레이어를 추가하면 실행 환경의 `/opt` 디렉터리에 자동으로 압축 해제돼요.
이점이 꽤 실용적이에요:
- 동일 라이브러리를 여러 함수에서 공유. 코드 중복 방지
- 무거운 종속성을 분리하면 배포 패키지가 가벼워지고, 업로드·배포 시간 단축
- 비즈니스 로직과 종속성 분리로 함수 코드가 목적에 집중
- 배포 패키지가 가벼워지면 AWS 콘솔 내장 코드 에디터도 사용 가능
- AWS 환경의 기본 SDK 버전은 예고 없이 변경될 수 있는데, 레이어에 특정 버전을 포함하면 안전하게 고정
함수당 최대 5개, 함수 코드 + 모든 레이어 압축 해제 후 총합 250MB가 한도. 레이어는 한 번 생성되면 불변(Immutable) 스냅샷으로 버전 관리돼서, 수정하려면 새 `.zip`을 올려 새 버전을 만들어야 해요.
주의할 점도 있어요. Python의 경우 압축 파일 내 최상위 폴더를 `python`으로 지정해야 런타임이 인식해요. 모든 레이어는 `/opt`에 풀리니까, 서로 다른 레이어에 같은 이름의 파일이 있으면 나중에 호출된 레이어가 덮어써요. 순서가 중요한 거죠.
그리고 Lambda는 Amazon Linux에서 실행되니까 레이어 패키지는 리눅스 호환 형태로 빌드돼야 해요. Go나 Rust 같은 컴파일 언어는 하나의 실행 파일로 제공하는 게 성능상 유리하고, 레이어로 종속성을 분리하면 오히려 초기화 단계에서 어셈블리를 수동 로드해야 해서 콜드 스타트 지연이 길어질 수 있어요. 권장되지 않는 패턴이에요.
Lambda를 쓸 거면 제약사항부터 확인하라는 얘기, 당연한 것 같지만 실전에서는 놓치기 쉽잖아요. 특히 15분 타임아웃, 250MB 패키지 제한, 레이어 5개 한도 — 이 세 가지는 아키텍처 설계 단계에서 반드시 고려해야 할 숫자들이에요.