
에이전트를 깨운 알림
요약
SigNoz 웹훅과 LLM 에이전트를 결합하여 장애 발생 시 자동으로 원인을 조사하고 보고서를 작성하는 'Monitors in Black' 시스템 구축 과정을 다룹니다. 관측성 플랫폼의 알림을 트리거로 활용해 개발자의 개입 없이 초기 조사를 수행하는 자동화 시도를 소개합니다.
핵심 포인트
- SigNoz 웹훅을 활용한 LLM 에이전트 호출 자동화
- 장애 발생 시 조사 초안을 작성하는 에이전트 구현
- OpenTelemetry와 FastAPI를 이용한 테스트 환경 구축
- 알림 발생부터 원인 파악까지의 시간 간극 단축 목표
SigNoz 웹훅과 LLM을 활용한 사고 자동 분류(Auto-triaging), 그리고 거의 아무도 넘지 못하는 장벽.
새벽 3시 7분, 내 휴대폰 불빛이 천장을 밝힙니다. 결제 서비스의 에러율 임계치 초과(Error rate breach). 나는 모든 온콜(on-call) 개발자가 암기하고 있는 의식을 수행합니다. 노트북을 열고, SigNoz를 열고, 로그를 최근 15분으로 필터링하고, 가장 보기 흉한 트레이스(trace)를 찾아 무언가 자백할 때까지 폭포처럼 쏟아지는 데이터의 흐름을 따라 내려갑니다. 20분 후, 나는 근본 원인(root cause)과 해결책을 찾아냅니다. 하지만 한 가지 생각이 계속 머릿속을 맴돕니다. 내가 취한 모든 단계는 순전히 근육 기억(muscle memory)에 의한 것이었습니다. 알림이 잘못된 에이전트를 깨운 것입니다.
그래서 나는 그것을 고쳐보기로 했습니다. 목표는 알림이 대신 LLM 에이전트를 깨우도록 만드는 것이었습니다. 사람이 휴대폰 잠금을 해제하기도 전에 조사의 초안을 작성하는 에이전트 말이죠. 또한, 이 아이디어가 보통 어디서 죽는지, 즉 내가 정면으로 부딪혔던 바로 그 장벽에 대해서도 보여드리겠습니다.
그날 밤의 결과물은 다음과 같습니다. 나는 이것을 'Monitors in Black'이라 부릅니다. 의도적으로 고장 내는 가상의 마이크로서비스(microservices) 도시, 하나의 알림 규칙, 그리고 나 대신 호출(paged)되는 에이전트로 구성된 작은 데모 스택입니다. 이 에이전트는 케이스 파일을 열고, 스스로 증거를 수집하고, 판결을 내린 뒤, 코드 이름까지 포함하여 루프를 닫습니다. 유치한 전제 같지만, 그 밑바탕에는 실제 목표가 있습니다. 바로 알림이 발생한 시점과 누군가가 실제로 무슨 일이 일어났는지 알게 되는 시점 사이의 간극을 줄이는 것입니다.
아이디어
여기서 실제로 감시를 수행하는 것은 SigNoz입니다. 사용해 본 적이 없다면, 이는 오픈 소스 관측성(observability) 플랫폼입니다. 트레이스(traces), 로그(logs), 메트릭(metrics), 그리고 알림 엔진을 갖추고 있어, 무엇이 고장 났는지 확인하기 위해 세 개의 별도 로그인을 하나로 엮을 필요가 없습니다. SigNoz는 이미 힘든 일을 수행하고 있습니다. 무언가 고장 나는 즉시 이를 인지하며, 해당 순간에 당신이 소유한 어떤 HTTP 엔드포인트로든 핑(ping)을 보낼 수 있는 웹훅(webhook) 알림 채널을 가지고 있습니다.
계획은 명확해 보입니다. 알림이 발생하면, 웹훅이 커스텀 서비스에 전달되고, 서비스가 LLM에게 조사를 요청하면, LLM이 보고서를 작성하는 것입니다. 잠들지 않는 주니어 조사관 말이죠.
나는 이 계획에 어느 날 저녁을 투자했습니다. 처음 90분은 완벽했습니다.
설정
나는 자체 호스팅(self-hosted)된 SigNoz 인스턴스를 구축했습니다:
git clone -b main https://github.com/SigNoz/signoz.git
cd signoz/deploy/docker
docker compose up -d
이 명령어는 포트 8080에서 UI를 노출하며 SigNoz v0.133.0을 로컬 환경에 실행합니다.
다음으로, OpenTelemetry로 계측(instrumented)된 의도적으로 취약한 FastAPI 앱을 작성했습니다. 이 앱에는 플래그 하나가 숨겨져 있어, 해당 플래그를 전환하면 요청의 60%가 500 오류와 함께 실패하도록 강제합니다. 작은 트래픽 생성기(traffic generator)가 계속해서 요청을 흘려보냈고, 몇 분 만에 서비스 맵이 활성화되었습니다.

저는 오류율(1분 동안 임계값 >20%)에 대한 알림 규칙을 설정하고, 웹훅 채널을 제 수신기로 지정했습니다:
@app.post("/hooks/signoz")
async def signoz_hook(request: Request):
if request.headers.get("x-agency-token") != WEBHOOK_SECRET:
...
저는 혼란 플래그를 전환했고, 오류가 증가하는 것을 지켜보며 기다렸습니다. 알림이 울렸고, 07:13:10 UTC에 제 엔드포인트에 도달했습니다. 영화 음악을 틀 시간입니다.
벽(The wall)
그리고 저는 수신된 페이로드(payload)를 출력했습니다.
레이블(Labels), 주석(annotations), 지문(fingerprint), 그리고 규칙 이름만 있었습니다. 그게 전부였습니다. 오류가 경보를 울린 로그(logs)도, 트레이스 ID(trace IDs)도, 샘플도 없었습니다. 제 조사관 에이전트는
아마 대부분의 사람들은 이 지점에서 노트북을 덮어버릴 것입니다. 페이로드(payload)에 직접 증거를 채워 넣는 것과 같은 명백한 해결책은 사용할 수 없습니다. 막다른 길처럼 느껴집니다.
전환점
당연한 사실을 깨닫기까지 부끄러울 정도로 많은 양의 차(tea)를 마셔야 했습니다. 페이로드가 증거를 직접 운반할 필요는 없다는 사실 말입니다. 진짜 조사관들도 현관문 앞에서 파일 상자를 건네받지는 않습니다. 그들은 주소를 받고 직접 가서 증거를 수집합니다.
SigNoz는 쿼리 API (Query API)를 노출합니다. 설정(Settings)에서 API 키를 생성하면, 어떤 서비스든 UI에서 보여주는 것과 정확히 동일한 로그(logs), 트레이스(traces), 메트릭(metrics) 데이터를 원하는 시간 범위 내에서 가져올 수 있습니다.
구체적으로, 엔드포인트(endpoint)는 SIGNOZ-API-KEY 인증 헤더를 사용하는 POST /api/v5/query_range입니다.
그렇게 에이전트의 직무 기술서가 바뀌었습니다. 웹훅(webhook)은 이제 무엇이 언제 발생했는지만 알려줍니다. 그러면 에이전트는 해당 타임스탬프(timestamp)를 기준으로 전후 15분간의 데이터를 SigNoz에 쿼리하여, 상위 에러 로그(error logs), 해당 서비스의 가장 느린 트레이스(slowest traces), 그리고 경보 메트릭(alerting metric)의 형태를 가져옵니다. 에이전트 스스로 사건 파일을 구축하는 것입니다.
async def investigate(case):
logs = signoz.query_logs(service=case.service, window_min=15, only_errors=True, limit=20)
traces = signoz.query_traces(service=case.service, window_min=15, slowest=10)
...
LLM 프롬프트(prompt)에는 그 어떤 프롬프트 엔지니어링(prompt engineering)보다 중요한 하나의 구조적 규칙이 있습니다. 바로 로그와 트레이스 콘텐츠는 신뢰할 수 없는 데이터(untrusted data)라는 점입니다. 이 데이터들은 명확하게 구분된 블록(delimited blocks) 안에 들어가야 하며, 지침(instructions)은 모델에게 해당 블록 내부의 모든 것을 분석할 데이터로 취급하되, 결코 따라야 할 명령으로 취급하지 말라고 명시해야 합니다. 로그에는 무엇이든 포함될 수 있습니다. 로그를 상사의 명령이 아닌, 용의자의 진술처럼 취급하십시오.
저는 다시 플래그(flag)를 뒤집었습니다. 경보가 울렸습니다. 60초 후, 제 터미널에는 실패한 서비스를 명시하고, OpenTelemetry 익스포터(exporter)에서 직접 가져온 연결 거부(connection refusal) 에러를 인용하며, 트레이스 ID(trace IDs)를 지목하고, 이 모든 상황을 "에러 스웜(Error Swarm)"으로 분류한 보고서가 나타났습니다. 심지어 출처까지 인용되어 있었습니다.


# CASE FILE #case-0020
**Status:** NEURALYZED
...
제가 배운 점 (What I learned)
- 웹훅(webhook)은 서류철이 아니라 초인종입니다. 알림 자체가 조사 내용을 담으려고 애쓰지 마세요. 에이전트에게 주소만 주고, Query API를 통해 스스로 증거를 수집하도록 하세요. 이 접근 방식은 페이로드 파싱(payload parsing)으로는 절대 할 수 없을 버전 업그레이드에서도 훨씬 더 잘 작동합니다.
- 그래도 페이로드는 방어적으로 파싱하세요. 알림 필드는 릴리스마다 변경되므로, 제 리시버는 모든 것을 선택 사항으로 취급하며 타임스탬프(timestamp)와 규칙 이름만 요구합니다.
- 증거를 제한하세요 (Cap the evidence). 제가 처음 배포했을 때는 모델에 200개의 에러 로그를 보냈습니다. 느리고 비용이 많이 들었으며 보고서의 질도 떨어졌습니다. 20개의 로그와 10개의 트레이스(traces)가 더 날카로운 결론을 도출했습니다. 조사관들도 노이즈에 빠지기 마련입니다.
- 신뢰할 수 없는 입력을 보안 경계로 취급하세요. 에이전트가 나중에 행동을 수행할 수 있고, 제 것도 결국 그렇게 될 것이라면, 로그 라인 안에 있는 적대적인 문자열(hostile string)이 절대 그것을 조종해서는 안 됩니다. 증거를 구분하고, 출력을 스키마(schema)로 제한하며, 모든 행동은 신뢰도 임계값(confidence threshold)과 허용 목록(allowlist) 뒤에 게이트를 설치해야 합니다.
- 간단한 범위 참고 사항: 이 방법은 제가 사용하는 환경에서 작동했습니다: Docker Compose를 통해 Linux에서 자체 호스팅된 SigNoz v0.133.0이며 OpenTelemetry Python SDK 1.44.0을 사용했습니다. 관리형 클라우드나 다른 버전에서는, 특히 페이로드 형태가 다를 수 있습니다.
크레딧 장면 (The credits scene)
어느 저녁의 작업으로 인해 새벽 3시짜리 페이지가 SigNoz 자체 알림 엔진(alert engine), Query API, 그리고 단 하나의 LLM 호출만을 사용하여 60초 분량의 자동 보고서로 바뀌었습니다. 알림은 여전히 발생하지만, 이제는 올바른 에이전트를 깨웁니다.
자료 (Resources)
- 웹훅 알림 채널 구성하기 (Configure a webhook notification channel) — 제가 처음에 어떻게 수신기에 알림이 발생하도록 만들었는지에 대한 내용입니다.
- SigNoz API 레퍼런스 (SigNoz API reference) — 웹훅이 수신기를 깨운 후, 트레이스 (traces), 로그 (logs), 메트릭 (metrics)을 가져오기 위해 수신기가 호출하는 쿼리 API (Query API) (v5,
query_range)입니다. - GitHub issue #11206 — 다른 누군가도
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기