관측(observability), 로그와 지표와 추적은 어떻게 다를까
장애가 지나간 뒤 원인을 찾으려면 그 전에 남겨 둔 기록이 있어야 한다.
로그, 지표, 추적이 각각 어떤 질문에 답하는지, 라벨 하나로 지표가 왜 불어나는지, 기록을 얼마나 오래 둘지 다룬다.
이 글의 목차관측은 밖으로 나온 기록으로 안을 알아내는 일이다6
서비스를 처음 배포하면 AWS의 모니터링 서비스인 CloudWatch에서 CPU 사용률 그래프를 보게 되고, 서버에는 로그 파일이 쌓인다.
평소에는 잘 들여다보지 않는다.
그러다 사용자가 “아까 결제가 안 됐어요”라고 알려 오면 그제야 기록을 연다.
그리고 그 요청이 애초에 기록됐는지, 원인을 알 만큼 자세히 남았는지, 보관 기간이 지나 지워지지는 않았는지를 차례로 확인하게 된다.
장애가 끝난 뒤에는 이 셋 가운데 무엇도 바꿀 수 없다.
무엇을 얼마나 남길지는 미리 정해 둬야 한다.
관측은 밖으로 나온 기록으로 안을 알아내는 일이다
관측(observability)은 시스템이 밖으로 내보낸 기록만 보고 안에서 무슨 일이 있었는지 알아낼 수 있는 정도를 말한다.
관측이 잘 되는 시스템은 내부 동작을 몰라도 밖에서 질문을 던져 이해할 수 있다.
그러려면 애플리케이션이 추적, 지표, 로그 같은 신호를 내보내야 한다.
이런 신호를 남기는 방식을 표준으로 맞추는 오픈소스 프로젝트가 OpenTelemetry다.
로그, 지표, 추적은 답하는 질문이 다르다
로그는 그때 무슨 일이 있었는지를 사건 하나하나 적은 기록이다.
시각과 함께 “주문 결제 실패, 결제 대행사 응답 시간 초과” 같은 내용이 남는다.
지표는 요청 수, 오류 수, 응답 시간 같은 수치를 일정 간격으로 모은 것이다.
시간을 따라 이어지는 수치의 줄을 시계열이라고 부른다.
추적은 요청 하나가 여러 서비스를 지나며 어디서 얼마나 시간을 썼는지 이어 붙인 기록이다.
웹 서버, 결제 서비스, 데이터베이스를 거친 요청이라면 각 구간의 시간이 한 줄로 이어진다.
문제는 하나로 다른 것을 대신하려 할 때 생긴다.
흔한 예가 로그로 오류율을 구하려는 경우다.
오류가 났을 때만 로그를 남기면 오류 건수는 셀 수 있어도 전체 요청 수가 없어서 비율이 나오지 않는다.
반대로 지표만 있으면 오류율이 3퍼센트라는 것은 알아도 그 3퍼센트가 왜 실패했는지는 알 수 없다.
그 답은 로그나 추적에 있다.
로그는 오랫동안 정해진 형식이 없었다
유닉스 계열에서 로그를 모으는 전통적인 방식은 syslog다.
버클리의 BSD 유닉스에서 처음 만들어져 여러 운영체제와 네트워크 장비로 퍼졌다.
syslog를 다룬 RFC 3164는 2001년에 나왔다.
RFC는 인터넷 기술을 정리해 공개하는 문서인데, RFC 3164는 현장에서 관찰한 동작을 적어 둔 정보성 문서였다.
정식 표준은 2009년 RFC 5424가 나오면서 생겼다.
프로그램마다 제각각 남기던 형식을 나중에 묶었기 때문에, 지금도 여러 곳의 로그를 모으려면 형식을 맞추는 데 손이 많이 간다.
RFC 5424에는 메시지 안에 이름과 값의 쌍을 담는 구조화 데이터 자리도 따로 마련돼 있다.
새로 로그를 남긴다면 사람이 읽는 문장보다 JSON처럼 필드가 나뉜 구조로 남기는 편이 낫다.
요청 ID, 걸린 시간, 결과를 각각 필드로 두면 나중에 조건을 걸어 찾기 쉽다.
요청마다 고유한 ID를 붙여 로그에 함께 남기면, 여러 서비스에 흩어진 로그를 그 ID로 이어 볼 수도 있다.
라벨을 붙일수록 시계열이 늘어난다
지표 쪽에서 널리 쓰이는 오픈소스 도구가 Prometheus다.
Prometheus에서는 지표에 라벨을 붙여 원하는 기준으로 잘라 본다.
응답 시간에 화면 이름과 상태 코드를 라벨로 붙이면 화면별로도, 상태 코드별로도 나눠 볼 수 있다.
편리한 만큼 함정도 있다.
라벨 값의 고유한 조합마다 새 시계열이 생긴다.
그래서 Prometheus는 사용자 ID처럼 값이 끝없이 늘어나는 것을 라벨로 쓰지 말라고 경고한다.
화면 20종과 상태 코드 5종을 라벨로 붙이면 조합은 최대 100개다.
여기에 사용자 ID를 더하면 사용자 수만큼 다시 곱해지고, 저장해야 할 데이터도 그만큼 크게 늘어난다.
화면 3종·상태 2종에 예시 사용자 구분을 더하면 시계열이 늘어난다.
라벨 조합
3 × 2서로 다른 시계열
6개라벨 조합
3 × 2 × 4서로 다른 시계열
24개같은 지표라도 서로 다른 라벨 조합마다 시계열이 생긴다. 3 × 2의 여섯 조합에 사용자 네 명이 모두 들어가면 24개다.
여섯 조합을 모두 기록하면 시계열도 여섯 개다.
모든 조합이 실제로 기록된다는 가정이다. 실제 시계열 수는 관측한 조합에 달려 있고, 저장소 메모리를 계산한 값은 아니다.
먼저 볼 네 가지 신호
Google의 서비스 운영 엔지니어(SRE)들이 쓴 책은 사용자를 상대하는 시스템에서 네 가지만 잴 수 있다면 지연, 트래픽, 오류, 포화를 보라고 한다.
지연은 응답에 걸린 시간, 트래픽은 요청의 양, 오류는 실패한 요청이고, 포화는 CPU나 메모리 같은 자원이 얼마나 찼는지를 뜻한다.
그런데 포화를 보려고 EC2의 지표 목록을 열면 메모리 항목이 보이지 않는다.
EC2가 기본으로 보내는 지표에는 운영체제 안의 메모리 사용량과 디스크 사용량이 없다.
이 값을 보려면 CloudWatch 에이전트를 설치해 따로 보내야 한다.
신호가 오지 않는 상태도 따로 다뤄야 한다.
알람이 울리지 않았다는 것은 정상이라는 뜻일 수도 있고, 수집이 끊겼다는 뜻일 수도 있다.
둘을 구분하는 방법은 CloudWatch 알람과 누락 데이터 글에서 다룬다.
얼마나 오래 남길까
기록은 오래 남길수록 저장 비용이 든다.
그래서 자세한 기록은 짧게, 요약한 수치는 길게 두는 경우가 많다.
CloudWatch의 지표 보관 기간도 이런 식으로 나뉜다.
1분 간격으로 모은 지표는 15일까지는 1분 단위로 볼 수 있고, 63일까지는 5분 단위로, 455일까지는 1시간 단위로 합쳐진 값만 남는다.
석 달 전의 지표는 1시간 단위 값만 남아 있다.
그 안에서 몇 분 동안 있었던 일을 따로 짚어 내기는 어렵다.
로그는 반대로 기본값이 문제가 되기도 한다.
CloudWatch Logs는 로그를 로그 그룹 단위로 묶어 보관 기간을 정하는데, 따로 정하지 않으면 기한 없이 보관한다.
그런 로그 그룹은 계속 쌓이고, 저장 비용도 함께 늘어난다.
실무에서는 로그 그룹마다 보관 기간을 정해 두고, 몇 달에 걸쳐 볼 추세는 지표로 남긴다.
기간을 정할 때는 문제를 늦게 알아차리는 경우도 셈에 넣는다.
예를 들어 로그를 사흘만 두는 서비스에 문제가 생기고 일주일 뒤에 문의가 들어오면, 그 요청의 기록은 이미 지워져 있다.