Prometheus·Tempo·Grafana 관찰 가능성 스택 설정 개선
목차
Prometheus를 host 네트워크 모드로 전환한 게 이번 작업의 핵심이었다. Docker 기본 브리지 네트워크에서 컨테이너를 돌리면 Prometheus 컨테이너 안에서 호스트의 localhost:8080을 타겟으로 잡을 수 없다. 컨테이너 입장에서 localhost는 자기 자신이지, 호스트 머신이 아니기 때문이다. 처음엔 왜 스크레이프 타겟이 down 상태로 뜨는지 한참 헤맸다.
docker-compose에서 network_mode: host를 추가하니 해결됐다.
prometheus:
image: prom/prometheus:latest
network_mode: host
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
host 모드를 쓰면 컨테이너가 호스트 네트워크 스택을 그대로 공유한다. 그래서 localhost:8080/actuator/prometheus를 바로 때릴 수 있다. 보안 격리 수준이 낮아지는 건 맞는데, 내부 전용 모니터링 스택이고 외부에 직접 노출되지 않는 구조라서 이 선택이 합리적이라고 봤다. 외부에서 Prometheus UI를 직접 접근해야 하는 환경이라면 다시 생각해야 하는 설정이다.
Spring Boot 쪽 Actuator 설정은 이미 돼 있었고, 이번에 점검하는 수준이었다.
management:
endpoints:
web:
exposure:
include: health,prometheus
metrics:
export:
prometheus:
enabled: true
/actuator/prometheus를 외부에 그냥 열면 안 된다. JVM 메모리 상태, 스레드 수, DB 커넥션 풀 현황 같은 내부 정보가 다 노출된다. IP 필터 기반으로 허용 목록만 통과시키는 필터를 별도로 달아뒀고, 모니터링 서버 IP만 접근 가능하도록 막았다. Actuator 경로를 완전히 열거나 완전히 막는 이분법보다 IP 필터가 실용적이다. 내부 헬스체크는 유지하면서 외부 노출만 막을 수 있으니까.
Tempo 파일 정리와 datasource URL 수정
Tempo 쪽에서는 scp로 올려두었던 설정 파일들이 지저분하게 쌓여 있었다. 수정할 때마다 기존 파일을 정리하지 않고 그냥 올리다 보니, 실제로 어떤 파일이 적용 중인지 금세 헷갈리는 상태가 됐다. 이번에 현재 실제 적용 중인 파일을 확인하고 나머지를 정리했다. 설정 파일은 귀찮더라도 git으로 버전을 추적하거나, 최소한 "이게 현재 버전"이라는 게 명확하게 유지돼야 한다. 장애 상황에서 어떤 설정이 실제로 적용 중인지 파악하는 데 시간을 쓰면 그 자체가 손실이다.
Grafana datasource URL 수정은 Tempo 포트 설정이 바뀌면서 따라온 문제였다. Grafana UI에서 수동으로 클릭해서 고치면 빠르긴 한데, 컨테이너를 재시작하거나 환경을 새로 세팅하면 또 날아간다. 이번 기회에 provisioning 파일로 관리하는 방향으로 바꿨다.
# grafana/provisioning/datasources/tempo.yaml
apiVersion: 1
datasources:
- name: Tempo
type: tempo
url: http://localhost:3200
access: proxy
isDefault: false
jsonData:
tracesToLogsV2:
datasourceUid: loki
이렇게 파일로 관리하면 Grafana 컨테이너 재시작해도 datasource 설정이 날아가지 않고, 변경 이력도 Git으로 남는다. 설정을 UI에서 클릭으로 관리하는 건 처음 세팅할 때는 편리하지만, 운영이 길어지면 관리 포인트가 분산돼서 나중에 "어떻게 세팅했더라"를 다시 뒤져보게 된다.
분산 트레이싱 샘플링 트레이드오프
Micrometer Brave + Zipkin 조합으로 요청 흐름을 추적하고 있다. 트레이스 ID가 붙으면 여러 서버에 분산된 로그를 하나의 요청 흐름으로 묶어볼 수 있다. "이 에러가 어느 서비스에서 시작됐는지"를 찾을 때 특히 유용하다.
Tempo에서 Grafana로 트레이스를 연동하면 메트릭 스파이크 시점과 요청 트레이스를 같이 볼 수 있다. "왜 이 시간에 p99 응답 시간이 튀었는지" Grafana 대시보드에서 그래프 구간을 클릭하면 그 시점의 트레이스로 드릴다운되는 구조다. 메트릭만 보면 "느려졌다"까지만 알 수 있고, 트레이스를 같이 보면 "어느 스팬에서 막혔는지"까지 보인다.
샘플링 비율 설정이 생각보다 트레이드오프가 있다.
| 샘플링 비율 | 특징 | 적합한 상황 |
|---|---|---|
| 100% | 모든 요청 기록, 저장 비용 높음 | 개발/스테이징 환경 |
| 5~10% | 대부분 문제 포착, 비용 중간 | 트래픽 낮은 운영 |
| 1~2% | 비용 낮음, 간헐 이슈 놓칠 수 있음 | 트래픽 높은 운영 |
운영 환경에서 100% 샘플링은 부담이 크다. 반대로 너무 낮게 잡으면 간헐적으로 발생하는 느린 요청이 샘플에 안 잡혀서 재현이 안 되는 상황이 생긴다. 일반적으로 1~5%를 기본으로 가져가고, 에러가 발생한 요청은 별도로 100% 기록하는 조건부 샘플링을 거는 방식도 있다. Micrometer에서는 SpanExportingPredicate를 커스텀하거나, OpenTelemetry SDK를 쓴다면 ParentBased + TraceIdRatioBased 조합으로 이걸 처리할 수 있다.
이번 작업에서 가장 시간을 잡아먹은 건 의외로 YAML 파싱 문제였다. 설정 값 앞뒤에 공백이 들어가 있었는데, 읽어온 값을 그냥 string 비교하다가 매칭이 안 됐다. trim() 한 줄 추가로 해결됐지만, 원인 찾는 데 시간이 걸렸다. 환경 변수나 파일에서 읽어온 값을 바로 비교할 때 이 부분에서 종종 막힌다. YAML에서 따옴표 없이 쓴 문자열 값 끝에 공백이 붙는 경우가 생각보다 자주 있다.
작업 규모는 크지 않았다. 그래도 이런 설정 파일 정리와 URL 수정 같은 것들을 미루면, 정작 장애가 생겼을 때 관찰 가능성 스택 자체를 신뢰할 수 없는 상황이 생긴다. 메트릭이 안 들어오는 건지, 타겟 설정이 잘못된 건지, datasource URL이 틀린 건지 한꺼번에 의심해야 하면 진짜 문제 찾는 시간보다 스택 점검하는 시간이 더 길어진다. 그래서 이런 작업들이 필요하다.
댓글 0
첫 댓글 달아줘.