[Observability 3/5] Grafana 생태계 지도: 수집부터 저장과 시각화까지

심대용·2026년 9월 26일
post-thumbnail

이 글에서 다룰 주제

  • 최소 구성: Prometheus와 Grafana만으로 무엇을 볼 수 있을까?
  • 트레이스 추가: OpenTelemetry·Alloy·Tempo는 어느 자리를 맡을까?
  • 구성 점검: 그래프가 비었을 때 어느 연결부터 확인할까?

주요 단어 · 계측 · Scrape · OTLP · Alloy · 데이터 소스 · Tempo

2편에서 메트릭은 전체의 변화, 트레이스는 한 요청의 내부 흐름을 보여 준다고 정리했다. 이번에는 그 데이터가 실제로 어디에서 만들어져 어느 화면까지 도달하는지 연결한다. 읽고 나면 제품 목록을 외우기보다 필요한 질문에 맞춰 최소 구성을 고르고, 데이터가 사라진 구간을 찾을 수 있는 것이 목표다.

가상의 demo-api가 제공하는 GET /search를 살펴보자. 2026-10-04 10:00~10:05 KST에 요청 100건 중 90건은 100ms, 10건은 2000ms가 걸렸다고 가정한다. 모두 HTTP 200이다. 오류율만 보면 이 지연을 놓친다. 실제 회사나 프로젝트의 구조·측정값이 아닌 시리즈 공통 학습 예제다.

앱이 만든 트레이스를 Alloy가 Tempo로 전달하고 Grafana가 저장소를 조회하는 전체 구조

그림 1. 작성자 제작 개념도. 이번 편에서 확장할 트레이스 경로를 미리 보여 준다. 먼저 메트릭의 최소 구성부터 익힌 뒤 이 경로를 추가한다. 데이터 전송과 조회 요청의 방향에 주목하자.

1. 최소 구성: 먼저 메트릭 하나가 화면에 도착하게 만들기

계측(Instrumentation)은 프로그램의 실행 상태를 메트릭·Span 같은 데이터로 기록하는 일이다. 데이터가 만들어지지 않으면 저장소와 대시보드를 설치해도 표시할 내용이 없다.

처음부터 생태계의 모든 도구가 필요하지는 않다. “검색 요청이 얼마나 들어오고, 언제 느려지는가?”가 첫 질문이라면 메트릭을 노출하는 앱, Prometheus, Grafana로 시작할 수 있다.

여기서는 앱에 메트릭 계측이 이미 있고 /metrics에서 값을 제공한다고 가정한다. /search는 검색 기능의 주소이고, /metrics는 관측 값을 읽는 주소다. 둘은 별개다. Prometheus는 /search를 반복 실행해 성능을 시험하는 도구가 아니다.

세 구성요소가 하는 일을 순서대로 읽어 보자.

  1. 앱이 요청 수나 요청 시간 분포를 기록하고 /metrics로 노출한다.
  2. Prometheus가 주기적으로 /metrics에 HTTP 요청을 보내 값을 받아 저장한다.
  3. Grafana가 Prometheus에 질의하고, 반환받은 값을 그래프로 그린다.

Scrape는 2번처럼 수집기가 대상의 메트릭을 읽어 오는 동작이다. Prometheus는 수집뿐 아니라 시계열 저장과 PromQL 질의도 맡는다. 따라서 이 최소 구성에서는 메트릭 저장용으로 Mimir를 추가하지 않아도 된다. Prometheus 구조와 기능

Grafana의 데이터 소스(Data source)는 화면이 어느 시스템에 어떤 방식으로 질의할지 정한 연결이다. Grafana에 Prometheus 데이터 소스를 등록하는 것은 이미 저장된 메트릭을 읽을 준비다. 앱의 계측이나 Prometheus의 수집 설정까지 대신 만들어 주지는 않는다. Grafana 데이터 소스

2. 화살표 읽기: 수집 요청과 데이터 응답을 구분하기

Prometheus의 scrape 요청과 메트릭 응답, Grafana의 조회 요청과 조회 결과를 구분한 상세도

그림 2. 작성자 제작 상세도. “누가 먼저 요청하는가?”를 기준으로 보면 화살표 방향을 기억하기 쉽다. 수집과 화면 조회는 서로 다른 왕복이다.

Prometheus 로고 출처: CNCF artwork. Prometheus는 The Linux Foundation의 상표이며 이 글은 독립적인 학습 자료입니다.

Prometheus가 앱으로 보내는 것은 메트릭 수집 요청이다. 그 응답에 담긴 메트릭은 앱에서 Prometheus로 돌아온다. Grafana가 Prometheus로 보내는 것은 조회 요청이고, 결과는 반대 방향으로 돌아온다.

그래서 앱 → Prometheus → Grafana라는 선 하나만 보면 데이터가 이동하는 큰 방향은 알 수 있어도 실제 호출 주체는 알 수 없다. 연결 오류를 조사할 때는 요청과 응답을 나눠 읽어야 한다. Prometheus가 앱에 접근할 수 있는 것과 Grafana가 Prometheus에 접근할 수 있는 것은 별도의 조건이다.

2.1 첫 확인: 수집 자체는 성공하는가?

다음은 Prometheus가 대상에 붙인 job 라벨이 demo-api라고 가정한 PromQL이다. 공식 구문을 확인한 미실행 예제이며, 실제 환경의 job 값에 맞춰 바꿔야 한다.

up{job="demo-api"}

Grafana에서 2026-10-04 10:00~10:05 KST를 선택하고 살펴보는 상황을 생각하자. 설명을 위해 scrape 주기와 조회 step을 모두 15초로 가정한다. 두 간격은 각각 수집 빈도와 결과를 계산해 표시하는 간격이며 항상 같아야 하는 것은 아니다.

이 쿼리는 일치하는 대상별로 단위 없는 0 또는 1을 보여 준다. 1은 해당 평가 시점에서 확인할 수 있는 최근 scrape가 성공했다는 뜻이다. 0이면 주소·네트워크·인증·대상의 응답 등을 확인한다. 결과 자체가 없다면 job 이름, 대상 등록, 조회 시간부터 확인한다. 대상이 수집 목록에서 사라졌다면 0이 계속 남는다고 기대할 수도 없다. Prometheus가 생성하는 up 메트릭

특히 up=1은 검색 기능이 빠르다는 뜻이 아니다. 메트릭 주소는 정상 응답하면서 /search가 느릴 수 있다. 수집이 성공한 뒤에야 요청 수와 요청 시간 분포처럼 실제 질문에 필요한 메트릭을 확인할 수 있다.

3. 트레이스 추가: “한 요청 안에서 무엇이 느렸나?”

메트릭으로 지연이 발생한 시간대를 찾았지만, 한 요청이 어디서 오래 걸렸는지는 아직 모른다고 하자. 이 질문이 생겼을 때 트레이스 경로를 추가한다. 최소 메트릭 경로는 그대로 두고 관측 데이터를 하나 더 보낸다.

전체도와 같은 배치에서 OpenTelemetry 계측, Alloy 전달, Tempo 저장과 Grafana 조회 경로를 강조한 그림

그림 3. 작성자 제작 강조판. 주황색은 현재 설명하는 트레이스 경로다. 사용자 검색 요청을 Alloy로 보내는 구조가 아니라, 요청을 처리하며 만든 Span을 별도 전송한다.

OpenTelemetry(OTel)는 관측 데이터를 만들고 전달하는 API·SDK·도구·규약을 제공하는 프로젝트다. Span은 시작·종료 시각과 속성을 가진 한 작업 구간이고, 연결된 Span들이 트레이스를 이룬다.

앱에 OTel 계측을 적용하면 HTTP 요청이나 의존성 호출의 Span을 만들 수 있다. 지원 라이브러리를 자동으로 계측할 수도 있고, 개발자가 의미 있는 구간을 직접 표시할 수도 있다. 여기서는 자동 계측에 필요한 내부 구간을 보완했다고 가정한다. OpenTelemetry 계측

느린 요청 한 건의 가상 트레이스를 읽어 보자.

GET /search 전체       2000ms
  입력 검증              20ms
  검색 저장소 호출     1800ms
  응답 생성             180ms

이 숫자는 세 구간이 겹치지 않고 직렬로 실행됐다는 가정이다. 실제 트레이스에는 병렬·중첩 구간이 있으므로 모든 Span 시간을 무조건 더하면 안 된다. 여기서 뒷받침되는 해석은 “2000ms 중 1800ms가 저장소 호출 구간에 있었다”까지다. 저장소 내부 실행, 연결 대기, 네트워크 중 무엇이 원인인지 더 확인해야 한다.

생성한 Span은 다음 경로로 보낼 수 있다.

앱의 OTel SDK
    ↓ OTLP로 Span 전송
Alloy
    ↓ OTLP로 Span 전송
Tempo

OTLP는 OpenTelemetry의 데이터 전송 프로토콜이다. 이 예제에서는 트레이스 전송에 사용하며, SDK의 전송 방식과 수신기의 설정이 맞아야 한다. OTLP/HTTP와 OTLP/gRPC를 이름만 보고 서로 바꿔 연결할 수는 없다.

Alloy는 이 경로에서 데이터를 받아 처리하고 전달하는 수집기다. 예를 들어 여러 Span을 모아 배치로 보내도록 구성할 수 있다. Tempo는 도착한 트레이스를 저장하고 검색하는 백엔드다. 앱에서 만들지 않은 내부 Span을 Alloy나 Tempo가 나중에 복원해 주는 것은 아니다. Alloy의 OpenTelemetry 수집, Tempo 소개

Alloy가 반드시 필요한 유일한 중간 지점이라는 뜻은 아니다. 수집기를 따로 두면 여러 앱의 전송 목적지와 처리 정책을 한곳에서 다루기 쉬워진다. 반면 운영할 구성요소와 점검할 연결도 늘어난다. 여기서는 역할을 나눠 이해하기 위해 Alloy를 넣은 하나의 구성을 선택했다.

4. 추가 도구 선택: 부족한 질문이 생겼을 때 확장하기

기본적인 메트릭과 트레이스가 보인 다음에는 아래처럼 아직 답하지 못한 질문에 따라 확장을 고를 수 있다.

Alloy의 수집·전달, Loki·Tempo·Mimir·Pyroscope의 신호별 역할과 Grafana의 조회·시각화를 구분한 지도

그림 4. 작성자 제작 역할 지도. 이 글에서 사용한 Grafana 계열 제품의 기호는 공식 로고가 아닌 직접 만든 역할 아이콘이다. 제품명과 역할을 구분하는 지도이며, 실제 배포 구조나 반드시 설치해야 하는 도구 목록을 뜻하지 않는다.

더 알고 싶은 것추가할 수 있는 구성맡는 역할
같은 시각에 어떤 오류가 기록됐나?로그 수집 + Loki로그 저장·검색
CPU가 어느 코드에 쓰였나?프로파일 수집 + Pyroscope프로파일 저장·분석
여러 수집기의 메트릭을 오래 모을까?중앙 메트릭 저장소 Mimir메트릭 중앙 저장·확장

Loki는 로그 스트림의 라벨을 중심으로 인덱싱하고 LogQL로 조회한다. service처럼 범위를 좁힐 정보와 요청마다 달라지는 상세 값을 구분해야 한다. 로그에 Trace ID가 있다고 해서 그 값을 모두 인덱스 라벨로 만들 필요는 없다. Loki 개요

Pyroscope는 프로파일을 저장·분석한다. CPU 프로파일은 CPU 실행이 집중된 코드를 찾는 데 적합하지만, 1800ms의 외부 호출 대기를 그대로 CPU 사용 시간으로 설명해 주지는 않는다. 사용할 수 있는 프로파일 종류와 연결 기능은 언어·수집 방식에 맞춰 확인한다. Pyroscope 소개

Mimir는 메트릭의 중앙 저장·장기 보관·확장이 필요한 경우의 선택지다. 예를 들어 Prometheus가 수집한 데이터를 remote_write로 보낼 수 있다. 이때 전송은 Prometheus에서 Mimir 방향이며, 조회할 Grafana 데이터 소스는 선택한 저장소를 가리킨다. 모든 환경에 Prometheus와 Mimir를 동시에 설치해야 하는 것은 아니다. Mimir 소개

4.1 Beyla는 어디에 놓일까?

Beyla는 지원되는 Linux 환경에서 eBPF를 이용해 애플리케이션을 자동 계측하는 도구다. 요청률·오류·지연 같은 기본 관측과 트랜잭션 Span을 얻는 데 도움이 된다. 다만 커널·권한·언어·프로토콜별 지원 조건을 확인해야 한다. Beyla 공식 문서

Beyla는 계측 데이터를 만드는 쪽에 놓고 이해하면 된다. 모든 내부 함수의 의미까지 알아내는 도구는 아니다. “입력 검증”이나 “검색 결과 변환” 같은 구간이 필요하면 SDK를 통한 추가 계측을 고려한다. OTel에도 자동 계측이 있으므로 두 도구를 단순히 “코드 수정 있음/없음”으로만 구분하면 선택 기준이 흐려진다.

5. 연결 점검: 화면이 비면 데이터 경로를 거슬러 올라가기

Grafana에 Tempo 데이터 소스를 추가했는데 트레이스가 없다고 하자. 이때 대시보드를 더 만드는 것보다 다음 경계를 차례로 확인하는 편이 효과적이다.

확인할 경계작은 확인 질문실패했다면 볼 곳
생성테스트 요청에서 Span을 만들었나?계측·샘플링 설정
전송·저장수신·전송 오류나 드롭이 있나?SDK·Alloy·Tempo 상태
조회시간·서비스 조건이 맞나?데이터 소스·검색 범위

특히 service.name=demo-api 같은 공통 서비스 정보와 시각이 맞아야 같은 사건을 찾기 쉽다. 다만 OTel의 resource 속성과 Prometheus의 job 라벨은 출처와 역할이 다르다. 이름이 비슷하다는 이유로 모든 저장소에서 자동으로 같은 필드가 된다고 가정하지 않는다.

로그의 Trace ID를 누르면 트레이스로 이동하는 기능도 데이터 소스만 두 개 등록했다고 완성되지 않는다. 로그에 올바른 식별자를 남기고, 해당 필드에서 트레이스 조회로 연결하는 설정이 필요하다. 우선 같은 시간·서비스·식별자를 수동으로 찾을 수 있는지 확인한 뒤 탐색 링크를 붙이면 연결 문제를 나누기 쉽다.

샘플링으로 일부 트레이스만 보관했다면 없는 요청이 생길 수 있다. 수집 지연이나 조회 시간대 오류도 빈 결과를 만든다. 데이터가 없다는 사실은 요청이 없었다거나 서비스가 정상이라는 결론과 다르다. 관측 파이프라인의 오류·드롭도 관찰 대상이다.

6. 적용 질문: 구성을 읽을 수 있는지 확인하기

Q1. Prometheus와 Grafana는 연결됐는데 /search의 지연 그래프가 없다. Tempo부터 설치할까?

먼저 앱에 요청 시간 메트릭이 있는지, Prometheus가 수집했는지, 올바른 메트릭을 질의했는지 확인한다. Tempo는 트레이스 질문을 위한 추가 경로이며 빠진 메트릭을 대신 만들어 주지 않는다.

Q2. Grafana에서 Tempo로 향하는 화살표는 무엇인가?

트레이스를 찾는 조회 요청이다. 저장된 결과는 Tempo에서 Grafana로 돌아온다. 앱이 Span을 저장소로 전송하는 화살표와 구분한다.

Q3. 1800ms의 저장소 호출 Span을 찾으면 원인을 확정했을까?

조사할 구간을 좁힌 것이다. 연결 풀 대기, 실제 저장소 작업, 네트워크 등을 구분하려면 더 세밀한 Span이나 관련 로그·메트릭이 필요하다.

다음 편에서는 서버 응답이 끝난 뒤에도 사용자가 기다릴 수 있다는 점에 주목한다. 브라우저 관측을 더하면 “서버 처리 시간”과 “사용자가 결과를 본 시간”을 나눠 설명할 수 있다.


검증 범위 — 2026-10-04: 링크한 공식 문서에서 제품의 역할, scrape·조회 방향, OTel·Alloy·Tempo의 연결 개념을 확인했다. 구성도와 수치는 작성자가 만든 학습 예제이며 실제 배포를 실행 검증한 구성은 아니다. up 쿼리는 공식 구문 기반 미실행 예제다.

이전 글: 메트릭·로그·트레이스·프로파일은 각각 무엇을 알려줄까?

다음 글: RUM·사용자 행동 분석·Session Replay 구분하기

profile
어제보다 더 성장하는 나

0개의 댓글