LabHub
배우기 러닝패스 코스

可観測性

ラベルにuser_idを入れてはいけない理由

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

메트릭의 시계열 개수는 라벨 값 조합의 입니다. 라벨 하나를 잘못 고르면 시계열이 수백만 개가 되고, 그 순간 모니터링 시스템이 관측 대상보다 먼저 죽습니다.

概念マップ: 시계열 개수・곱・1,600개・1억 6천만 개

왜 이게 필요했나

http_requests_total 에 라벨을 붙입니다.

http_requests_total{method, status, endpoint}

시계열 개수는 5 × 8 × 40 = 1,600개. 괜찮습니다.

여기에 "사용자별로 보고 싶다" 는 요청이 들어와 user_id 를 추가합니다. 사용자가 10만 명이면,

5 × 8 × 40 × 100,000 = 1억 6천만 개.

프로메테우스는 시계열마다 메모리에 인덱스를 유지합니다. 서버가 OOM 으로 죽고, 그러면 장애를 관측할 수단이 장애 때문에 사라집니다.

카디널리티 폭발을 만드는 라벨들

절대 라벨에 넣지 말아야 할 것들입니다.

라벨 왜 위험한가
user_id, session_id 사용자 수만큼 증가. 상한이 없다
request_id, trace_id 요청마다 고유. 무한
email, ip 사실상 무한 + 개인정보
timestamp 시간마다 새 시계열 (시계열에 시간을 넣는 모순)
url (쿼리스트링 포함) ?page=1, ?page=2… 무한
에러 메시지 전문 메시지에 값이 섞이면 무한

특히 마지막. error="connection to 10.0.3.17:5432 timed out" 처럼 IP·포트가 들어간 메시지를 라벨로 쓰면 조합이 폭발합니다. error="db_timeout" 처럼 분류된 값을 씁니다.

판단 기준 — 이 라벨의 값은 몇 가지인가

라벨을 추가하기 전에 스스로 묻습니다.

  1. 가능한 값이 몇 개인가? 세어서 답할 수 있어야 합니다. "많아요" 는 답이 아닙니다.
  2. 시간이 지나면 늘어나는가? 늘어난다면 상한이 있는가?
  3. 이 라벨로 실제 알림을 만들 것인가? 대시보드에서 한 번 볼 것 같다면 메트릭이 아니라 로그나 트레이스로 가야 합니다.

경험칙으로 라벨 하나당 값 100개 이하, 메트릭 하나당 시계열 1만 개 이하를 목표로 잡습니다.

URL 은 반드시 정규화한다

# 나쁨 — 주문 개수만큼 시계열
endpoint="/api/orders/8f3a91"

# 좋음 — 라우트 패턴
endpoint="/api/orders/:id"

프레임워크의 라우트 정의를 쓰면 자동으로 정규화됩니다. 직접 문자열을 쓰면 반드시 폭발합니다. 이건 실수의 문제가 아니라 시간 문제입니다.

세 신호를 나눠 쓴다

높은 카디널리티 정보가 정말 필요할 때가 있습니다. "이 사용자의 요청이 왜 느렸나" 같은 질문입니다. 그건 메트릭의 일이 아닙니다.

신호 카디널리티 답하는 질문
메트릭 낮아야 함 "얼마나 많이/빠르게?" 추세와 알림
로그 높아도 됨 "그때 무슨 일이?" 개별 사건
트레이스 높아도 됨 "시간이 어디서 갔나?" 요청 하나의 경로

메트릭으로 알림을 받고 → 시간을 특정하고 → 로그·트레이스로 개별 요청을 파고듭니다. 세 신호는 대체재가 아니라 조사 순서입니다.

이미 폭발했다면

현장에서 만나는 모습

이미 터진 뒤에 되돌리는 순서

카디널리티는 늘 사고가 난 뒤에 알게 된다. 프로메테우스가 메모리를 다 쓰고 죽거나, 질의가 시간 초과로 안 돌아온다. 그때 순서가 있다.

먼저 무엇이 얼마나 차지하는지 센다. 프로메테우스의 상태 화면(/tsdb-status)에 가장 많은 시계열을 만든 지표와 라벨이 나온다. 명령으로도 볼 수 있다.

topk(10, count by (__name__)({__name__=~".+"}))
count(app_request_duration_seconds_bucket)
count(count by (user_id)(app_request_total))

마지막 줄이 그 라벨의 실제 값 가지 수다. 이 숫자가 수천이면 그 라벨 하나가 원인이다.

막는 것은 저장 직전에 한다. 애플리케이션을 고쳐 배포하는 것이 정답이지만 시간이 걸린다. 그동안은 스크레이프 설정에서 라벨을 지우거나 지표를 통째로 버린다.

metric_relabel_configs:
  - source_labels: [__name__]
    regex: 'app_request_total'
    target_label: user_id
    replacement: ''            # 라벨 값을 비운다
  - source_labels: [__name__]
    regex: 'debug_.*'
    action: drop               # 이 지표는 아예 저장하지 않는다

이미 저장된 시계열은 저절로 사라지지 않는다. 새로 안 들어와도 보존 기간 동안 남아 메모리를 쓴다. 급하면 관리 API 로 지운다(--web.enable-admin-api 가 켜져 있어야 하고, 지우면 되돌릴 수 없다).

히스토그램은 조용히 배가 된다. 버킷 하나가 곧 시계열 하나다. 버킷 12개짜리 히스토그램에 라벨 세 개를 곱하면 순식간에 수만이 된다. 라벨을 줄일 수 없다면 버킷 수를 먼저 줄인다.

같은 실수를 막는 것은 배포 전 검사다. 지표 이름과 라벨 목록을 코드에서 뽑아, 허용 목록에 없는 라벨이 붙으면 CI 에서 막는다. 사람의 주의력에 기대면 바쁜 주에 반드시 다시 들어온다.

다음 확인에서 볼 것

이어지는 퀴즈에서는 라벨 값의 곱으로 시계열 수를 계산하고, 메트릭과 로그·트레이스의 역할을 구분합니다. 이미 폭발이 시작된 상황에서 수집 단계 완화와 애플리케이션의 근본 수정 중 무엇을 먼저 적용할지도 판단해 보세요.