라벨 하나가 모니터링을 통째로 죽인다 — 지표 카디널리티 폭발
디버깅하기 쉬우라고 지표에 사용자 ID 라벨을 붙였더니, 몇 시간 뒤 모니터링 시스템이 메모리를 다 쓰고 죽었어요. 라벨 하나가 왜 그렇게 무거운지, 넣어도 되는 라벨을 가르는 법을 알 수 있어요.
지표에 라벨을 하나 추가했어요. 어느 사용자의 요청인지 알면 디버깅이 쉬워질 것 같아서요.
배포하고 몇 시간 뒤 모니터링 시스템이 죽었어요. 메모리를 다 쓰고 응답을 안 해요. 재시작해도 다시 죽고요.
그런데 우리 서비스는 멀쩡해요. 장애가 나도 알 수 없는 상태일 뿐이죠.
지표 하나가 아니라 지표를 담는 시스템이 죽은 거예요.
그리고 이건 라벨의 값이 몇 개나 되는지에 달려 있어요. 라벨을 하나 붙였다는 사실이 아니라, 그 라벨이 몇 종류의 값을 갖느냐가 전부예요.
종류가 곧 곱셈의 크기고, 그 곱셈이 감당 범위를 넘는 순간 관측 능력이 통째로 사라집니다.
왜 그런가
시계열 데이터베이스는 라벨 조합마다 별도의 시계열을 만들어요.
요청 수를 세는 지표에 메서드 라벨(5종)과 상태 코드 라벨(10종)을 붙이면 50개의 시계열이 생겨요. 여기에 엔드포인트 라벨(100종)을 더하면 5,000개가 되고요. 곱셈이에요.
여기에 사용자 ID 를 붙이면 어떻게 될까요. 사용자가 10만 명이면 시계열이 5억 개가 돼요. 각 시계열이 메모리에 색인을 갖고 있으니 그것만으로 시스템이 무너집니다.
값의 종류가 많은 라벨을 붙이면 안 되는 이유예요. 사용자 ID, 요청 ID, 이메일, 세션 ID, 정규화하지 않은 URL 경로, 오류 메시지 전문 같은 것들이요.
특히 위험한 게 무한히 늘어나는 값이에요. 사용자 수는 유한하지만 계속 늘어요. 요청 ID 는 요청마다 새로워요. 이런 라벨을 붙이면 시계열이 계속 늘어나기만 하고 절대 줄지 않아요.
그리고 지난 데이터도 문제예요. 한 번 만들어진 시계열은 보관 기간이 지날 때까지 남아요. 라벨을 되돌려도 즉시 회복되지 않습니다.
정리하면 라벨은 곱셈으로 늘어나고, 값의 종류가 곧 비용이에요.
코드로 보면
라벨에 넣어도 되는 것과 안 되는 것:
· 넣어도 되는 것 — 값의 종류가 적고 안 늘어난다
method(GET/POST…) · status(2xx/4xx/5xx)
정규화한 경로(/users/:id) · 환경 · 리전
넣으면 안 되는 것 — 종류가 많거나 무한히 는다
user_id · request_id · email · session_id
정규화 안 한 경로(/users/12345)
오류 메시지 전문 · 타임스탬프
· 그런 정보가 필요하면 지표가 아니라 로그·추적으로
지표: 얼마나 · 몇 번 (집계)
로그·추적: 누가 · 무엇을 (개별)
· 시계열 수에 상한을 걸어 둔다
지표당 최대 라벨 조합 수를 제한
넘으면 그 지표를 버리고 경고한다✕ 문제 지점 · ✓ 해법 · 그대로 돌려 보기 전에 자기 환경에 맞게 고쳐 주세요.
실전에서는
역할을 나누는 게 핵심이에요. 지표는 집계, 로그와 추적은 개별.
지표는 "얼마나, 몇 번"에 답해요. 요청이 초당 몇 건인지, 오류율이 몇 퍼센트인지요. 여기에 개별 식별자는 필요 없어요.
"누가, 무엇을"이 필요하면 로그나 추적을 봐야 해요. 그쪽은 개별 항목을 다루도록 설계돼 있어서 식별자를 담아도 괜찮아요. 대신 조회 방식과 비용 구조가 다릅니다.
이 구분을 지키면 라벨 폭발이 안 일어나요. 지키지 않으면 지표 시스템에 로그의 일을 시키는 셈이고, 그건 구조적으로 감당이 안 돼요.
경로 정규화가 실무에서 가장 자주 필요한 처리예요. /users/12345 를 그대로 라벨에 넣으면 사용자 수만큼 시계열이 생겨요. /users/:id 로 정규화해야 합니다. 많은 프레임워크가 라우트 패턴을 제공하니 그걸 쓰면 돼요.
오류 메시지도 조심해야 해요. 메시지 전문을 라벨에 넣으면 변수가 섞인 만큼 종류가 늘어요. 오류 종류를 몇 개의 범주로 나눠 그것만 라벨로 쓰세요.
방어 장치도 필요해요. 시계열 수에 상한을 걸어 두면, 실수로 나쁜 라벨을 붙여도 시스템 전체가 죽지 않아요. 그 지표만 버려지고 경고가 나오죠. 이게 없으면 한 번의 실수가 전체 관측 능력을 없앱니다.
그리고 비용 관점도 있어요. 앞 편에서 본 대로 관측 비용이 상당한데, 그 비용은 시계열 수에 비례해요. 라벨을 함부로 늘리면 청구서에 바로 나타납니다.
처방
- 라벨에는 값의 종류가 적고 늘지 않는 것만 넣으세요.
- 사용자 ID·요청 ID·이메일·세션 ID 를 라벨에 절대 넣지 마세요.
- 경로를 정규화하세요. 프레임워크의 라우트 패턴을 쓰면 됩니다.
- 오류는 메시지 전문이 아니라 몇 개의 범주로 나눠 라벨하세요.
- 개별 정보가 필요하면 로그나 추적을 쓰세요. 지표는 집계용입니다.
- 시계열 수에 상한을 걸어 두세요. 한 번의 실수가 전체를 죽이지 않게요.
- 지표별 시계열 수를 정기적으로 보세요. 상위 지표가 대개 문제입니다.
- 새 라벨을 추가할 때 "이 값의 종류가 몇 개인가"를 반드시 묻는 규칙을 만드세요.
- 지표 이름을 동적으로 만들지 마세요. 라벨과 같은 문제인데 다루기는 더 어렵습니다.
- 히스토그램에 라벨을 붙일 때는 훨씬 더 조심하세요. 버킷 수만큼 곱해지니 다른 지표의 수십 배로 늘어납니다.
지금 확인해 보세요
시계열이 몇 개인가:
· 전체 시계열 수와 추세
계속 늘기만 하면 무한히 늘어나는 라벨이 있다
· 지표별 시계열 수 상위
대개 소수의 지표가 대부분을 차지한다
히스토그램은 버킷 수만큼 곱해지니 특히 크다
· 라벨별 값의 종류 수
종류가 수천을 넘는 라벨이 있으면 그게 원인이다
· 경로가 정규화되어 있는가
/users/12345 가 보이면 정규화가 안 된 것이다
· 시계열 상한이 걸려 있는가
없으면 한 번의 실수가 전체 관측을 없앤다
· 새 라벨 추가 시 확인 절차가 있는가
'이 값의 종류가 몇 개인가' 를 묻는 규칙핵심 한 줄라벨은 곱셈으로 늘어난다. 값의 종류가 곧 비용이다.