
SigNoz MCP 기반의 AI SRE 에이전트: $0.0013를 위한 근본 원인 분석 (Root-Cause Analysis)
요약
SigNoz MCP를 활용하여 자율적으로 장애에 대응하고 관측 가능성을 감사하는 AI SRE 에이전트 'MIB' 구축 사례를 소개합니다. 단순한 질의응답을 넘어 선제적이고 반응적인 관측 가능성 플랫폼을 구현하는 아키텍처와 실무적 교훈을 다룹니다.
핵심 포인트
- SigNoz MCP 기반의 자율형 AI SRE 에이전트(Agent J, K) 구축
- OpenTelemetry 시맨틱 컨벤션을 활용한 에이전트 자체 계측
- 사후 대응적 관측성을 넘어선 선제적(Proactive) 플랫폼 지향
- 에이전트 설계 시 결정론적 플레이북과 신뢰 엔진의 중요성
🏆 WeMakeDevs × Agents of SigNoz Hackathon 2026을 위해 제작되었습니다
· GitHub: github.com/HARJAPAN2005/MIB-Men-in-Backend
· Demo:
· Hackathon: wemakedevs.org/hackathons/signoz
SigNoz MCP 상에서 어떻게 반응형(reactive) + 선제적(proactive) AI SRE 플랫폼을 구축했는지 — 그리고 우리 자신의 상위 메트릭(metrics) 중 38%가 아무런 이득 없이 비용만 발생시키고 있을 때 무엇을 배웠는지에 대하여.
⏱️ 12분 분량 · 🔧 Python · FastAPI · SigNoz MCP · OpenTelemetry · Foundry
프로젝트 개요 (Project at a Glance)
우리는 SigNoz MCP 서버 상에서 작동하는 두 개의 자율형 AI SRE 에이전트인 **MIB (Men in Backend)**를 구축했습니다.
| Agent J | Agent K | |
|---|---|---|
| 역할 (Role) | 장애 대응자 (Incident responder) | 관측 가능성 감사자 (Observability auditor) |
| ... |
두 에이전트 모두 OpenTelemetry GenAI 시맨틱 컨벤션(semantic convention) 스팬(span)을 사용하여 스스로를 계측(instrument)하며, 동일한 SigNoz로 데이터를 방출합니다. 결과적으로 SigNoz는 SigNoz를 감시하는 에이전트들을 감시하게 됩니다.
목차 (Contents)
- "트레이스(Traces)와 채팅하기"의 문제점
- 아이디어: 자율적인 관측 가능성 인력 (Autonomous Observability Workforce)
- 아키텍처 (Architecture)
- 구축 과정: 중요한 부분들
- Agent J의 플레이북: 결정론(Determinism)이 승리하는 이유
- 이중 디코딩(Double-Decode) 문제
- 호스트 재작성(Host-Rewrite) 문제
- Foundry 패치: 의도적인 성형 (Deliberate Molding)
- GenAI 시맨틱 컨벤션 스팬 (GenAI Semantic Convention Spans)
- Agent K: 자동으로 적용하지 않는 감사자
- 예측 엔진 (Predictive Engine)
- 신뢰 엔진 (Confidence Engine)
- SigNoz 통합: 우리가 실제로 사용한 것
- 우리가 저지른 실수들
- 배운 점 (Lessons Learned)
- 예상치 못했던 부분
- 결론 (Conclusion)
"트레이스(Traces)와 채팅하기"의 문제점
가장 흔한 AI 관측 가능성(observability) 데모는 다음과 같습니다: LLM을 텔레메트리(telemetry) 백엔드에 연결하고, 질문을 던지면 답변을 얻는 방식입니다. "결제(checkout)의 에러율은 얼마인가요?" "어떤 서비스가 가장 느린가요?" 처음 한두 번은 인상적입니다. 하지만 다섯 번째쯤 질문을 던지게 되면, 다음과 같은 의문이 생깁니다:
모든 것을 볼 수 있는 도구가 왜 여전히 나의 질문을 필요로 할까요?
더 근본적인 문제는 관측성 (Observability)이 언제나 사후 대응적 (reactive)이었다는 점입니다. 대시보드는 이미 일어난 일을 보여줍니다. 알람 (Alerts)은 임계값이 넘은 후에야 울립니다. PagerDuty는 누군가를 깨우고, 그 사람은 새벽 2시에 메트릭 (metric) → 트레이스 (trace) → 로그 (log) 순으로 수동으로 추적하며, 어떤 결정론적 프로그램 (deterministic program)이라도 자동으로 조립할 수 있는 증거 체인을 구축합니다.
챗봇은 이 문제를 해결하지 못합니다. 챗봇은 여전히 사후 대응적입니다. 단지 질문에 더 빠르게 대답할 뿐입니다. 우리가 원했던 것은 다른 것이었습니다. 요청받지 않아도 스스로 행동 (act) 하고, 당신이 확인하는 것을 잊은 것들을 감사 (audit) 하며, 스스로를 관측하여 비용을 측정할 수 있게 하고, 알람이 울리기 전 (before) 에 문제를 표시하는 에이전트 (agents)입니다.
그것이 바로 MIB입니다.
아이디어: 자율적인 관측성 워크포스 (Workforce)
설계를 가능하게 한 사고 모델은 간단했습니다: 관측성 플랫폼을 데이터베이스가 아닌 작업 공간 (workplace)으로 취급하는 것입니다.
SigNoz는 트레이스 (traces), 로그 (logs), 메트릭 (metrics), 대시보드 (dashboards), 알람 (alerts) 등 모든 것을 보유하고 있으며, SigNoz MCP 서버는 이 모든 것을 구조화된 도구 인터페이스를 통해 노출합니다. 따라서 이를 쿼리(query)하는 챗봇을 만드는 대신, 정해진 일정에 따라 도구를 사용하고, 정의된 직무 기술서 (job descriptions)를 가지며, 파괴적인 작업에는 인간의 승인 단계 (human-approval gates)를 거치고, 비용 보고서 (조사당 토큰 비용)를 제출하는 직원 (employees) 을 만들었습니다.
두 종류의 에이전트:
- Agent J (페이지를 받는 신입): 알람 발생 → J가 정해진 5단계 MCP 플레이북 (playbook) 실행 → 정확한 트레이스로 바로 연결되는 원클릭 링크가 포함된 근본 원인 (root-cause) 카드를 Slack에 게시.
- Agent K (구역을 점검하는 베테랑): 관측성 스택 자체에 대한 정기 감사 — 어떤 메트릭이 비용을 발생시키지만 아무도 읽지 않는지, 어떤 알람이 죽은 채널로 울리는지 등을 확인 — 그 후 승인 단계를 거쳐 하나의 해결책을 제안.
그리고 반전이 있습니다: 두 에이전트 모두 OpenTelemetry GenAI 시맨틱 컨벤션 (semantic convention) 스팬 (spans)을 사용하여 스스로를 계측 (instrument) 하며, 이를 동일한 SigNoz로 방출합니다. 결과적으로 SigNoz가 SigNoz를 감시하는 에이전트들을 감시하게 됩니다. 우리는 이 계층을 Neuralyzer라고 불렀습니다.
아키텍처 (Architecture)
demo-lite app ──OTLP──► SigNoz (Foundry: docker/compose)
│ alert webhook ▲
▼ │ OTel (GenAI spans + metrics)
...
동일한 SigNoz 인스턴스 내의 세 가지 관측성 계층 (observability layers):
| 계층 | 정의 | SigNoz가 보는 데이터 |
|---|---|---|
| 1 — Victim app | demo-lite, 14개 마이크로서비스 (microservices) | OTLP traces, logs, metrics |
| ... | ||
![]() |
Mission Control++: 실시간 에이전트 활동, 예측적 장애 경고, 그리고 증거 그래프 (evidence graph).
핵심 구성 요소 (Key Components)
SigNoz + Foundry — 우리는 SigNoz의 자체 선언적 설치 도구인 Foundry를 통해 SigNoz를 배포합니다. casting.yaml 파일은 47줄로 구성되어 있으며, 생성된 compose 파일에 의도적인 세 개의 RFC 6902 JSON 패치(patches)를 포함하고 있습니다. 이에 대해서는 잠시 후에 더 자세히 다루겠습니다.
SigNoz MCP — MCP 서버는 traces, logs, metrics, dashboards, alerts, 그리고 notification channels를 포함하여 SigNoz의 모든 계층을 아우르는 41개의 도구(tools)를 노출합니다. 에이전트들은 플랫폼에 접속하기 위한 유일한 인터페이스로 이를 사용합니다.
Agency HQ — SigNoz의 Alertmanager로부터 alert webhooks를 수신하고, 조사를 Agent J에게 백그라운드 asyncio 태스크로 전달하여(webhook이 빠르게 반환되도록 함), 필요에 따라 Agent K를 실행하는 FastAPI 애플리케이션입니다. 또한 Mission Control++ 웹 UI와 SSE 이벤트 스트림(event stream)을 호스팅합니다.
Mission Control++ — 예측적 장애 엔진(predictive incident engine), 리플레이 시스템(replay system), 델타 귀속(delta attribution) 기능이 포함된 신뢰도 엔진(confidence engine), AI 토론 계층(AI debate layer), 증거 그래프(evidence graph) 생성, 그리고 Markdown 및 PDF 형식의 자동 사후 분석(auto-postmortem) 생성을 구현하는 별도의 모듈(mission_control_plus.py, 약 655줄)입니다. 표준 라이브러리와 httpx 외에는 외부 의존성이 없습니다.
구축하기: 중요한 부분들
Agent J의 플레이북: 결정론(Determinism)이 승리하는 이유
가장 어려웠던 설계 결정은 동시에 첫 번째 결정이기도 했습니다. Agent J를 스스로 도구를 선택하는 진정한 에이전트 루프 (agentic loop)로 만들 것인가, 아니면 고정된 결정론적 플레이북 (deterministic playbook)으로 만들 것인가 하는 문제였습니다.
우리는 고정된 방식을 선택했습니다. 그 이유는 다음과 같습니다:
중간 결과에 따라 도구를 선택하는 에이전트 루프는 동일한 장애 상황에 대해 서로 다른 조사 경로를 생성합니다. 이는 학술적으로는 흥미로울 수 있으나, 데모 상황에서는 재앙에 가깝습니다. 더 중요한 점은, 서비스 성능 저하를 조사하는 SRE (Site Reliability Engineer)에게 무한한 조사 자유가 주어지는 것은 아니라는 사실입니다. 여기에는 정형화된 작업 순서가 존재합니다:
- 알람(alert) 발생 확인
- 잘못된 트레이스 (traces) 찾기
- 가장 심각한 트레이스 심층 분석
- 로그 (logs) 상관관계 분석
- 메트릭 (metric) 변화량 측정
모든 유능한 SRE는 이 과정을 수행합니다. 우리는 이를 코드로 구현했습니다.
그 결과 agent_j.py에 run_playbook() 함수가 탄생했습니다. 150줄의 코드이며, 다섯 번의 순차적인 MCP 호출로 구성됩니다. LLM은 오직 종합(synthesis)만을 위해 단 한 번만 개입합니다:
resp = await litellm.acompletion(
model=AGENT_J_MODEL,
messages=[{"role": "system", "content": SYS_PROMPT},
...
reasoning_effort="disable" 라인은 실제 버그를 해결하는 과정에서 도입되었습니다. 모든 인스트루멘테이션 (instrumentation)이 완료된 첫 번째 실행에서, LLM 호출에 16초가 소요되었고 잘린(truncated) JSON이 반환되었습니다. 원인은 gemini-2.5-flash가 사고 모델 (thinking model)이기 때문이었습니다. max_tokens=1600 설정과 사고 기능이 활성화된 상태에서, 모델이 JSON을 출력하기도 전에 사고 과정 (chain-of-thought reasoning)에 대부분의 토큰 예산을 소모해 버렸고, 결국 출력이 잘린 것입니다. reasoning_effort="disable"로 설정하자 지연 시간(latency)은 약 2초로 단축되었고, 파싱 실패율은 0이 되었습니다.
모든 MCP 호출에는 searchContext 인자도 전달되는데, 이는 단순히 장식용이 아닙니다. MCP 서버는 이를 사용하여 자체 텔레메트리 (telemetry)에 태그를 지정하며, 이것이 우리 모니터링 대시보드의 레이어-3 패널이 작동하는 방식입니다:
s2 = await mcp_call(client, "signoz_search_traces",
{"service": svc, "error": True, "start": start_ms, "end": end_ms,
"limit": 25, "searchContext": ctx + " (step 2: error traces)"})
gen_ai.tool.name별 mcp.tool.calls는 도구별 상세 내역을 보여주며, searchContext는 각 호출에 대해 트레이스(trace) 내에서 사람이 읽을 수 있는 레이블을 제공합니다.
약 3.7초 만에 생성된 Agent J의 인시던트 카드 — 근본 원인(root cause), 증거 체인(evidence chain), 그리고 원클릭 트레이스 링크.
이중 디코딩 문제 (The Double-Decode Problem)
MCP 도구의 결과는 예상과는 다릅니다. tools/call로부터 오는 응답 엔벨로프(envelope)는 다음과 같은 형태를 띱니다:
{
"result": {
"content": [{"type": "text", "text": "{\"data\": {...}\"}}"]
...
실제 페이로드(payload)는 result.content[0].text
_내부_에 있는 JSON 문자열입니다. 즉, 모든 MCP 호출은 이중 디코딩 (double-decoding)이 필요합니다. 일부 도구는 JSON 뒤에 사람이 읽을 수 있는 [Decisions applied] 블록을 추가하기도 하는데, 이는 단순한 json.loads() 호출을 깨뜨립니다. 우리의 _loads_lenient()는 이 두 가지 상황을 모두 처리합니다:
def _loads_lenient(text: str):
try:
return json.loads(text)
...
호스트 재작성 문제 (The Host-Rewrite Problem)
MCP 도구에 의해 반환되는 모든 webUrl은 MCP 서버 자체의 SIGNOZ_URL 환경 변수를 기반으로 구축됩니다. 이 값의 기본값은 http://signoz-signoz-0:8080으로, 이는 내부 Docker 서비스 이름입니다. 따라서 브라우저에서 클릭할 수 없습니다. 우리는 URL을 사용하기 전에 모든 MCP 결과에 대해 deep_rewrite() 단계를 실행합니다:
INTERNAL_HOST = "signoz-signoz-0:8080"
BROWSER_HOST = os.environ.get("SIGNOZ_BROWSER_HOST", "localhost:8080")
...
알림 웹훅(alert webhook)의 externalURL 필드는 이미 브라우저에서 클릭이 가능하므로 (MCP 서버가 아닌 Alertmanager에서 제공됨), Slack 카드에 포함되는 알림 규칙 딥 링크(deep link)에는 이를 그대로 사용합니다.
Foundry 패치: 의도적인 성형 (Foundry Patches: Deliberate Molding)
Foundry의 강점 중 하나는 생성된 compose 파일을 직접 수정하지 않고도 RFC 6902 JSON 패치(patches)를 적용할 수 있는 능력입니다. 우리는 세 가지 패치를 사용했습니다:
패치 1 — 헬스체크(Healthcheck) 수정
signoz-mcp 이미지는 distroless(shell, wget, curl 없음)입니다. 기본 헬스체크(healthcheck)는 wget --spider /livez입니다. 이는 절대 실행될 수 없으며, 컨테이너가 실제로 트래픽을 처리하고 있음에도 unhealthy로 보고되었습니다. 우리는 이를 패치하여 해결했습니다:
- op: replace
path: /services/signoz-mcp/healthcheck
value:
...
패치 2 — MCP 자체 텔레메트리 (MCP self-telemetry, 세 번째 관측성 계층)
MCP 서버는 기본적으로 "OpenTelemetry export not configured"라는 로그를 남깁니다. 두 개의 환경 변수를 설정하면 이를 활성화할 수 있습니다:
- op: add
path: /services/signoz-mcp/environment/-
value: OTEL_EXPORTER_OTLP_ENDPOINT=http://signoz-ingester-1:4317
...
이 패치를 적용한 후, mcp.tool.calls 및 mcp.tool.call.duration.* 메트릭(metrics)이 gen_ai.tool.name 태그와 함께 SigNoz에 나타나기 시작합니다. 단 네 줄의 추가로 완전한 세 번째 관측성 계층(observability layer)을 구축했습니다.
패치 3 — 알림 지연(Alert latency)
Alertmanager의 기본 group_wait는 30초입니다. Ruler의 평가 창(evaluation window)이 30초인 상황에서, 장애 발생부터 Slack 카드 도착까지 최악의 경우 약 3.5분이 소요됩니다. 우리는 이를 5초로 패치했습니다:
- op: add
path: /services/signoz-alertmanager/environment/-
value: ALERTMANAGER_GROUP_WAIT=5s
이는 3분을 기다려야 하는 데모와, 커피를 따르기도 전에 카드가 도착하는 데모 사이의 차이를 만들어냈습니다.
GenAI 시맨틱 컨벤션 스팬 (GenAI Semantic Convention Spans)
우리가 OpenLLMetry의 자동 계측(auto-instrument) 대신 수동 계측(manual instrumentation)을 선택한 이유는 단 하나입니다: 바로 결정론(determinism) 때문입니다. 자동 계측을 사용하면 정확한 속성(attributes)이 라이브러리 버전에 따라 달라집니다. 수동 스팬(manual spans)을 사용하면 SigNoz에 무엇이 기록되는지 정확히 알 수 있습니다:
with tracer.start_as_current_span("agent_j.synthesize") as span:
span.set_attribute("gen_ai.system", get_gen_ai_system_provider(AGENT_J_MODEL))
span.set_attribute("gen_ai.operation.name", "chat")
...
gen_ai.usage.cost는 API에서 반환되지 않으므로, 우리는 가격표를 통해 이를 계산합니다:
GENAI_PRICING = {
"gemini/gemini-2.5-flash": (0.30, 2.50), # 1M 토큰당 USD (입력, 출력)
"gpt-4o-mini": (0.15, 0.60),
...
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
