05 · 쿼리·운영 컴포넌트 — vmselect / vmalert / vmauth
- vmselect는 저장을 하지 않는 stateless 쿼리 엔진입니다. 모든 vmstorage에 Fanout으로 던지고 돌아온 결과를 Merge·Sort해 반환합니다.
- 쿼리는 3-Prefix(태그→Metric ID→TSID→값·이름 복원)로 풀립니다. 쓰기 시점 정규화를 역방향으로 되짚는 대칭 구조입니다.
- 메모리 관리 3포인트: 쿼리 시점 dedup, Rollup Result Cache(허용 메모리 12.5%, 최근 5분 제외), Query Latency Offset(기본 30초, 정확성↔실시간성 트레이드오프).
- vmalert는 Recording rules로 무거운 집계를 미리 계산해 조회 부하를 쓰기 시점으로 옮깁니다. vmauth는 멀티 클러스터 앞단 라우팅 게이트웨이로 사용자 배포 없이 장애 전환을 처리합니다.
데이터가 들어가는 길(03 수집·04 저장)은 이미 다뤘습니다. 이 문서는 데이터가 빠지는 길, 그리고 그 주변에 붙는 운영 컴포넌트를 봅니다. 쿼리 엔진 vmselect, 지표를 미리 계산해 두는 vmalert, 멀티 클러스터의 앞단을 지키는 라우팅 게이트웨이 vmauth 셋입니다.
관련 문서: 02 아키텍처, 03 수집, 04 저장, 실전 01 카디널리티, 실전 02 대규모 운영
vmselect — Fanout 쿼리 엔진
도식 텍스트
- Grafana — PromQL/MetricsQL 발신
- vmselect
- vmstorage-1
- vmstorage-2
- vmstorage-N
- Merge → Sort → JSON
- Grafana — JSON 응답 수신
- 쿼리
- Fanout
- Fanout
- Fanout
- 응답
vmselect는 PromQL/MetricsQL 쿼리를 받아 모든 vmstorage 노드에 던집니다(Fanout). 돌아온 결과는 모아서 클라이언트에 반환합니다. 02 아키텍처의 데이터 흐름에서 화살표가 반대로 향하는 쪽, 즉 읽기 경로의 진입점입니다.
전체 흐름은 세 단계입니다.
1. Grafana → vmselect
PromQL/MetricsQL 쿼리(레이블 필터 포함)를 던진다.
엔드포인트 예: /select/0/prometheus/api/v1/query_range
2. vmselect → 모든 vmstorage (Fanout)
전 노드에 요청을 전송하고 블록 단위로 수신한다.
3. vmselect → Grafana
받은 블록을 메모리 버퍼에 모아 Merge → Sort → JSON 응답.vmselect는 저장을 하지 않습니다. 저장하지 않으니 어느 노드에 무엇이 있는지도 모릅니다. 그래서 모든 vmstorage에 똑같이 뿌립니다(03 수집의 랑데부 해싱은 쓰기 라우팅일 뿐 읽기는 위치를 모릅니다). 그렇게 돌아온 조각을 하나로 합칩니다. 덕분에 vmselect는 stateless하고 부하에 맞춰 수평 확장하기 쉽습니다(→ 실전 02 대규모 운영에서 Kubernetes 위에 올리는 이유).
3-Prefix 검색 — PromQL 한 줄이 풀리는 과정
vmselect 내부에서 쿼리는 3단계 prefix로 풀립니다. 04 저장에서 본 TSID·IndexDB 구조를 역방향으로 되짚는 과정입니다.
파싱과 캐싱: 먼저 PromQL 문자열을 구조화된 데이터로 파싱합니다. 어떤 함수인지, 필터는 어떻게 걸렸는지, 시간 윈도는 얼마인지를 뽑아냅니다. 이 파싱 결과 자체도 캐싱해 같은 형태의 쿼리를 매번 다시 파싱하지 않습니다.
- Prefix 1 · 태그 → Metric ID — 태그 필터를 IndexDB에 전달해 매칭되는 Metric ID를 식별합니다. 인메모리 캐시를 먼저 확인하고
name·method·status등 여러 레이블 조건의 교집합에 해당하는 Metric ID를 모읍니다. - Prefix 2 · Metric ID → TSID — 모인 Metric ID를 TSID로 변환합니다(예: Metric ID 49가 어떤 TSID인지 조회).
- Prefix 3 · TSID → 값·이름 복원 — TSID로 Value와 Timestamp를 가져오고 응답에 넣을 지표 이름과 레이블을 역으로 복원합니다.
이 과정을 거치면 http_requests_total{method="get", status="200"} 같은 필터가 “그 시계열의 값이 몇이다"로 완성됩니다. 쓰기 시점에 이름 → TSID로 정규화했던 것을 읽기 시점에 TSID → 이름으로 되돌리는 대칭 구조입니다.
메모리 관리 3포인트
vmselect가 신경 쓰는 것은 결국 메모리입니다. 쿼리 시점 dedup, Rollup Result Cache, Query Latency Offset이 그 장치입니다.
1) 쿼리 시점 Deduplication
03 수집의 replicationFactor로 같은 시계열이 여러 노드에 중복 저장되고 vmstorage에서도 dedup을 합니다. 그런데 vmselect에서 한 번 더 합니다. dedup.minScrapeInterval=10s 같은 옵션을 두면 동일 타임스탬프 구간에서 최신 값만 살리고 나머지는 버립니다. Fanout으로 여러 복제본이 동시에 돌아오니 최종 응답을 만들기 직전에 한 번 더 걷어냅니다.
2) Rollup Result Cache — 왜 12.5%이고 왜 최근 5분을 제외하나 한 번 처리한 쿼리 결과는 캐싱합니다. 이 캐시가 쓸 수 있는 양은 vmselect 허용 메모리의 12.5%까지입니다. 쿼리 처리 자체에 필요한 메모리를 캐시가 잠식하지 않도록 둔 상한입니다.
조건이 하나 더 붙습니다. 현재 시각 기준 최근 5분 구간은 캐싱에서 제외합니다. 가장 최근 5분의 데이터는 아직 vmstorage에 다 도착하지 않았을 수 있습니다. 응답 지연이나 네트워크 지연으로 일부 데이터포인트가 빠지기도 합니다. 그 불안정한 결과를 캐시에 넣어 버리면 나중에 그 캐시가 잘못된 값을 계속 돌려줍니다. 그래서 아직 확정되지 않은 최근 5분은 아예 캐싱 대상에서 뺍니다.
3) Query Latency Offset — 기본 30초, 실시간과의 트레이드오프
search.latencyOffset 설정값이며 기본 30초입니다. 쿼리할 때 가장 최근 30초의 데이터를 일부러 뒤로 밀어 검색합니다. Rollup Result Cache의 “최근 5분 제외"와 문제의식이 같습니다. 수집이 밀려 흔들리는 데이터는 조회 결과에 넣지 않습니다.
실시간 대시보드처럼 recency가 중요한 곳이라면 이 값을 0초로 줄여도 됩니다. 대신 조회할 때마다 최신 데이터가 들쭉날쭉 채워지므로 같은 그래프가 새로고침마다 다르게 보이는 것을 감수해야 합니다. 정확성(안정)과 실시간성 사이의 선택입니다.
vmalert — Recording Rules 선계산
vmselect가 아무리 빨라도 애초에 읽어야 할 데이터포인트가 수백만 개면 답이 없습니다. 시각화 대시보드에서 서비스 지표를 하루 이상 범위로 조회하는 요청이 그렇습니다.
장비 5,000대를 쓰는 서비스에서 “매 분 검색 요청 수의 합"을 하루 범위로 표시한다고 해 보겠습니다.
데이터 수집 간격: 1분
매 분 장비별 시계열: 5,000개
1일 데이터포인트: 5,000개/분 × 60분 × 24시간 = 7,200,000개/일하루치 그래프 한 장에 720만 개를 반환해야 합니다. 그런데 정작 화면에 필요한 건 “합계” 시계열 하나입니다.
미리 계산해 시계열 1개로 압축하면 됩니다. 검색 요청 수의 합을 사전에 계산해 저장해 두면 조회 시엔 1,440개(하루 = 1,440분)만 읽으면 됩니다. 720만 개 → 1,440개, 약 5,000분의 1입니다.
이 선계산은 Prometheus의 Recording rules 호환 툴인 vmalert가 맡습니다. Recording rule로 정의한 표현식을 주기적으로 평가해 그 결과를 새로운 시계열로 다시 저장합니다. 읽기 부하가 쓰기 시점으로 옮겨 갑니다. 무거운 대시보드 쿼리의 응답 시간이 짧아지고 클러스터 부하도 함께 내려갑니다.
vmalert가 실제 멀티 클러스터 운영에서 어떤 접근 패턴 부하를 덜어 주는지는 실전 02 대규모 운영에서 다룹니다.
vmauth — 라우팅 게이트웨이
클러스터를 여러 개 운영하기 시작하면(실전 02 대규모 운영의 멀티 클러스터) 새로운 문제가 생깁니다. 버전 업그레이드, 장비 교체, 배포, IDC 장애 같은 운영 이슈가 발생할 때마다 사용자가 직접 엔드포인트를 바꿔 대체 클러스터로 붙어야 합니다. 그만큼 사용자와 주고받아야 할 커뮤니케이션 비용이 늘어납니다. 최악의 경우 IDC 인프라 장애가 나면 사용자가 설정을 배포하기 전까지 모니터링 복구가 지연됩니다.
그래서 사용자 앞에 라우팅 게이트웨이를 둡니다. 사용자는 고정된 엔드포인트 하나만 바라보고 운영자가 그 뒤에서 적절한 클러스터로 라우팅합니다. VM은 이 자리에 쓸 vmauth를 제공합니다(래핑해서 사용). 게이트웨이를 두면 달라지는 점은 이렇습니다.
- 사용자 엔드포인트 고정 + 내부 라우팅 전환: 운영 이슈가 나면 내부 라우팅 설정 변경만으로 대체 클러스터로 넘깁니다. IDC 장애 같은 비상 상황에서도 사용자 배포 없이 라우팅 포인트만 바꿔 빠르게 복구합니다.
- 접근 패턴·부하량 기반 라우팅과 로드밸런싱: 사용자별 조회 패턴과 부하량에 맞춰 어느 클러스터로 보낼지 정합니다.
- Basic auth: 인증된 사용자만 접근하게 해 예상치 못한 부하를 앞단에서 막습니다.
- 부하 모니터링: 사용자별 부하·요청 수를 모니터링해 과부하 유발 지점을 식별합니다.
여러 클러스터를 운영해도 사용자와 주고받을 말은 줄고 내부 유지보수는 수월해집니다.
vmauth로 클러스터를 무중단 전환하는 방법과 오경보 대응 같은 상세 운영 맥락은 실전 02 대규모 운영에서 이어집니다.
출처
- Inside VictoriaMetrics (강민구, NAVER) — vmselect Fanout·3-Prefix 검색·메모리 관리 3포인트: 33:46~38:50 구간. (https://d2.naver.com/helloworld/9290861)
- 네이버 검색 SRE의 시계열 데이터베이스 운영기 (이선규) — vmalert 지표 선계산(선계산 시점): 32:18 구간. (https://d2.naver.com/helloworld/6867189)
- VictoriaMetrics 시계열 데이터, 대혼돈의 멀티버스 (DEVIEW 2023) — 멀티 클러스터·데이터 마이그레이션 맥락. (https://d2.naver.com/helloworld/6867189)
- 합성 골격:
chapter9/victoriametrics.md§3.4·§4.3·§4.4.