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 함수와 같은 백엔드 서비스로 전달할 수 있습니다.