Lambda 함수를 코드레벨(node.js 기준)에서 다음과 같은 항목에 대해 이해한다.
Hello World 예제를 통해 살펴보자.
'use strict';
exports.lambdaHandler = async (event, context) => {
// 1. event
// 2. context
console.log('Inside Lambda function');
// 3. callback function
return {
'statusCode': 200,
'body': JSON.stringify({
message: 'hello world',
})
};
};
event
객체는 뭐하는 애인가?The contents of the event parameter include all of the data and metadata your Lambda function needs to drive its logic. For example, an event created by API Gateway will contain details related to the HTTPS request that was made by the API client (for example, path, query string, request body)
- AWS Document>
Lambda 의 Input/Output 그리고 실제 함수 코드 내에서의 값을 살펴보자.
{
"resource": "Resource path",
"path": "Path parameter",
"httpMethod": "Incoming request's method name"
"headers": {String containing incoming request headers}
"multiValueHeaders": {List of strings containing incoming request headers}
"queryStringParameters": {query string parameters }
"multiValueQueryStringParameters": {List of query string parameters}
"pathParameters": {path parameters}
"stageVariables": {Applicable stage variables}
"requestContext": {Request context, including authorizer-returned key-value pairs}
"body": "A JSON string of the request payload."
"isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encoded"
}
{
"message": "Hello me!",
"multiValueHeaders":{
'Accept':[
"*/*"
],
// 중략..
},
"pathParameters": {
"proxy": "hello/world"
},
"requestContext": {
"accountId": "12345678912",
"resourceId": "rofjd2",
"stage": "testStage",
"requestId": "deef4898-7610-11e6-8f14-25afc3e9ae23",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
// 중략
"sourceIp": "192.168.0.1",
"user": null
},
"resourcePath": "/{proxy+}",
"httpMethod": "GET",
"apiId": "gy415nuk2l"
},
"body": "{\r\n\t\"a\": 1\r\n}",
"isBase64Encoded": false
}
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
// header: 단일 값 헤더만 (값이 오로지 1개)
"headers": { "headerName": "headerValue", ... },
// multiValueHeaders: 값이 여러개
// header, multiValueHeaders 모두 지정했을 경우 multiValueHeaders 값만 표시
"multiValueHeaders": { "headerName": ["headerValue", "headerValue2", ...], ... },
"body": "..."
}
람다는 FaaS(Functino as a Service)로
한 람다 당 하나의 컨테이너에서 실행된다.
동시 람다 호출수가 급증할 때 (트레픽 급증) 컨테이너가 충분히 추가 되기 전에 호출되는 경우
429
(Too Many Request) || 500
(Internal Server Error) 를 반환한다.
- 한 컨테이너에서 한번의 전체 람다함수 호출
- 후속 요청시 핸들러 함수'만'호출
💡 위 매커니즘으로 인해 람다함수는 캐싱효과를 갖게된다.
// 핸들러 바깥 스코프 코드는 한 컨테이너 내에서 한번만 실행된다 (Caching 효과)
const dynamoDBClient = new DynamoDB.DocumentClient({region: process.env.AWS_REGION, apiVersion: "latest"});
// 핸들러 함수인 logger 내의 함수는 동일 컨테이너 내에서 여러번 호출 될 수 있다.
export async function logger (event: any) {
console.dir(event);
console.log('=========event end, body start=========')
//중략
}
위 DynamoDB.DocumentClient()
는 한 컨테이너당 한 번만 실행된다. 반면에 logger()
핸들러 함수는 후속 요청시 매번 호출된다.
👉 초기화 코드는 단 한번만 실행되고 필요한 핸들러 함수만 반복 호출함으로써 실행 속도 향상 🆙
lambda Cold Start 에서 코드 환경을 다운로드하고 패키지, 종속성, 전역 변수 초기화를 한다. 이에 임시 공간 (/tmp) 또한 초기화한다.
Lambda 는 /tmp
directory 를 파일 스토리지로 사용할 수 있으나, 임시로만 써야한다. Lambda도 일정 시간 이상 사용하지 않으면 회수된다.
당연히 이렇게 해야한다.
전역 변수가 handler 외부에 위치하면 재사용이 일어난다.
즉 Cold Start 이후에 Warm start 할 때는 handler 외부에 있던 전역변수를 재사용 할 수 있다.
// 클라이언트 초기화 (Cold Start)
import {DynamoDBClient} from '@aws-sdk/client-dynamodb';
// 전역변수 초기화 (Cold Start)
const ddbClient = new DynamoDBClient({region: "ap-northeast-2"});
export async function handler(event, context) {
// 이미 초기화된 전역변수 ddbClient 사용 (Warm Start)
const response = await ddbClient.send();
}
특히 RDS 의 경우 AWS RDS Porxy EndPoint 사용이 거의 필수다.
DB Porxy 는 Connection Pooling 을 지원한다.
DynamoDB 는 HTTP Based 로 별도 커넥션풀 운용이 필요 없다. (Serverless Architecture 에서 DynamoDB 가 사랑받는 이유중 하나기도 하다.)
각각의 lambda 가 서로 다른 connection 객체를 생성하는 것은 매우 연산이 비싸 DB에 과도한 부하를 줄 수 있다.
Lambda - CloudWatch 대신 로컬에서 디버깅 & 테스트 할 수 있게하는 환경을 제공한다.
배포 패키지 크기가 커질수록 File Tracker (람다 Container 를 배포하는 매커니즘) 의 배포 프로세스가 무거워진다.
반드시 배포 패키지를 최소화하라.
Cold Start 의 로드 타임을 줄여준다.
상태 관리는 최악임. 외부 저장소를 사용하라.