튜토리얼: 커스텀 런타임 빌드하기
튜토리얼: 커스텀 런타임 빌드하기 (Tutorial: Building a custom runtime)
이 튜토리얼에서는 커스텀 런타임(custom runtime)을 가진 Lambda 함수를 만들어요. 먼저 런타임을 함수의 배포 패키지에 포함해서 시작한 다음, 함수와 독립적으로 관리하는 레이어로 옮기고, 마지막으로 리소스 기반 권한 정책을 업데이트해서 런타임 레이어를 세상과 공유해요.
전제 조건
이 튜토리얼은 기본적인 Lambda 작업과 Lambda 콘솔에 대한 어느 정도의 지식이 있다고 가정해요. 아직 안 했다면 콘솔로 Lambda 함수 만들기의 지침에 따라 첫 Lambda 함수를 만들어 보세요.
다음 단계를 완료하려면 AWS CLI version 2가 필요해요. 명령과 예상 출력은 별도 블록으로 나열돼요.
aws --version
다음 출력이 보여야 해요.
aws-cli/2.13.27 Python/3.11.6 Linux/4.14.328-248.540.amzn2.x86_64 exe/x86_64.amzn.2
긴 명령에서는 이스케이프 문자(\)로 여러 줄에 걸쳐 명령을 나눠요. Linux와 macOS에서는 원하는 셸과 패키지 매니저를 사용해요.
참고 Windows에서는 Lambda에 자주 쓰는 일부 Bash CLI 명령(예:
zip)이 운영체제 내장 터미널에서 지원되지 않아요. Windows와 통합된 Ubuntu와 Bash를 얻으려면 Windows Subsystem for Linux를 설치하세요. 이 안내서의 예시 CLI 명령은 Linux 형식을 사용해요. 인라인 JSON 문서가 포함된 명령은 Windows CLI를 쓸 때 다시 포맷해야 해요.
Lambda 함수를 만들려면 IAM 역할이 필요해요. 역할은 로그를 CloudWatch Logs로 보내고 함수가 쓰는 AWS 서비스에 접근할 권한이 필요해요. 함수 개발용 역할이 없다면 지금 만들어 두세요.
실행 역할을 만들려면
- IAM 콘솔의 roles 페이지를 열어요.
- Create role을 선택해요.
- 다음 속성으로 역할을 만들어요. Trusted entity — Lambda. Permissions — AWSLambdaBasicExecutionRole. Role name —
lambda-role. AWSLambdaBasicExecutionRole 정책에는 함수가 CloudWatch Logs에 로그를 쓸 때 필요한 권한이 들어 있어요.
함수 만들기
커스텀 런타임을 가진 Lambda 함수를 만들어요. 이 예시는 런타임 bootstrap 파일과 함수 핸들러 두 파일로 구성돼요. 둘 다 Bash로 구현돼요.
- 프로젝트용 디렉터리를 만들고 그 디렉터리로 이동해요.
mkdir runtime-tutorial
cd runtime-tutorial
bootstrap이라는 새 파일을 만들어요. 이게 커스텀 런타임이에요.
#!/bin/sh
set -euo pipefail
# Initialization - load function handler
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"
# Processing
while true
do
HEADERS="$(mktemp)"
# Get an event. The HTTP request will block until one is received
EVENT_DATA=$(curl -sS -LD "$HEADERS" "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
# Extract request ID by scraping response headers received above
REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
# Extract invocation ID for cross-wiring protection
INVOCATION_ID=$(grep -Fi Lambda-Runtime-Invocation-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
# Run the handler function from the script
RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")
# Send the response, echoing the invocation ID
curl "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE" \
${INVOCATION_ID:+--header "Lambda-Runtime-Invocation-Id: $INVOCATION_ID"}
done
이 런타임은 배포 패키지에서 함수 스크립트를 로드해요. 스크립트를 찾는 데 두 변수를 사용해요. LAMBDA_TASK_ROOT는 패키지가 추출된 위치를 알려주고, _HANDLER는 스크립트 이름을 포함해요. 런타임이 함수 스크립트를 로드한 뒤에는 런타임 API로 Lambda에서 호출 이벤트를 가져오고, 그 이벤트를 핸들러에 전달하며, 응답을 Lambda에 다시 게시해요. 요청 ID를 얻기 위해 런타임은 API 응답의 헤더를 임시 파일에 저장하고 파일에서 Lambda-Runtime-Aws-Request-Id 헤더를 읽어요. 또한 Lambda-Runtime-Invocation-Id 헤더를 읽어 응답에 다시 내보내요.
참고 런타임에는 오류 처리, 핸들러에 컨텍스트 정보 제공 등 추가 책임이 있어요. 자세한 내용은 요구 사항(Requirements)을 참고하세요.
- 함수용 스크립트를 만들어요. 다음 예시 스크립트는 이벤트 데이터를 받아
stderr에 로그로 기록하고 반환하는 핸들러 함수를 정의해요.
function handler () {
EVENT_DATA=$1
echo "$EVENT_DATA" 1>&2;
RESPONSE="Echoing request: '$EVENT_DATA'"
echo $RESPONSE
}
이제 runtime-tutorial 디렉터리는 이렇게 생겼어요.
runtime-tutorial
├ bootstrap
└ function.sh
- 파일을 실행 가능하게 만들고
.zip파일 아카이브에 추가해요. 이게 배포 패키지예요.
chmod 755 function.sh bootstrap
zip function.zip function.sh bootstrap
bash-runtime이라는 함수를 만들어요.--role에는 Lambda 실행 역할의 ARN을 입력해요.
aws lambda create-function --function-name bash-runtime \
--zip-file fileb://function.zip --handler function.handler --runtime provided.al2023 \
--role arn:aws:iam::123456789012:role/lambda-role
- 함수를 호출해요.
aws lambda invoke --function-name bash-runtime --payload '{"text":"Hello"}' response.txt --cli-binary-format raw-in-base64-out
cli-binary-format 옵션은 AWS CLI version 2를 쓸 때 필요해요. 이걸 기본 설정으로 만들려면 aws configure set cli-binary-format raw-in-base64-out을 실행하세요. 자세한 내용은 AWS Command Line Interface User Guide for Version 2의 AWS CLI supported global command line options을 참고하세요.
다음과 같은 응답이 보여요.
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
- 응답을 확인해요.
cat response.txt
다음과 같은 응답이 보여요.
Echoing request: '{"text":"Hello"}'
레이어 만들기
런타임 코드를 함수 코드와 분리하려면 런타임만 포함하는 레이어를 만들어요. 레이어는 함수의 의존성을 독립적으로 개발할 수 있게 해주고, 여러 함수에서 같은 레이어를 쓰면 스토리지 사용량을 줄일 수 있어요. 자세한 내용은 레이어로 Lambda 의존성 관리하기를 참고하세요.
bootstrap파일을 포함하는.zip파일을 만들어요.
zip runtime.zip bootstrap
- publish-layer-version 명령으로 레이어를 만들어요.
aws lambda publish-layer-version --layer-name bash-runtime --zip-file fileb://runtime.zip
이렇게 하면 레이어의 첫 번째 버전이 만들어져요.
함수 업데이트하기
함수에서 런타임 레이어를 쓰려면 함수가 레이어를 사용하도록 구성하고, 함수에서 런타임 코드를 제거해요.
- 함수 구성을 업데이트해 레이어를 가져오게 해요.
aws lambda update-function-configuration --function-name bash-runtime \
--layers arn:aws:lambda:us-east-1:123456789012:layer:bash-runtime:1
이렇게 하면 /opt 디렉터리의 함수에 런타임이 추가돼요. Lambda가 레이어의 런타임을 사용하게 하려면 다음 두 단계처럼 함수의 배포 패키지에서 bootstrap을 제거해야 해요.
- 함수 코드만 포함하는
.zip파일을 만들어요.
zip function-only.zip function.sh
- 함수 코드를 핸들러 스크립트만 포함하도록 업데이트해요.
aws lambda update-function-code --function-name bash-runtime --zip-file fileb://function-only.zip
- 런타임 레이어와 함께 동작하는지 확인하려고 함수를 호출해요.
aws lambda invoke --function-name bash-runtime --payload '{"text":"Hello"}' response.txt --cli-binary-format raw-in-base64-out
cli-binary-format 옵션은 AWS CLI version 2를 쓸 때 필요해요. 이걸 기본 설정으로 만들려면 aws configure set cli-binary-format raw-in-base64-out을 실행하세요. 자세한 내용은 AWS Command Line Interface User Guide for Version 2의 AWS CLI supported global command line options을 참고하세요.
다음과 같은 응답이 보여요.
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
- 응답을 확인해요.
cat response.txt
다음과 같은 응답이 보여요.
Echoing request: '{"text":"Hello"}'
런타임 업데이트하기
- 실행 환경에 대한 정보를 로그로 남기려면 런타임 스크립트가 환경 변수를 출력하도록 업데이트해요.
#!/bin/sh
set -euo pipefail
# Configure runtime to output environment variables
echo "## Environment variables:"
env
# Load function handler
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"
# Processing
while true
do
HEADERS="$(mktemp)"
# Get an event. The HTTP request will block until one is received
EVENT_DATA=$(curl -sS -LD "$HEADERS" "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
# Extract request ID by scraping response headers received above
REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
# Extract invocation ID for cross-wiring protection
INVOCATION_ID=$(grep -Fi Lambda-Runtime-Invocation-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
# Run the handler function from the script
RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")
# Send the response, echoing the invocation ID
curl "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE" \
${INVOCATION_ID:+--header "Lambda-Runtime-Invocation-Id: $INVOCATION_ID"}
done
- 새 버전의
bootstrap파일을 포함하는.zip파일을 만들어요.
zip runtime.zip bootstrap
bash-runtime레이어의 새 버전을 만들어요.
aws lambda publish-layer-version --layer-name bash-runtime --zip-file fileb://runtime.zip
- 함수가 레이어의 새 버전을 사용하도록 구성해요.
aws lambda update-function-configuration --function-name bash-runtime \
--layers arn:aws:lambda:us-east-1:123456789012:layer:bash-runtime:2
레이어 공유하기
레이어를 다른 AWS 계정과 공유하려면 레이어의 리소스 기반 정책(resource-based policy)에 크로스 계정 권한 문을 추가해요. add-layer-version-permission 명령을 실행하고 principal로 계정 ID를 지정해요. 각 문에서 단일 계정, 모든 계정, 또는 AWS Organizations의 조직에 권한을 부여할 수 있어요.
다음 예시는 계정 111122223333에 bash-runtime 레이어 버전 2에 대한 접근을 부여해요.
aws lambda add-layer-version-permission \
--layer-name bash-runtime \
--version-number 2 \
--statement-id xaccount \
--action lambda:GetLayerVersion \
--principal 111122223333 \
--output text
다음과 비슷한 출력이 보여요.
{"Sid":"xaccount","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111122223333:root"},"Action":"lambda:GetLayerVersion","Resource":"arn:aws:lambda:us-east-1:123456789012:layer:bash-runtime:2"}
권한은 단일 레이어 버전에만 적용돼요. 새 레이어 버전을 만들 때마다 이 과정을 반복해요.
정리하기
각 레이어 버전을 삭제해요.
aws lambda delete-layer-version --layer-name bash-runtime --version-number 1
aws lambda delete-layer-version --layer-name bash-runtime --version-number 2
함수가 레이어 버전 2에 대한 참조를 갖고 있으므로, 그 버전은 Lambda에 여전히 존재해요. 함수는 계속 동작하지만, 삭제된 버전을 사용하도록 함수를 더 이상 구성할 수는 없어요. 함수의 레이어 목록을 수정한다면 새 버전을 지정하거나 삭제된 레이어를 생략해야 해요.
delete-function 명령으로 함수를 삭제해요.
aws lambda delete-function --function-name bash-runtime