이 글은 사이드 프로젝트의 관측성 설계·운영 기록을 바탕으로 정리했습니다. 계정, 토큰, 내부 인스턴스 식별자는 제외했습니다.
홈 서버를 운영 경로에 추가하기 전에 먼저 답해야 할 질문이 있었다. “홈 서버가 동작하는가?”가 아니라 **“문제가 생겼을 때 어디서, 어떤 신호로, 얼마나 빨리 알 수 있는가?”**였다.
관측성은 서비스 밖에서 내부 상태를 이해할 수 있게 만드는 방법이다. Grafana, Prometheus, OpenTelemetry는 모두 관측성에 쓰이지만 역할은 서로 다르다. 이 프로젝트에서는 신호가 만들어지고 수집되고 저장된 뒤 화면에 보이는 순서를 먼저 나눴다. 메트릭부터 시작하고 트레이스와 로그는 나중에 추가하기로 했다.
메트릭, 트레이스, 로그는 무엇이 다를까?
| 신호 | 주로 답하는 질문 | 사이드 프로젝트에서의 예 |
|---|---|---|
| 메트릭 | 언제부터, 얼마나 자주 문제가 생겼는가? | HTTP 5xx 비율, p95 지연, JVM heap, 디스크 사용률 |
| 트레이스 | 요청 한 건이 정확히 어디에서 느려졌는가? | API → DB → 객체 저장소로 이어지는 요청 구간 |
| 로그 | 그 시점에 어떤 예외와 맥락이 있었는가? | 배포·기동 메시지, DB 연결 실패, 예외가 발생한 호출 경로 |
세 신호는 서로 대체재가 아니다. 5xx 비율 증가라는 메트릭으로 이상 구간을 찾고, 같은 시간대의 트레이스로 병목 구간을 좁힌 뒤, 연결된 로그에서 구체적인 예외를 읽는 흐름을 목표로 했다.
flowchart LR A["애플리케이션 · JVM · 서버"] --> B["수집·가공\nPrometheus · Alloy"] B --> C["저장\nMimir · Tempo · Loki"] C --> D["대시보드 · 탐색 · 알림"] D --> E["장애 감지와 원인 분석"]
이미 있던 모니터링 구성 활용하기
사이드 프로젝트 백엔드에는 이미 Spring Boot Actuator, Micrometer, Prometheus registry가 있었다. Prometheus는 Actuator가 공개한 메트릭 주소를 주기적으로 읽어 수집한다. 이 기존 구성을 새 도구로 바꾸지 않고 첫 번째 수집 경로로 유지했다. Spring Boot의 Metrics 문서가 이 구성의 출발점이다.
| 구성요소 | 맡긴 역할 | 이번 선택 |
|---|---|---|
| Actuator + Micrometer | 애플리케이션과 JVM 메트릭 생성·노출 | 유지 |
| Prometheus | 메트릭 주소를 주기적으로 수집하고 로컬에 보관 | 유지하고 remote_write로 Cloud 전송 추가 |
| Grafana | 여러 데이터 저장소의 조회와 시각화 | 로컬 화면은 유지, Cloud 화면은 외부 관제에 사용 |
| Grafana Cloud | 외부 저장소, 대시보드, 알림, 외부 상태 확인 | 메트릭과 외부 가용성부터 도입 |
| OpenTelemetry Java Agent + Grafana Alloy | 트레이스·로그 수집과 가공 | 개인정보 처리 규칙 검증 뒤 도입 |
Prometheus와 Grafana가 겹쳐 보이지만 역할은 다르다. 전자는 메트릭을 수집·저장하는 엔진이고, 후자는 Prometheus를 포함한 저장소를 조회해 대시보드와 알림을 제공하는 UI·운영 계층이다. 이 구분을 해 두면 “Grafana를 설치했으니 메트릭이 저장된다” 같은 착각을 피할 수 있다.
메트릭부터 시작한 이유
첫 번째 목표는 홈 서버가 운영 경로에 들어오기 전, 클라우드와 홈 서버를 같은 화면에서 비교하는 것이었다. 기존 Prometheus가 이미 애플리케이션·JVM·호스트 메트릭을 수집하고 있었으므로 다음 경로가 가장 작은 변경이었다.
flowchart LR A["Spring Boot\nMicrometer · Actuator"] <-->|"scrape"| P["Prometheus"] N["node exporter"] <-->|"scrape"| P P -->|"remote_write"| G["Grafana Cloud metrics"] G --> D["Dashboard · Alert"]
이 결정에는 두 가지 이유가 있었다.
- 기존 로컬 대시보드와 PromQL 질의를 깨지 않고, 외부 관제만 추가할 수 있다.
- 홈 서버와 클라우드 서버에 같은 라벨 규칙을 적용해
project,service,instance,environment,role기준으로 나눠 볼 수 있다.
같은 이름의 메트릭이라도 서버를 구분하는 라벨이 없으면 어느 서버가 요청을 처리했는지 알 수 없다. 반대로 요청 ID나 사용자 ID처럼 값의 종류가 계속 늘어나는 정보를 라벨로 넣으면 저장할 데이터와 비용이 크게 늘어난다. 이 프로젝트에서는 운영에 꼭 필요한 구분값만 라벨로 사용했다.
트레이스와 로그를 나중에 추가한 이유
OpenTelemetry는 계측 API와 OTLP 같은 표준을 제공하고, Grafana Alloy는 Prometheus scrape와 OpenTelemetry 수신·가공을 함께 수행할 수 있는 수집기다. 둘은 경쟁 관계가 아니라 표준과 그 표준을 사용하는 수집기 구현에 가깝다. OpenTelemetry 문서와 Grafana Alloy 문서를 함께 읽으면 이 역할 분담이 더 분명해진다.
하지만 자동으로 데이터를 수집할 수 있다고 바로 운영에 적용해도 안전한 것은 아니다. URL의 쿼리 문자열, SQL 문장, 예외 메시지, 객체 저장소 경로, 인증 정보가 트레이스의 상세 값이나 로그에 섞일 수 있다. 사용자 데이터를 다루는 사이드 프로젝트에서는 이 위험이 특히 컸다.
따라서 순서를 바꿨다.
- 메트릭과 외부 상태 확인으로 서비스 가용성과 자원 상태를 먼저 감시한다.
- 원본 로그에 민감한 정보가 있는지 점검하고, 불필요한 수집 옵션을 끈다.
- Alloy의 필터와 변환 단계에서 민감한 값을 한 번 더 제거한다.
- 필요한 트레이스부터 일부만 골라 수집한다.
- 로그는 개인정보, 토큰, 서명된 임시 URL이 제거되는지 확인한 뒤 전송한다.
이 순서는 데이터를 더 많이 모으는 것보다 안전하게 설명 가능한 데이터만 모으는 것을 우선한다.
알림이 실제로 오는지 시험하기
대시보드에 그래프가 나온다고 알림까지 정상이라는 뜻은 아니다. 이 프로젝트에서는 서버 메트릭 수집 중단, 외부 API 상태 확인 실패, 디스크 사용률, JVM 메모리, HTTP 5xx·지연, DB 연결 풀 같은 조건을 알림 규칙으로 만들었다. 임시 조건으로 실제 알림을 발생시켜 메일 수신을 확인한 다음, 운영 기준값과 알림 대기 시간을 복원했다.
이 과정에서 얻은 기준은 단순하다.
| 검증 대상 | 확인해야 할 것 |
|---|---|
| 데이터 수집 | remote_write 전송 실패가 없고, 애플리케이션·서버 시계열이 모두 보이는가 |
| 외부 가용성 | 서비스 바깥의 probe가 공개 API와 홈페이지를 확인하는가 |
| 알림 전달 | 문제 발생과 해결 알림이 모두 운영자에게 도달하는가 |
| 전환 검증 | 주 서버와 예비 서버의 요청·서버 메트릭이 같은 대시보드에서 구분되는가 |
특히 외부 상태 확인(synthetic check)은 애플리케이션 내부의 상태 확인과 다르다. 서버 내부 상태가 UP이어도 DNS, TLS, 프록시, 네트워크 문제로 사용자는 접속하지 못할 수 있다. 그래서 실제 사용자처럼 외부에서 접속하는 확인 절차를 별도로 두었다.
핵심 정리
이 사이드 프로젝트에서 관측성은 “Grafana Cloud를 설치했다”로 끝나는 작업이 아니었다. 기존 메트릭 경로를 보존한 채 외부 관제와 알림을 먼저 만들고, 홈 서버 전환의 성공·실패를 인스턴스별 신호로 확인할 수 있게 하는 작업이었다.
다음 단계의 과제도 남아 있다. trace는 필요한 구간부터 샘플링과 개인정보 제거 규칙을 검증하며 활성화해야 하고, 로그 전송은 더 엄격한 검토가 필요하다. 그러나 이 순서를 지키면 새로운 신호를 추가해도 기존 운영 경로를 잃지 않고, “문제가 없다”가 아니라 “문제가 없음을 어떤 데이터로 확인했는가”를 설명할 수 있다.