들어가며: 관측성 데이터는 많아졌지만, 분석은 여전히 어렵습니다
안녕하세요. LY Corporation에서 Home 서비스의 안정성을 책임지고 있는 Home SRE(Site Reliability Engineering) 팀입니다.
장애 알림이 울렸을 때의 장면을 떠올려 보겠습니다.
- 먼저 대시보드를 열어 메트릭을 확인합니다.
- 이어서 로그 플랫폼으로 이동해 같은 시간대의 에러 로그를 검색합니다.
- 그래도 원인이 명확하지 않으면 트레이스 도구를 열어 어떤 요청이 문제였는지 살펴봅니다.
- 필요하다면 프로파일도 확인합니다.
장애의 원인을 밝힐 수 있는 각각의 신호는 분명히 존재합니다. 문제는 그 신호들이 서로 다른 화면에 흩어져 있다는 점입니다. 메트릭은 에러율이 올랐다고 말하고, 로그는 특정 예외 메시지를 보여 주고, 트레이스는 어떤 요청이 오래 걸렸는지 알려 줍니다. 하지만 이 세 가지를 하나의 이야기로 엮어 "그래서 원인이 무엇인가"를 판단하는 일은 여전히 사람이 해야 했습니다.
저희는 이 과정을 줄이고 싶었습니다. 목표는 단순했습니다.
"Grafana에서 자연어로 질문하면, 메트릭·로그·트레이스·프로파일을 함께 조회하고 원인 후보까지 정리해 주는 도구를 만들자."
그 결과물이 SRELens입니다. SRELens는 Grafana 애플리케이션 플러그인으로 작동하는 자연어 기반 관측 데이터 분석 도구입니다. 사용자가 ‘이 시간대에 왜 에러가 늘었는지 분석해 줘’라고 묻는다면, 대규모 언어 모델(LLM, Large Language Model) 기반 에이전트가 실제 관측성 데이터를 조회하고, 필요한 도구를 호출하며, 근거와 함께 분석 결과를 제시합니다. 또한, LLM에게 맡긴 분석이 실패할 수 있다는 것을 전제하여 어디까지 LLM에게 맡기고, 어디부터는 코드와 정책으로 통제할 수 있는지도 고민하였습니다.
이 글에서는 SRELens를 만든 배경과 오픈소스 PoC(Proof of Concept, 개념 검증)에서 확인한 한계, 운영 환경을 위해 직접 구현한 설계, 그리고 실제 분석 시나리오를 소개하겠습니다.
SRE 팀이 마주한 문제
먼저 저희가 겪고 있던 문제를 조금 더 구체적으로 살펴보겠습니다.
메트릭, 로그, 트레이스가 서로 다른 화면에 있었습니다
저희 환경에서는 관측성 데이터가 종류별로 나뉘어 있었습니다.
- 메트릭은 Grafana 또는 사내 메트릭 플랫폼 IMON에서 확인합니다.
- 로그는 사내 로그 플랫폼 LaaS, IU에서 검색합니다.
- 트레이스는 사내 트레이스 도구인 IMON 트레이스 또는 Tempo 환경에서 조회합니다.
- 프로파일은 별도의 프로파일링 도구를 구축해 확인해야 합니다.

각 도구는 제 역할을 잘하고 있었지만, 장애를 분석하는 사람 입장에서는 데이터가 흩어져 있다는 점이 큰 부담이었습니다. 예를 들어 장애를 분석할 때 다음과 같은 과정을 거쳐야 했습니다.
- 메트릭에서 오류율 증가 확인
- 같은 시간대의 로그를 찾기 위해 다른 화면으로 이동
- 다시 트레이스 ID를 기준으로 트레이스 도구를 실행
필요하면 프로파일링 도구까지 별도로 확인해 야 했습니다.
이런 흐름에서는 도구를 오가는 시간뿐 아니라, 시간 범위와 서비스명, 라벨, 트레이스 ID 같은 분석 맥락을 계속 옮겨 적어야 하는 비용도 생깁니다. 장애 상황처럼 시간이 중요한 순간에는 이 컨텍스트 스위칭 비용이 더 크게 느껴졌습니다.
그래서 Home SRE 팀은 먼저 관측성 데이터를 한 흐름에서 볼 수 있는 기반으로 자체 호스팅형(self-hosted) LGTM-P 스택을 구축했습니다. LGTM-P는 Loki, Grafana, Tempo, Mimir, Pyroscope로 구성된 관측성 스택입니다. 저희는 애플리케이션에서 수집한 데이터를 OpenTelemetry Collector를 중심으로 받아, 메트릭은 Mimir, 로그는 Loki, 트레이스는 Tempo, 프로파일은 Pyroscope로 보내고, Grafana 안에서 이 데이터를 이어서 분석할 수 있는 구조를 만들었습니다.

자체 호스팅형 LGTM-P를 도입하면서 SRE 팀은 쿼리 성능, 보관 기간, 샘플링 정책, 라벨 규칙, 데이터 간 연결 방식, 자체 플러그인과 AI 기능 연동 방식을 더 직접적으로 조정할 수 있게 됐습니다. 하지만 데이터가 한곳에 모였다고 해서 분석이 자동으로 쉬워지는 것은 아니었습니다.
데이터가 모여도, 조회 방법은 여전히 사람이 알고 있어야 했습니다
Grafana 안에서 메트릭·로그·트레이스·프로파일을 이어서 볼 수 있게 되었지만, 여전히 사용자는 어떤 데이터소스를 선택해야 하는지, 어떤 라벨로 서비스를 찾아야 하는지, 어떤 시간 범위로 쿼리해야 하는지 알고 있어야 했습니다. 메트릭에서 발견한 이상 징후를 어떤 로그와 트레이스로 이어 봐야 하는지도 사용자가 직접 판단해야 했습니다.
예를 들어 같은 서비스 이름도 데이터 소스마다 다른 라벨로 표현될 수 있습니다. Prometheus 계열 메트릭에서는 service_name을 쓰지만, Tempo에서는 resource.service.name을 써야 할 수 있습니다. Loki에서는 로그 본문을 JSON으로 파싱해야 하는 경우도 있고, 구조화된 메타데이터(structured metadata)를 직접 필터해야 하는 경우도 있습니다.
숙련된 SRE는 이런 차이를 경험으로 알고 있습니다. 하지만 이 지식은 문서보다 사람의 머릿속에 있는 경우가 많았습니다. 저희는 이 운영 지식을 LLM이 활용할 수 있는 형태로 명문화하고 싶었습니다.
SRELens는 바로 이 간극을 줄이기 위해 시작했습니다. 자체 호스팅형 LGTM-P가 관측성 데이터를 한 흐름 안에 모으는 기반이라면, SRELens는 ‘모인 데이터를 자연어로 탐색하고 원인 분석 흐름으로 연결하는 인터페이스’입니다.
왜 직접 만들었나: 오픈소스 PoC에서 확인한 한계
처음부터 자체 개발을 결정한 것은 아닙니다. 먼저 Grafana에 LLM을 결합하는 오픈소스 플러그인을 활용해 PoC를 진행했습니다. PoC를 통해 가능성은 확인했지만, 실제 운영 환경에 적용하기에는 몇 가지 한계가 있었습니다.
첫 번째는 사용자 컨텍스트와 권한 전파 관점의 한계였습니다. 저희 환경에서는 Grafana가 인증한 사용자 정보를 기준으로 대화 기록, 사용량 한도, 조회 권한을 관리해야 했습니다. 하지만 PoC 단계에서 확인한 결과 사용자 컨텍스트를 원하는 방식으로 안정적으로 전달받기 어려웠습니다.
두 번째는 시스템 프롬프트 제어 관점의 한계였습니다. 운영 도구에서 프롬프트는 단순한 문장이 아닙니다. 어떤 도구를 어떤 순서로 호출할지, 결과가 없을 때 어떻게 재시도할지, 대시보드 수정과 같은 위험한 작업을 어떻게 제한할지 같은 정책이 들어갑니다. 이 정책을 사용자 질의와 한 덩어리로 붙이는 방식으로는 조직 공통 규칙을 안정적으로 보호하기 어려웠습니다.
세 번째는 도구 호출 라운드 제한이었습니다. 장애 분석 질문은 보통 한 번의 도구 호출로 끝나지 않습니다. 메트릭에서 스파이크를 찾고, 트레이스에서 에러 스팬을 찾고, 로그에서 예외 메시지를 확인하는 식으로 여러 단계를 거쳐야 하는데 기본 라운드가 짧으면 중간에 분석이 끊깁니다.
네 번째는 데이터 소스별 라벨 차이였습니다. 같은 ‘서비스 이름’을 찾더라도 데이터 소스마다 라벨과 필드가 다릅니다. 이 차이를 LLM이 모르면 올바른 데이터 소스를 선택해도 결과가 0건으로 돌아옵니다.
마지막으로 운영과 라이선스 부담입니다. 저희가 원하는 수준으로 수정해 사내 운영 환경에 배포하기에는 제약이 있었습니다.
이 PoC를 통해 저희가 얻은 결론은 명확했습니다.
"자연어 질의 자체보다 중요한 것은, LLM 에이전트의 행동을 운영 환경에 맞게 통제할 수 있는 제어권이다."
그래서 SRELens를 직접 구현하기로 했습니다.
SRELens 아키텍처
SRELens는 Grafana 애플리케이션 플러그인입니다. 프런트엔드는 Grafana 안에서 채팅 UI를 제공하고, 백엔드는 LLM 호출과 도구 오케스트레이션, 프롬프트 합성, 사용량 제어를 담당합니다. 관측성 데이터 조회는 MCP(Model Context Protocol) 게이트웨이를 통해 수행합니다.

구성 요소별 역할은 다음과 같습니다.
| 영역 | 기술 | 역할 |
|---|---|---|
| 프런트엔드 | TypeScript, React, Grafana UI | 채팅 UI, 실시간 응답 표시, 이미지 첨부, Markdown 렌더링 |
| 백엔드 | Go, Grafana Plugin SDK | LLM 에이전트, 프롬프트 합성, 도구 호출 제어, 사용량 제한 |
| LLM | OpenAI의 툴 호출 지원 GPT 계열 모델 | 자연어 이해, 분석 계획 수립, 도구 호출 결정 |
| MCP 게이트웨이 | FlavaMCP | Mimir, Loki, Tempo 등 관측성 도구 호출 |
| 저장소 | Redis | 대화 기록, 사용자 프롬프트, 사용량 카운터 저장 |
| 관측성 백엔드 | Mimir, Loki, Tempo, Pyroscope | 메트릭·로그·트레이스·프로파일 데이터 제공 |
여기서 중요한 점은 백엔드가 단순 프록시가 아니라는 것입니다. SRELens 백엔드는 LLM에게 질문을 전달하는 역할을 넘어, 어떤 시스템 프롬프트를 적용할지, 도구를 몇 번까지 호출하게 할지, 결과가 너무 크면 어떻게 다룰지, 사용자별 비용을 어떻게 제한할지까지 통제합니다.
또한 SRELens는 FlavaMCP가 제공하는 업스트림 관측성 도구에 더해, Grafana 패널 검색과 이미지 렌더링을 위해 자체 플러그인 로컬 툴도 제공합니다. 예를 들어 find_grafana_panel과 render_grafana_panel은 SRELens 백엔드가 같은 툴 인터페이스 뒤에 합쳐 제공합니다. LLM 입장에서는 모두 동일한 툴처럼 보이지만, 실제 구현에서는 업스트림 MCP 툴과 로컬 Grafana 툴을 CompositeClient로 묶어 사용합니다.
핵심 설계 결정
SRELens를 만들며 가장 많이 고민한 것은 ‘어디까지 LLM에게 맡기고, 어디부터 코드와 정책으로 통제할 것인가’였습니다. 이 절에서는 주요 설계 결정을 소개하겠습니다.
결정 1. 시스템 프롬프트를 세 개의 레이어로 나눴습니다
프롬프트는 LLM 애플리케이션에서 가장 먼저 손대기 쉬운 영역입니다. 하지만 운영 도구에서 프롬프트는 단순한 지시문이 아닙니다. 프롬프트에 조직 공통 정책, 데이터 소스별 메타데이터, 개인의 선호가 섞이면 책임 주체가 불명확해집니다.
그래서 SRELens는 시스템 프롬프트를 세 레이어로 나눴습니다.
레이어 1. 베이스 프롬프트(Base System Prompt): 어떻게 일할지 정의합니다
베이스 프롬프트(Base는 SRELens의 전역 행동 정책입니다. 예를 들어 다음과 같은 규칙을 담습니다.
- 도구 호출 순서
- 대시보드 생성·수정·삭제와 같은 위험 작업의 안전 규칙
- 결과가 비었을 때의 폴백(fallback) 전략
- 결과가 잘렸을 때 다시 집계 쿼리로 조회해야 한다는 규칙
- 응답 형식과 근거 제시 방식
베이스 프롬프트는 조직 공통 정책이므로 관리자만 수정할 수 있습니다. 설정 값이 비어 있으면 코드에 포함된 기본 프롬프트를 사용하고, 관리자가 Grafana 설정에서 값을 채우면 그 값으로 대체합니다.

레이어 2. 데이터소스 프래그먼트(DataSource Fragment): 어디를 볼지 알려 줍니다
데이터소스 프래그먼트는 환경별 관측성 메타데이터입니다. 어떤 Mimir, Loki, Tempo datasource UID를 우선 사용할지, 서비스명을 찾을 때 어떤 라벨 후보를 시도할지, Loki 로그 필드는 어떻게 다뤄야 하는지 등을 YAML 형태로 제공합니다.
이 레이어는 SRELens에서 특히 중요했습니다. 에이전트가 매번 “어떤 데이터 소스를 써야 하지?”, “서비스 라벨은 service_name일까 resource.service.name일까?”부터 탐색하면 라운드가 낭비됩니다. 데이터소스 프래그먼트는 SRE가 알고 있는 운영 지식을 LLM이 사용할 수 있는 형태로 압축해, 분석 성공률과 속도를 함께 높입니다.
레이어 3. 사용자 프롬프트(User Prompt): 개인과 팀의 맥락을 덧붙입니다
사용자 프롬프트는 사용자 개인 또는 팀의 맥락입니다. 담당 서비스, 선호하는 응답 형식, 자주 보는 대시보드 이름 등을 저장할 수 있습니다. 이 값은 Redis에 저장되고, 시스템 프롬프트의 Additional Context 영역에 주입됩니다.
중요한 점은 사용자 프롬프트가 베이스 프롬프트를 덮어쓰지 않는다는 것입니다. 개인화는 조직 공통 정책 위에 덧붙는 형태입니다. 사용자가 자신의 선호를 저장해도 SRE 공통 안전 규칙은 유지됩니다.

| 계층 | 설정 주체 | 저장 위치 | 역할 |
|---|---|---|---|
| 베이스 프롬프트 | 관리자(admin)/SRE | 플러그인 설정 또는 내장 파일 | 전역 행동 정책과 안전 규칙 |
| 데이터소스 프래그먼트 | 관리자/SRE | 플러그인 설정 YAML | 데이터 소스 UID, 라벨 후보, 조회 관습 |
| 사용자 프롬프트 | 각 Grafana 사용자 | Redis | 개인·팀 맥락과 응답 선호 |
결정 2. 툴 오케스트레이터(Tool Orchestrator)를 백엔드의 단일 책임으로 뒀습니다
프런트엔드가 시스템 프롬프트를 조립하면 사용자가 정책을 우회할 여지가 생깁니다. 그래서 SRELens에서는 프런트엔드가 사용자가 보낸 메시지와 프런트엔드에서 보완할 수 있는 어시스턴트 메시지만 보내고, 시스템 프롬프트 합성은 모두 백엔드 오케스트레이터가 담당합니다.
오케스트레이터는 에이전트 루프를 실행합니다. 사용자의 질문을 LLM에 전달하고, LLM이 도구 호출을 요청하면 MCP 또는 로컬 툴을 실행한 뒤, 그 결과를 다시 LLM에 전달합니다. 이 과정을 최종 답변이 나올 때까지 반복합니다.
운영 환경에서는 이 루프에 안전 장치가 필요했습니다.
- 라운드 상한: 기본 최대 10라운드까지 도구를 호출합니다. 분석에 필요한 깊이는 확보하되 무한 루프는 막습니다.
- 중복 호출 차단: 같은 도구를 같은 인자로 반복 호출하지 않도록 호출 해시(callHash)를 사용합니다.
- 도구별 재시도 제한: 실패하는 도구를 무한 재시도하지 않도록 재시도는 기본 2회로 제한합니다.
- 개별 도구 결과 크기 제한: 관측성 응답이 너무 커지면 잘라 내고, 잘렸다는 사실을 명시합니다.
- 전체 요청 히스토리 제한: 여러 도구 결과가 누적되어 LLM 요청 전체가 커지면 오래된 툴 실행 결과부터 플레이스홀더로 치환합니다. 이때 툴 호출과 툴 결과의 짝이 깨지지 않도록 tool_call_id는 유지합니다.
- 노 데이터 힌트: 결과가 비었을 때 바로 포기하지 않고, 라벨 후보·시간 범위·데이터 소스를 바꿔 재시도하도록 유도합니다.
이 장치들은 모두 “LLM이 잘 판단해 주겠지”에 기대지 않기 위한 코드 레벨의 가드레일입니다.
결정 3. 비용과 요청량을 백엔드에서 제한했습니다
LLM 호출에는 비용이 따릅니다. 특히 툴 호출 기반 에이전트는 한 번의 사용자 질문이 여러 번의 LLM 호출과 도구 호출로 이어질 수 있습니다. 그래서 SRELens는 사용자별 사용량 제한을 백엔드에 넣었습니다.
기본 정책은 다음과 같습니다.
- 사용자별 일일 토큰 한도
- 사용자별 분당 요청 수 제한
- 한도 초과 시 HTTP 429 반환
- 응답 후 OpenAI가 반환한 실제 토큰 사용량를 사용자별 일일 버킷에 누적
- 일일 버킷은 Asia/Seoul 기준 자정에 리셋
여기서 핵심은 ‘사후 반영(post-accounting)’입니다. 요청 전에는 현재 사용량이 한도를 넘었는지 검사하고, 응답 후에는 실제 사용한 프롬프트 토큰과 출력(completion) 토큰을 누적합니다. 추정치가 아니라 모델이 반환한 사용량을 기반으로 운영·비용 모니터링을 할 수 있습니다.

Redis가 일시적으로 사용할 수 없는 상황도 고려했습니다. Redis는 대화 기록, 사용자 프롬프트, 한도(quota) 저장소로 사용되지만, Redis 장애가 곧 SRELens 전체 장애가 되지는 않도록 했습니다. Redis 연결에 실패하면 대화 기록과 개인화 기능은 제한되지만, 단일 채팅 요청은 계속 처리할 수 있습니다. 운영 도구에서 중요한 것은 전체 기능이 한 번에 멈추지 않게 하는 것이었습니다.
실제 분석 시나리오
이제 SRELens가 실제로 어떤 차이를 만들었는지 세 가지 시나리오로 살펴보겠습니다. 예시는 공개 가능한 형태로 서비스명과 식별자를 일반화했습니다.
시나리오 1. 에러 스파이크의 원인을 API 요청 패턴까지 좁히기
한 베타 서비스에서 에러 카운트가 급증했습니다. 기존 방식이라면 먼저 알림 쿼리를 확인하고, 로그 플랫폼에서 같은 시간대의 에러(ERROR) 로그를 검색한 뒤, 필요하면 트레이스 도구를 열어 어떤 API가 문제를 주도했는지 다시 찾아야 했습니다.
SRELens에서는 대시보드 링크와 함께 다음처럼 질문했습니다.
"이 대시보드와 관측성 데이터를 바탕으로, 09:50~10:05 사이 beta 환경에서 오류가 급증한 원인을 분석해 줘."

SRELens는 메트릭과 로그, 트레이스를 함께 확인한 뒤 다음과 같은 결론을 제시했습니다.
해당 구간에 CopyMedia 요청이 급증했고, 이 중 다수가 존재하지 않는 대상에 대한 복사 시도로 이어졌습니다. 그 결과 404(NotFound)와 이미 존재하는 리소스에 대한 409(AlreadyExists) 비즈니스 에러가 대량 발생했습니다. 이 에러들이 ERROR 로그와 error status 트레이스로 집계되면서 오류율이 증가한 것으로 보입니다. 인프라 장애보다는 잘못된·중복된 복사 요청 패턴이 오류율 증가의 주된 원인으로 보입니다.
중요한 점은 SRELens가 ‘에러가 늘었다’에서 멈추지 않았다는 것입니다. 어떤 종류의 요청이 늘었고, 어떤 비즈니스 에러로 이어졌으며, 왜 인프라 장애보다는 요청 패턴 문제로 볼 수 있는지까지 연결했습니다.
시나리오 2. 포이즌 필(poison-pill) 재시도 폭풍 분석하기
두 번째 사례는 데이터소스 프래그먼트의 효과가 가장 잘 드러난 사례입니다.
한 스트림 처리 서비스에서 짧은 시간 동안 같은 요청이 여러 번 실패했습니다. 단일 메시지를 다운스트림 HTTP API에 전달할 때마다 '400 Bad Request'가 반환됐고, 재시도 토픽을 통한 자동 재시도가 반복됐습니다. 외부 의존성 장애나 자원 부족이 아니라, 메시지 자체의 결함이 재시도 체인을 통해 증폭되는 포이즌 필 패턴이었습니다.
기존 방식에서는 여러 화면을 오가야 했습니다.
- 메트릭에서 경고/에러 스파이크를 확인합니다.
- 스트림 서비스 로그에서 실패 메시지를 찾습니다.
- 다운스트림 API 로그에서 400 응답 사유를 확인합니다.
- Tempo에서 동일 트레이스 안의 재시 도 체인을 확인합니다.
- Kafka 오프셋과 재시도 토픽 흐름을 맞춰 봅니다.
SRELens에서는 다음과 같이 물었습니다.
"16:50~16:55 사이 dev 환경의 stream-processor-beta 서비스에서 같은 요청이 여러 번 실패한 사건이 있었어. 먼저 Tempo에서 error 트레이스를 찾아 트레이스 ID, 호출된 downstream, HTTP status를 알려줘. 그다음 Loki에서 같은 시간대 이 서비스의 로그를 보여줘."


초기에는 이 질문이 항상 성공하지는 않았습니다. 저희는 같은 질문을 프롬프트 개선 단계별로 반복 실행했습니다.
- 단순한 기본적인 프롬프트에서는 트레이스 검색이 재시도 한도에 걸려 실패했습니다.
- 데이터소스 프래그먼트와 베이스 프롬프트 개선 전에는 Tempo 조회는 성공했지만 Loki에서 잘못된 데이터소스와 필터를 선택해 결과가 0건이었습니다.
- 데이터소스 프래그먼트와 베이스 프롬프트 개선 후에는 동일한 짧은 자연어 질문으로 Tempo와 Loki 모두 정확한 데이터소스와 쿼리에 도달했습니다.
개선 후 SRELens가 사용한 쿼리는 다음과 같은 형태였습니다.
{resource.service.name="stream-processor-beta" && status=error}
{service_name="stream-processor-beta"} | detected_level="ERROR"
두 번째 쿼리가 특히 중요했습니다. 해당 환경의 Loki에서는 에러 레벨이 로그 본문 JSON 필드가 아니라 구조화된 메타데이터에 들어 있었기 때문입니다. 데이터소스 프래그먼트가 이 관습을 알려 주지 않았다면 에이전트는 | json 파서를 붙여 결과 0건을 받을 가능성이 높았습니다.
최종적으로 SRELens는 동일 트레이스 안의 여러 재시도, 모두 400을 반환한 다운스트림 클라이언트 스팬(downstream client span), 그리고 로그의 예외 메시지까지 연결했습니다. 결론은 ‘업스트림이 발행한 메시지의 필수 필드가 비어 있어 발생한 포이즌 필 재시도 폭풍’이었습니다. 후속 조치로는 메시지 발행 측 확인(validation) 강화와 스키마 오류를 DLQ(Dead Letter Queue)로 분류하는 재시도 정책 개선을 제안했습니다.
이 사례에서 저희가 얻은 교훈은 분명했습니다.
"데이터소스 프래그먼트는 단순한 프롬프트가 아니라, SRE가 알고 있는 운영 지식의 압축본입니다."
시나리오 3. 로그가 없을 때 “모른다”고 말하기
세 번째 사례는 SRELens가 무엇을 할 수 있는지보다, 무엇을 하지 않는지가 더 중요했던 사례입니다.
한 API 서비스에서 요청 지연이 증가했고, 트레이스에서는 약 1~2초 수준의 타임아웃 에러 스팬과 HTTP 500이 반복적으로 나타났습니다. 그런데 질의 시점에는 해당 서비스의 로그가 아직 Loki로 수집되기 전이었습니다. 즉, 로그 근거 없이 메트릭과 트레이스만으로 분석해야 했습니다.
SRELens의 응답은 다음과 같았습니다.
"현재 확보된 근거는 Tempo 트레이스이며, 트레이스는 서비스 내부에서 1~2초 수준의 지연과 함께 status=error / HTTP 500이 발생했음을 보여 줍니다. 가장 유력한 원인은 downstream 호출의 지연 또는 오류입니다. 다만 Loki 로그는 조회되지 않아 로그 기반 근거는 확보하지 못했습니다. 추가 근거 확보가 필요합니다."
이 답변이 좋았던 이유는 단순합니다. SRELens는 없는 데이터를 지어내지 않았습니다. 사용 가능한 근거와 부족한 근거를 나눠 말했고, 트레이스로 확인 가능한 범위 안에서만 가설을 세웠습니다.
관측성 도구에서 “모른다”를 정직하게 말하는 것은 잘못된 확신을 주는 것만큼이나 중요합니다. SRELens의 베이스 프롬프트와 오케스트레이터 가드레일은 이 원칙을 지키기 위해 설계됐습니다.


개발하며 만난 도전과 해결
LLM이 엉뚱한 라벨을 선택했습니다
가장 자주 만난 문제는 라벨 선택이었습니다. 같은 서비스명이라도 데이터 소스마다 라벨이 다르기 때문에 첫 쿼리가 실패하는 경우가 많았습니다.
저희는 데이터소스 프래그먼트에 라벨 후보와 데이터소스별 주의 사항을 넣고, 베이스 프롬프트에는 폴백 순서를 명시했습니다. 예를 들어 특정 라벨로 결과가 없으면 다른 후보 라벨을 시도하고, 너무 좁은 시간 범위라면 조금 넓혀 재조회하도록 했습니다.
관측성 응답이 너무 컸습니다
로그나 트레이스 결과는 쉽게 수십 KB에서 수백 KB를 넘습니다. 이를 그대로 LLM에 넣으면 컨텍스트가 빠르게 가득 찹니다.
SRELens는 두 단계로 대응했습니다. 먼저 코드에서 개별 툴 결과 크기를 제한하고, 결과가 잘렸다는 마커를 붙입니다. 다음으로 베이스 프롬프트에서 “잘린 결과를 근거로 답하지 말고, 집계 쿼리로 다시 조회하라”는 규칙을 둡니다.
예를 들어 로그를 처음부터 모두 가져오지 않고 count_over_time, topk, sum by (…) 같은 집계를 먼저 사용하도록 유도합니다. 코드는 잘렸다는 신호를 남기고, 프롬프트는 그 신호를 봤을 때의 올바른 행동을 정합니다. 두 장치가 함께 있어야 안전했습니다.
도구 호출 루프가 길어질 수 있었습니다
LLM 에이전트는 때때로 같은 도구를 반복 호출하거나, 실패한 쿼리를 조금씩 바꿔 계속 시도합니다. 장애 분석에서는 어느 정도의 재시도가 필요하지만, 무제한으로 허용하면 응답 지연과 비용 증가로 이어집니다.
그래서 SRELens는 max rounds, tool retry limit, duplicate call block을 함께 사용합니다. 이 장치들은 LLM을 믿지 않는다는 뜻이 아닙니다. 운영 도구로 만들기 위해 필요한 안전벨트입니다.
LLM 응답도 신뢰할 수 없는 입력으로 다뤘습니다
프런트엔드는 Markdown, 코드 블록, 표, Mermaid 다이어그램을 렌더링합니다. 하지만 LLM 응답은 신뢰할 수 없는 입력이기도 합니다. 따라서 응답받은 원본 그대로의 HTML은 렌더링하지 않고, Mermaid 결과는 정제(sanitization) 작업을 거치며, 링크와 이미지 URL도 허용된 스킴만 통과시키도록 했습니다.
LLM 애플리케이션에서 보안은 백엔드만의 문제가 아니었습니다. 사용자에게 보여 주는 UI도 같은 수준으로 조심해야 했습니다.
사용자 경험을 위한 고민
SRELens가 아무리 정확해도 쓰기 불편하면 실제 장애 대응 과정에서 사용되지 않습니다. 그래서 UI/UX에서도 몇 가지 원칙을 세웠습니다.
답변을 기다리는 동안 무엇을 하는지 보여 줍니다
SRELens는 응답을 한 번에 기다리지 않고 SSE(Server-Sent Events)로 실시간 스트리밍합니다. 텍스트 응답뿐 아니라 tool_call, image, link, done, error 같은 이벤트를 나눠 보냅니다.
특히 에이전트가 Prometheus, Loki, Tempo 같은 도구를 호출하는 동안 UI에 도구 이름을 노출합니다. 사용자는 ‘멈춘 것인지, 분석 중인지’를 추측하지 않아도 됩니다.

길어지는 응답은 중단할 수 있습니다
장애 분석 질문은 도구 호출이 길어질 수 있습니다. 사용자는 Stop 버튼으로 생성을 중단할 수 있고, 이미 받은 답변은 채팅에 남습니다. 부분 응답도 분석 과정에서는 가치가 있을 수 있기 때문입니다.
패널 이미지를 대화 안으로 가져옵니다
텍스트만으로는 부족한 경우가 있습니다. 예를 들어 "이 서비스의 latency 패널을 보여 줘"라는 질문에는 숫자 요약보다 그래프가 더 직관적입니다.
SRELens는 find_grafana_panel로 적절한 패널 후보를 찾고, render_grafana_panel로 Grafana 이미지 렌더러를 호출해 PNG 이미지를 가져옵니다. 사용자는 Grafana 화면을 따로 열지 않아도 채팅 흐름 안에서 패널 스냅샷을 확인하고, 필요한 경우 첨부된 링크로 이동할 수 있습니다.

대화 기록과 개인화는 자연스럽게 이어집니다
대화 기록은 Redis에 저장되고, 같은 대화를 이어갈 때 이전 맥락을 함께 사용합니다. 대화 기록과 대화 목록은 30일 동안 사용하지 않으면 자동 정리되고, 대화하거나 목록을 볼 때마다 TTL이 갱신됩니다.
반면 사용자 프롬프트는 별도로 유지됩니다. 담당 서비스나 선호하는 응답 방식은 오래 쓰지 않았다는 이유로 사라지면 안 되기 때문에, 사용자가 직접 수정하거나 삭제하기 전까지 유지합니다.
SRELens는 Grafana가 인증한 사용자 컨텍스트를 기준으로 기록과 사용량 한도를 관리합니다. 저희 운영 환경에서는 Grafana 로그인이 GitHub OAuth와 연동되어 있어, 결과적으로 GitHub 계정 기준으로 사용자별 기록과 한도가 적용됩니다.
SRELens를 만들며 배운 점
첫 번째 교훈은 LLM을 붙이는 일보다 제어권을 설계하는 일이 더 중요하다는 것입니다. 프롬프트를 누가 바꿀 수 있는지, 도구 호출을 몇 번까지 허용할지, 결과가 비었을 때 어떻게 재시도할지, 비용은 어디서 막을지 같은 질문이 제품의 안정성을 결정했습니다.
두 번째 교훈은 운영 지식을 프롬프트 가능한 형태로 바꾸는 일이 분석 품질을 좌우한다는 것입니다. 데이터소스 프래그먼트는 단순한 힌트가 아니었습니다. 서비스 라벨, 데이터소스 UID, 로그 필드 관습 같은 지식을 명문화했을 때 에이전트는 훨씬 적은 라운드로 올바른 쿼리에 도달했습니다.
세 번째 교훈 은 없는 근거를 없다고 말하는 능력도 기능이라는 것입니다. 장애 분석 도구가 잘못된 확신을 주면 오히려 대응을 늦춥니다. SRELens는 조회 실패, 노 데이터, 잘라내기(truncation) 같은 상태를 숨기지 않고 드러내도록 설계했습니다.
앞으로의 계획
앞으로는 SRELens가 분석 결과를 더 쉽게 검증할 수 있도록 근거 링크와 재현 쿼리 제공을 강화하려고 합니다. 사용자가 “왜 이런 결론이 나왔는지”를 바로 확인하고, 필요하면 같은 쿼리를 Grafana Explore에서 다시 실행할 수 있게 만드는 것이 목표입니다.
또한 자주 발생하는 장애 패턴을 템플릿화해 반복적인 분석 흐름을 더 짧은 질문으로 실행할 수 있도록 개선할 계획입니다. 예를 들어 Kafka 랙, 타임아웃 스파이크, 포이즌 필 재시도 처럼 반복되는 패턴은 필요한 메트릭·로그·트레이스 조회 순서가 어느 정도 정해져 있습니다. 이를 SRELens 안에서 더 잘 활용하고 싶습니다.
마지막으로 사용량과 비용을 더 세밀하게 관찰할 수 있는 대시보드를 고도화하려고 합니다. LLM 기반 도구를 안정적으로 운영하려면 기능뿐 아니라 사용량, 비용, 실패율, 도구 호출 패턴까지 함께 봐야 하기 때문입니다.
마치며
SRELens는 ‘Grafana에 챗봇을 붙인 도구’가 아닙니다. 저희가 만들고 싶었던 것은 SRE가 이미 알고 있는 운영 지식과 관측성 데이터를 LLM 에이전트가 안전하게 사용할 수 있도록 연결하는 도구였습니다.

흩어진 화면을 오가며 신호를 머릿속으로 조립하던 일이, 이제는 Grafana 안에서 한 번의 질문으로 시작됩니다. 물론 모든 답을 자동으로 얻을 수 있는 것은 아닙니다. 하지만 SRELens는 적어도 어디를 먼저 봐야 하는지, 어떤 근거가 있고 무엇이 부족한지, 다음에 어떤 확인을 해야 하는지를 더 빠르게 정리해 줍니다.
비슷한 고민을 하고 계신 SRE, 플랫폼 엔지니어, LLM 애플리케이션 개발자분들께 이 글이 작은 참고가 되기를 바랍니다. 읽어 주셔서 감사합니다.


