AWS Lambda용 커스텀 런타임 구축하기

AWS Lambda용 커스텀 런타임 구축하기 (Building a custom runtime for AWS Lambda)

어떤 프로그래밍 언어로든 AWS Lambda 런타임을 구현할 수 있어요. 런타임은 함수가 호출될 때 Lambda 함수의 핸들러 메서드를 실행하는 프로그램이에요. 런타임을 함수의 배포 패키지에 포함하거나 레이어로 배포할 수 있어요. Lambda 함수를 만들 때 OS-only 런타임(provided 런타임 계열)을 선택하세요.

참고

커스텀 런타임을 만드는 것은 고급 사용 사례예요. 네이티브 바이너리로 컴파일하거나 타사 기성 런타임을 쓰는 방법을 찾고 있다면 Lambda의 OS-only 런타임을 언제 쓸까를 참고하세요.

출처: AWS Lambda 개발자 안내서

본문

요구 사항

커스텀 런타임은 특정 초기화·처리 작업을 완료해야 해요. 런타임은 함수의 설정 코드를 실행하고, 환경 변수에서 핸들러 이름을 읽고, Lambda 런타임 API에서 호출 이벤트를 읽어요. 런타임은 이벤트 데이터를 함수 핸들러에 전달하고, 핸들러의 응답을 Lambda에 게시해요.

초기화 작업

초기화 작업은 호출을 처리하도록 환경을 준비하기 위해 함수 인스턴스마다 한 번 실행돼요.

  1. 설정 조회(Retrieve settings) – 함수와 환경에 대한 세부 정보를 얻기 위해 환경 변수를 읽습니다.
    • _HANDLER – 함수 구성을 기준으로 한 핸들러의 위치입니다. 표준 형식은 file.method입니다. file은 확장자가 없는 파일 이름이고, method는 파일에 정의된 메서드·함수 이름입니다.
    • LAMBDA_TASK_ROOT – 함수 코드가 있는 디렉토리입니다.
    • AWS_LAMBDA_RUNTIME_API – 런타임 API의 호스트와 포트입니다.
  2. 함수 초기화(Initialize the function) – 핸들러 파일을 로드하고 포함된 전역·정적 코드를 실행합니다. 함수는 SDK 클라이언트나 데이터베이스 연결 같은 정적 리소스를 한 번 만들고 여러 호출에서 재사용해야 합니다.
  3. 오류 처리(Handle errors) – 오류가 발생하면 초기화 오류 API를 호출하고 즉시 종료합니다.

초기화는 청구되는 실행 시간과 타임아웃에 포함돼요. 실행이 함수의 새 인스턴스 초기화를 트리거하면 로그와 AWS X-Ray 트레이스에서 초기화 시간을 볼 수 있어요.

예제 로그

REPORT RequestId: f8ac1208... Init Duration: 48.26 ms   Duration: 237.17 ms   Billed Duration: 300 ms   Memory Size: 128 MB   Max Memory Used: 26 MB

처리 작업

실행되는 동안 런타임은 Lambda 런타임 인터페이스를 사용해 들어오는 이벤트를 관리하고 오류를 보고해요. 초기화 작업을 완료한 후 런타임은 들어오는 이벤트를 루프로 처리해요. 런타임 코드에서 다음 단계를 순서대로 수행하세요.

  1. 이벤트 가져오기(Get an event) – next invocation API를 호출해 다음 이벤트를 가져옵니다. 응답 본문에는 이벤트 데이터가 있고, 응답 헤더에는 요청 ID와 기타 정보가 있습니다.
  2. 추적 헤더 전파(Propagate the tracing header) – API 응답의 Lambda-Runtime-Trace-Id 헤더에서 X-Ray 추적 헤더를 가져와 같은 값으로 _X_AMZN_TRACE_ID 환경 변수를 로컬에 설정합니다. X-Ray SDK는 이 값을 사용해 서비스 간 추적 데이터를 연결합니다.
  3. Context 객체 만들기(Create a context object) – 환경 변수와 API 응답 헤더의 context 정보로 객체를 만듭니다.
  4. 함수 핸들러 호출(Invoke the function handler) – 이벤트와 context 객체를 핸들러에 전달합니다.
  5. 응답 처리(Handle the response) – invocation response API를 호출해 핸들러의 응답을 게시합니다.
  6. 오류 처리(Handle errors) – 오류가 발생하면 invocation error API를 호출합니다.
  7. 정리(Cleanup) – 다음 이벤트를 가져오기 전에 사용하지 않는 리소스를 해제하고, 다른 서비스로 데이터를 보내거나 추가 작업을 수행합니다.

진입점(Entrypoint)

커스텀 런타임의 진입점은 bootstrap이라는 실행 가능한 파일이에요. bootstrap 파일이 런타임이거나, 런타임을 만드는 다른 파일을 호출할 수 있어요. 배포 패키지의 루트에 bootstrap이라는 파일이 없으면 Lambda는 함수의 레이어에서 파일을 찾아요. bootstrap 파일이 없거나 실행 가능하지 않으면 함수는 호출 시 Runtime.InvalidEntrypoint 오류를 반환해요.

다음은 번들된 Node.js 버전을 사용해 runtime.js라는 별도 파일의 JavaScript 런타임을 실행하는 bootstrap 파일 예시예요.

예제 bootstrap

#!/bin/sh
cd $LAMBDA_TASK_ROOT
./node-v11.1.0-linux-x64/bin/node runtime.js

커스텀 런타임에서 응답 스트리밍 구현

응답 스트리밍 함수의 경우 응답·오류 엔드포인트가 약간 수정된 동작을 해서 런타임이 부분 응답을 클라이언트로 스트리밍하고 payload를 청크로 반환할 수 있어요. 구체적인 동작은 다음을 참고하세요.

  • /runtime/invocation/AwsRequestId/response – 런타임의 Content-Type 헤더를 전파해 클라이언트로 보냅니다. Lambda는 HTTP/1.1 chunked transfer encoding으로 응답 payload를 청크로 반환합니다. 런타임이 응답을 Lambda로 스트리밍하려면 다음을 해야 합니다.
    • Lambda-Runtime-Function-Response-Mode HTTP 헤더를 streaming으로 설정
    • Transfer-Encoding 헤더를 chunked로 설정
    • HTTP/1.1 chunked transfer encoding 사양에 맞게 응답 작성
    • 응답을 성공적으로 쓴 후 기본 연결 닫기
  • /runtime/invocation/AwsRequestId/error – 런타임이 이 엔드포인트로 함수·런타임 오류를 Lambda에 보고할 수 있는데, Transfer-Encoding 헤더도 받아들입니다. 이 엔드포인트는 런타임이 호출 응답을 보내기 시작하기 전에만 호출할 수 있어요.
  • /runtime/invocation/AwsRequestId/response에서 error trailers로 중간 오류 보고 – 런타임이 호출 응답을 쓰기 시작한 후 발생한 오류를 보고하려면 Lambda-Runtime-Function-Error-Type과 Lambda-Runtime-Function-Error-Body라는 HTTP trailing 헤더를 선택적으로 붙일 수 있습니다. Lambda는 이를 성공 응답으로 취급하고 런타임이 제공한 오류 메타데이터를 클라이언트로 전달합니다.

    참고

    trailing 헤더를 붙이려면 런타임이 HTTP 요청 시작 시 Trailer 헤더 값을 설정해야 해요. 이는 HTTP/1.1 chunked transfer encoding 사양의 요구 사항이에요.

    • Lambda-Runtime-Function-Error-Type – 런타임이 마주친 오류 유형입니다. 문자열 값으로 구성됩니다. Lambda는 어떤 문자열이든 받아들이지만 <category.reason> 형식을 권장합니다. 예: Runtime.APIKeyNotFound.
    • Lambda-Runtime-Function-Error-Body – 오류에 대한 Base64 인코딩 정보입니다.

Lambda Managed Instances용 커스텀 런타임 구축

Lambda Managed Instances는 Lambda(기본값) 함수와 같은 런타임 API를 사용해요. 하지만 Managed Instances의 동시 실행 모델을 지원하려면 커스텀 런타임을 구현하는 방식에 핵심적인 차이가 있어요.

동시 요청 처리

Managed Instances용 커스텀 런타임을 구축할 때의 주요 차이는 동시 호출 지원이에요. 런타임이 한 번에 하나의 호출을 처리하는 Lambda(기본값) 함수와 달리, Managed Instances는 단일 실행 환경 안에서 여러 호출을 동시에 처리할 수 있어요. 커스텀 런타임은 다음을 해야 해요.

  • 동시 /next 요청 지원 – AWS_LAMBDA_MAX_CONCURRENCY 환경 변수가 지정한 한도까지 다음 호출 API에 동시 호출을 할 수 있습니다.
  • 동시 /response 요청 처리 – 여러 호출이 호출 응답 API를 동시에 호출할 수 있습니다.
  • 스레드 안전한 요청 처리 구현 – 공유 리소스와 상태를 제대로 관리해 동시 호출이 서로 간섭하지 않게 합니다.
  • 요청 ID와 호출 ID를 올바르게 사용 – request ID는 이벤트를 식별하며 URL 경로에 사용합니다. invocation ID는 단일 호출 시도를 나타냅니다. 하나의 request ID가 여러 시도를 만들 수 있고 각 시도마다 고유한 invocation ID가 있습니다. /response와 /error 호출에 invocation ID를 다시 보내세요. 이 헤더는 선택 사항이며, 생략하면 하위 호환을 위해 허용됩니다. Lambda는 헤더가 있는데 값이 일치하지 않을 때만 400 InvalidInvocationId로 거부합니다.

구현 패턴

Managed Instances 런타임의 일반적인 구현 패턴은 worker 스레드 또는 프로세스를 만들어 동시 호출을 처리하는 것이에요.

  1. 동시성 한도 읽기 – 초기화 시 AWS_LAMBDA_MAX_CONCURRENCY 환경 변수를 읽어 지원할 동시 호출 수를 결정합니다.
  2. Worker 풀 만들기 – 동시성 한도와 같은 수의 worker(스레드, 프로세스, 또는 async 태스크) 풀을 초기화합니다.
  3. Worker 처리 루프 – 각 worker는 독립적으로 다음을 수행합니다.
    • /runtime/invocation/next를 호출해 호출 이벤트를 가져옵니다.
    • 이벤트 데이터로 함수 핸들러를 호출합니다.
    • 응답을 /runtime/invocation/AwsRequestId/response에 게시합니다.
    • 루프를 반복합니다.

추가 고려 사항

  • 로깅 형식 – Managed Instances는 JSON 로그 형식만 지원해요. 런타임이 AWS_LAMBDA_LOG_FORMAT 환경 변수를 존중하고 JSON 형식만 사용하는지 확인하세요.
  • 공유 리소스 – 동시 호출에서 /tmp 디렉토리 같은 공유 리소스를 사용할 때 주의하세요. 경쟁 상태를 방지하기 위해 적절한 잠금 메커니즘을 구현하세요.

더 알아보기 (Learn more)