AWS Lambda 기반 서버리스 Observability
분산 시스템과 서버리스 컴퓨팅 환경에서 Observability를 확보하는 것은 애플리케이션의 안정성과 성능을 보장하는 핵심 요소입니다. 이는 전통적인 모니터링을 넘어서는 개념으로, Amazon CloudWatch와 AWS X-Ray 같은 AWS Observability 도구를 활용하면 서버리스 애플리케이션에 대한 깊은 인사이트를 확보하고, 문제를 해결하며, 애플리케이션 성능을 최적화할 수 있습니다. 이 가이드에서는 Lambda 기반 서버리스 애플리케이션의 Observability를 구현하기 위한 핵심 개념, 도구, 모범 사례를 다룹니다.
인 프라 또는 애플리케이션에 Observability를 구현하기 전 첫 번째 단계는 핵심 목표를 결정하는 것입니다. 사용자 경험 향상, 개발자 생산성 증대, 서비스 수준 목표(SLO) 달성, 비즈니스 수익 증가, 또는 애플리케이션 유형에 따른 기타 특정 목표가 될 수 있습니다. 이러한 핵심 목표를 명확히 정의하고 어떻게 측정할 것인지를 확립한 다음, 그로부터 역순으로 Observability 전략을 설계하세요. 자세한 내용은 "중요한 것을 모니터링하기"를 참고하세요.
Observability의 핵심 축
Observability에는 세 가지 핵심 축이 있습니다:
- 로그: 애플리케이션이나 시스템 내에서 발생한 개별 이벤트의 타임스탬프가 기록된 기록으로, 실패, 오류, 상태 변환 등이 포함됩니다
- 메트릭: 다양한 시간 간격으로 측정된 수치 데이터(시계열 데이터)로, SLI(요청률, 오류율, 지속 시간, CPU% 등)가 해당됩니다
- 트레이스: 여러 애플리케이션과 시스템(주로 마이크로서비스)을 거치는 단일 사용자의 요청 경로를 나타냅니다
AWS는 AWS Lambda 애플리케이션에 대한 로깅, 메트릭 모니터링, 트레이싱을 지원하여 실행 가능한 인사이트를 제공하기 위해 네이티브 도구와 오픈소스 도구를 모두 제공합니다.
로그
이 Observability 모범 사례 가이드의 이번 섹션에서는 다음 주제를 심층적으로 다룹니다:
- 비정형 로그 vs 정형 로그
- CloudWatch Logs Insights
- 상관관계 ID 로깅
- Lambda Powertools를 활용한 코드 예제
- CloudWatch Dashboards를 활용한 로그 시각화
- CloudWatch 로그 보관 기간
로그는 애플리케이션 내에서 발생한 개별 이벤트입니다. 실패, 오류, 실행 경로 등의 이벤트가 포함될 수 있으며, 비정형, 반정형, 또는 정형 형식으로 기록됩니다.
비정형 로그 vs 정형 로그
개발자들은 흔히 print나 console.log 문을 사용하여 간단한 로그 메시지를 작성하는 것부터 시작합니다. 이러한 로그는 대규모로 프로그래밍 방식으로 파싱하고 분석하기 어렵습니다. 특히 여러 로그 그룹에 걸쳐 많은 로그 메시지를 생성하는 AWS Lambda 기반 애플리케이션에서는 더욱 그렇습니다. 결과적으로 CloudWatch에서 이러한 로그를 통합하는 것이 어렵고 분석이 힘들어집니다. 관련 정보를 찾으려면 텍스트 매칭이나 정규 표현식을 사용해야 합니다. 다음은 비정형 로깅의 예시입니다:
[2023-07-19T19:59:07Z] INFO Request started
[2023-07-19T19:59:07Z] INFO AccessDenied: Could not access resource
[2023-07-19T19:59:08Z] INFO Request finished
보시다시피, 로그 메시지에 일관된 구조가 없어 유용한 인사이트를 도출하기 어렵습니다. 또한 컨텍스트 정보를 추가하기도 어렵습니다.
반면 정형 로깅은 JSON과 같은 일관된 형식으로 정보를 기록하는 방식으로, 로그를 텍스트가 아닌 데이터로 취급할 수 있어 쿼리와 필터링이 간편해집니다. 개발자는 로그를 프로그래밍 방식으로 효율적으로 저장, 검색, 분석할 수 있으며 디버깅도 수월해집니다. 정형 로깅은 로그 레벨을 통해 서로 다른 환경에서 로그의 상세 수준을 쉽게 조절할 수 있는 방법을 제공합니다. 로그 레벨에 주의를 기울이세요. 과도한 로깅은 비용을 증가시키고 애플리케이션 처리량을 감소시킵니다. 개인 식별 정보는 로깅 전에 반드시 마스킹하세요. 다음은 정형 로깅의 예시입니다:
{
"correlationId": "9ac54d82-75e0-4f0d-ae3c-e84ca400b3bd",
"requestId": "58d9c96e-ae9f-43db-a353-c48e7a70bfa8",
"level": "INFO",
"message": "AccessDenied",
"function-name": "demo-observability-function",
"cold-start": true
}
정형 로깅을 사용하여 CloudWatch 로그에 중앙 집중화하는 것을 권장합니다. 이를 통해 트랜잭션에 대한 운영 정보, 서로 다른 컴포넌트 간의 상관관계 식별자, 애플리케이션의 비즈니스 결과를 기록하세요.
CloudWatch Logs Insights
CloudWatch Logs Insights를 사용하면 JSON 형식의 로그에서 필드를 자동으로 검색할 수 있습니다. 또한, JSON 로그를 확장하여 애플리케이션에 특화된 커스텀 메타데이터를 로깅할 수 있으며, 이를 통해 로그를 검색, 필터링, 집계할 수 있습니다.
상관관계 ID 로깅
예를 들어, API Gateway를 통해 들어오는 HTTP 요청의 경우 상관관계 ID는 requestContext.requestId 경로에 설정되며, Lambda Powertools를 사용하여 다운스트림 Lambda 함수에서 쉽게 추출하고 로깅할 수 있습니다. 분산 시스템에서는 여러 서비스와 컴포넌트가 함께 작동하여 요청을 처리하므로, 상관관계 ID를 로깅하고 다운스트림 시스템에 전달하는 것이 종단 간 트레이싱과 디버깅에 매우 중요합니다. 상관관계 ID는 요청의 시작 시점에 할당되는 고유 식별자입니다. 요청이 여러 서비스를 거치면서 상관관계 ID가 로그에 포함되므로, 요청의 전체 경로를 추적할 수 있습니다. AWS Lambda 로그에 상관관계 ID를 수동으로 삽입하거나, AWS Lambda Powertools와 같은 도구를 사용하여 API Gateway에서 상관관계 ID를 쉽게 추출하고 애플리케이션 로그와 함께 기록할 수 있습니다. 예를 들어, HTTP 요청의 상관관계 ID는 API Gateway에서 시작된 request-id가 될 수 있으며, 이를 Lambda 함수와 같은 백엔드 서비스로 전달할 수 있습니다.
Lambda Powertools를 활용한 코드 예제
모범 사례로서, 서버리스 애플리케이션의 진입점인 API Gateway나 Application Load Balancer에서 가능한 한 빨리 요청 수명 주기 초기에 상관관계 ID를 생성하세요. UUID, request ID, 또는 분산 시스템에서 요청을 추적할 수 있는 기타 고유 속성을 사용하세요. 각 요청과 함께 상관관계 ID를 커스텀 헤더, 본문 또는 메타데이터의 일부로 전달하세요. 다운스트림 서비스의 모든 로그 항목과 트레이스에 상관관계 ID가 포함되도록 하세요.
Lambda 함수 로그에 상관관계 ID를 수동으로 캡처하여 포함하거나, AWS Lambda Powertools를 사용할 수 있습니다. Lambda Powertools를 사용하면 지원되는 업스트림 서비스에 대한 사전 정의된 요청 경로 매핑에서 상관관계 ID를 쉽게 추출하여 애플리케이션 로그에 자동으로 추가할 수 있습니다. 또한, 장 애 발생 시 근본 원인을 쉽게 디버깅하고 식별하여 원래 요청으로 추적할 수 있도록 모든 오류 메시지에 상관관계 ID를 포함하세요.
아래 서버리스 아키텍처에서 상관관계 ID를 사용한 정형 로깅을 시연하고 CloudWatch에서 확인하는 코드 예제를 살펴보겠습니다:

// Initializing Logger
Logger log = LogManager.getLogger();
// Uses @Logger annotation from Lambda Powertools, which takes optional parameter correlationIdPath to extract correlation Id from the API Gateway header and inserts correlation_id to the Lambda function logs in a structured format.
@Logging(correlationIdPath = "/headers/path-to-correlation-id")
public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) {
...
// The log statement below will also have additional correlation_id
log.info("Success")
...
}
이 예제에서는 Java 기반 Lambda 함수가 Lambda Powertools 라이브러리를 사용하여 API Gateway 요청에서 들어오는 correlation_id를 로깅합니다.
위 코드 예제의 CloudWatch 로그 샘플:
{
"level": "INFO",
"message": "Success",
"function-name": "demo-observability-function",
"cold-start": true,
"lambda_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72",
"correlation_id": "<correlation_id_value>"
}_
CloudWatch Dashboards를 활용한 로그 시각화
데이터를 정형 JSON 형식으로 로깅하면, CloudWatch Logs Insights가 JSON 출력에서 값을 자동으로 검색하고 메시지를 필드로 파싱합니다. CloudWatch Logs Insights는 전 용 SQL 유사 쿼리 언어를 제공하여 여러 로그 스트림을 검색하고 필터링할 수 있습니다. glob 및 정규 표현식 패턴 매칭을 사용하여 여러 로그 그룹에 걸쳐 쿼리를 수행할 수 있습니다. 또한 커스텀 쿼리를 작성하고 저장하여 매번 다시 만들지 않고도 재실행할 수 있습니다.
CloudWatch Logs Insights에서는 하나 이상의 집계 함수를 포함하는 쿼리로 라인 차트, 막대 차트, 누적 영역 차트와 같은 시각화를 생성할 수 있습니다. 그런 다음 이러한 시각화를 CloudWatch Dashboards에 쉽게 추가할 수 있습니다. 아래 샘플 대시보드는 Lambda 함수 실행 시간의 백분위수 보고서를 보여줍니다. 이러한 대시보드를 통해 애플리케이션 성능 개선에 집중해야 할 부분을 빠르게 파악할 수 있습니다. 평균 지연 시간도 유용한 메트릭이지만, 평균 지연 시간이 아닌 p99를 최적화하는 것을 목표로 해야 합니다.
플랫폼, 함수, 확장 로그를 CloudWatch 외의 다른 위치로 보내려면, Lambda Extensions와 함께 Lambda Telemetry API를 사용할 수 있습니다. 다수의 파트너 솔루션이 Lambda Telemetry API를 사용하는 Lambda 레이어를 제공하여 해당 시스템과의 통합을 쉽게 해줍니다.
CloudWatch Logs Insights를 최대한 활용하려면, 정형 로깅 형태로 로그에 어떤 데이터를 수집해야 하는지 고민하세요. 이를 통해 애플리케이션의 상태를 더 효과적으로 모니터링할 수 있습니다.
CloudWatch 로그 보관 기간
기본적으로 Lambda 함수에서 stdout에 기록된 모든 메시지는 Amazon CloudWatch 로그 스트림에 저장됩니다. Lambda 함수의 실행 역할에는 CloudWatch 로그 스트림을 생성하고 스트림에 로그 이벤트를 기록할 수 있는 권한이 있어야 합니다. CloudWatch 요금은 수집된 데이터 양과 사용된 스토리지에 따라 청구된다는 점을 인지하는 것이 중요합니다. 따라서 로깅 양을 줄이면 관련 비용을 최소화할 수 있습니다. 기본적으로 CloudWatch 로그는 무기한 보관되며 만료되지 않습니다. 로그 스토리지 비용을 줄이기 위해 로그 보관 정책을 구성하는 것이 권장되며, 모든 로그 그룹에 적용하세요. 환경별로 서로 다른 보관 정책을 원할 수 있습니다. 로그 보관 기간은 AWS 콘솔에서 수동으로 구성할 수 있지만, 일관성과 모범 사례를 보장하려면 Infrastructure as Code(IaC) 배포의 일부로 구성해야 합니다. 다음은 Lambda 함수의 로그 보관 기간을 구성하는 CloudFormation 템플릿 예제입니다:
Resources:
Function:
Type: AWS::Serverless::Function
Properties:
CodeUri: .
Runtime: python3.8
Handler: main.handler
Tracing: Active
# Explicit log group that refers to the Lambda function
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/${Function}"
# Explicit retention time
RetentionInDays: 7
이 예제에서는 Lambda 함수와 해당 로그 그룹을 생성했습니다. RetentionInDays 속성이 7일로 설정되어 있어, 이 로그 그룹의 로그는 7일 동안 보관된 후 자동으로 삭제되므로 로그 스토리지 비용을 관리하는 데 도움이 됩니다.
메트릭
이 Observability 모범 사례 가이드의 이번 섹션에서는 다음 주제를 심층적으로 다룹니다:
- 기본 제공 메트릭 모니터링 및 알림 설정
- 커스텀 메트릭 게시
- Embedded Metrics를 사용하여 로그에서 메트릭 자동 생성
- CloudWatch Lambda Insights를 사용한 시스템 수준 메트릭 모니터링
- CloudWatch 알람 생성