
AI 에이전트가 왜 느린지 확인하려고 SigNoz를 설치했다가, 사용자에 대해 무엇을 저장하고 있는지 발견했다
요약
AI 에이전트의 성능 분석을 위해 SigNoz와 OpenTelemetry를 활용하여 관측 가능성(Observability)을 구현한 사례를 다룹니다. 분산 트레이싱을 통해 에이전트의 실행 단계를 시각화하는 과정에서 의도치 않게 사용자 입력 데이터가 평문으로 저장되는 보안 문제를 발견했습니다.
핵심 포인트
- OpenTelemetry를 활용한 AI 에이전트의 분산 트레이싱 구현
- SigNoz의 폭포수 뷰(Waterfall view)를 통한 단계별 지연 시간 분석
- 에이전트 실행 로그 내 사용자 입력 데이터의 평문 저장 위험성 확인
- SigNoz 설치 및 Python 환경 설정 시 발생할 수 있는 실무적 이슈
저는 개인정보 보호 문제를 찾고 있었던 것이 아닙니다. 그저 제 AI 에이전트가 실제로 얼마나 느린지 확인하고 싶었을 뿐입니다.
"Agents of SigNoz" 해커톤을 위해 저는 작은 도구 사용 에이전트(tool-using agent)를 만들었고, 이를 OpenTelemetry를 통해 SigNoz에 연결한 뒤, 에이전트가 실행될 때마다 무엇이 기록되는지 살펴보기 시작했습니다. 그 과정 중간에, 저는 사용자가 제 에이전트에 입력한 정확한 텍스트가 평문(plaintext) 상태로 영원히 남아 있는 데이터베이스 행(row)을 멍하니 바라보게 되었습니다. 이 포스트는 제가 무엇을 만들었는지, 무엇이 저를 당황하게 했는지, 그리고 그 행을 본 후 제가 무엇을 했는지에 대한 기록입니다.
배경
저는 데브옵스(DevOps) 전문가는 아닙니다. 인턴십 기간 동안 저는 공장 모니터링 대시보드를 구축했으며, MQTT 센서 데이터를 Node-RED로 보내고 이를 다시 Grafana로 연결하여 기계의 온도와 상태 코드를 관찰했습니다. 그래서 메트릭(metrics) 대시보드가 어떻게 생겼는지는 이미 알고 있었습니다. 하지만 분산 트레이싱(distributed tracing)이 무엇인지, 혹은 LLM 호출을 수행하는 AI 에이전트와 같이 센서가 아닌 대상에게 "관측 가능성 (observability)"이 무엇을 의미하는지는 알지 못했습니다.
저는 스스로 한 가지 규칙을 세웠습니다. 실제로 실행해보지 않은 것에 대해서는 쓰지 않겠다는 것입니다. 따라서 아래의 모든 내용은 제가 직접 실행한 것입니다.
에이전트는 작습니다. OpenAI 기반이며 네 가지 도구를 사용합니다: 날씨 조회, 계산기, 의도적으로 2.5초간 대기하는 가짜 "웹 검색", 그리고 의도적으로 에러를 발생시키는 가짜 "API 호출"입니다. 저는 OpenTelemetry(traces, logs, metrics)를 사용하여 수동으로 계측(instrumented)을 수행했고, Docker로 로컬에 직접 호스팅한 SigNoz 인스턴스를 가리키도록 설정했습니다.
실행하기
먼저 알아두어야 할 점이 하나 있습니다. SigNoz는 최근 기존의 docker-compose 설치 방식 대신 Foundry(foundryctl)라는 새로운 CLI를 도입했습니다. 오래된 튜토리얼을 찾으신다면 오늘날 실제로 접하게 될 내용과 일치하지 않을 것이며, 저 또한 이 때문에 혼란을 겪었습니다. 저는 ClickHouse 25.12.5.44를 실행 중인 SigNoz v0.133.0 버전을 사용하고 있었습니다.
SigNoz에 도달하기도 전에 마주한 유일한 진짜 걸림돌은 Python이었습니다. Homebrew의 python@3.14 빌드는 작동하는 ssl 모듈 없이 배포되기 때문에, pip가 PyPI에 접속조차 할 수 없었습니다 ("Can't connect to HTTPS URL because the SSL module is not available"). 저는 이를 포기하고 대신 시스템 Python (3.9.6)을 사용하여 가상 환경 (virtualenv)을 구축했고, 그렇게 하니 바로 작동했습니다. 사소한 문제였지만, 실제로 앉아서 작업을 진행해 봐야만 맞닥뜨릴 수 있는 종류의 일이었습니다.
"느림"에 대한 내 생각을 바꾼 것
트레이스 (traces)가 흐르기 시작하자, 저는 처음으로 제 에이전트 실행 중 하나에 대한 폭포수 뷰 (waterfall view)를 열었습니다. 그 뷰는 단일 요청을 가져와서 각 단계가 소요된 시간에 따라 왼쪽에서 오른쪽으로 나열하며, 해당 요청이 거친 모든 단계를 세분화하여 보여줍니다. 저에게는 새로운 경험이었습니다. 기존의 Grafana 대시보드는 수치가 임계값 (threshold)을 넘었다는 것만 알려주었습니다. 하지만 이 뷰는 어느 요청의 정확히 어떤 단계가 그 원인인지를 알려주었습니다.
그리고 제가 예상하지 못했던 것을 보여주었습니다. 일반적인 실행에서 제가 작성한 도구 코드 (계산기, 날씨 조회 등)는 0.1밀리초 (millisecond) 미만으로 실행되었습니다. 모든 OpenAI 호출은 1200에서 3700밀리초 사이의 시간이 걸렸습니다. AI 에이전트에게 모델 호출은 기본적으로 곧 지연 시간 (latency)이며, 제 코드는 그에 비하면 거의 비용이 들지 않는 수준입니다. 저는 스스로 그 수치를 찾아내려 하지 않았을 것입니다. 폭포수 뷰가 그저 제 눈앞에 가져다 놓았을 뿐입니다.
[
의도적으로 고장 내기
각각의 상황이 SigNoz에서 어떻게 나타나는지 확인하기 위해, 저는 에이전트를 두 가지 다른 방식으로 고장 내 보았습니다.
첫째는 잘못된 API 키입니다. 그것은 전체 요청을 중단(crash)시켰고, 실패한 LLM 호출과 부모 트레이스(parent trace) 모두 에러(errored)로 표시되어 돌아왔습니다. 둘째로, 저는 하나의 도구(tool)가 내부적으로 예외(exception)를 발생시키도록 만들었지만, 에이전트가 복구하여 계속 진행할 수 있도록 제 코드에서 이를 포착(catch)했습니다. 그 경우에는 해당 도구의 스팬(span)만 빨간색으로 표시되었습니다. 전체 실행은 실패로 표시되지 않았는데, 실제로 실패하지 않았기 때문입니다. 에이전트가 이를 처리했고 사용자에게 무언가 잘못되었다고 알려주었습니다. 따라서 트레이스의 형태만 보고도 텍스트를 한 줄도 읽기 전에 제가 어떤 종류의 실패를 보고 있는지 알 수 있었습니다.
이 두 번째 실패 상황은 제가 실제 알림(alert)을 테스트하는 데에도 사용했습니다. Slack 웹훅(webhook)을 설정하여 SigNoz에 알림 채널(notification channel)로 추가했고, "롤링 5분 윈도우(rolling 5-minute window) 내에서 에러 스팬이 0개보다 많은 경우"에 대한 트레이스 기반 알림(trace-based alert)을 구축했습니다. 그런 다음 의도적으로 고장 난 도구를 연속으로 6번 실행했고, 알림이 '발생(Firing)' 상태로 전환되는 것을 지켜보았습니다. 몇 초 후 Slack에 실제 메시지가 도착했습니다. 저는 단순히 설정만 해두고 작동할 것이라고 믿지 않았습니다. 강제로 작동하게 만들었습니다.


대시보드 쪽은 더 엉망이었고, 솔직히 말해서 그 부분이 제가 가장 쓰고 싶었던 내용입니다. 도구 이름별로 그룹화한 P90 지연 시간(latency) 차트인 첫 번째 패널은 UI에서 일반적인 "something went wrong" 오류와 함께 실패했습니다. 포기하는 대신 SigNoz 자체의 백엔드 컨테이너 로그를 확인했고 진짜 원인을 찾아냈습니다. 쿼리 빌더(query builder)가 histogramQuantile이라는 이름의 함수를 호출하는 ClickHouse SQL 쿼리를 생성했는데, 이는 ClickHouse 함수가 아니라 PromQL 함수였습니다. 해당 엔진에는 존재하지 않는 함수입니다. 이것은 제가 잘못 사용한 것이 아니라, 이 버전에서 발생하는 실제적이고 재현 가능한 버그입니다. 집계(aggregation) 방식을 백분위수(percentile)에서 일반 평균(average)으로 변경하여 문제를 우회했습니다.
그다음에는 두 번째로 더 어처구니없는 문제에 부딪혔습니다. ClickHouse는 타임스탬프(timestamp)를 UTC로 저장하는데 저는 8시간 차이가 나는 현지 시간과 대조하여 확인하고 있었고, 설상가상으로 메트릭(metrics)을 추가한 이후에 도구 중 하나를 다시 실행하지 않았기 때문에 차트에 "데이터 없음(No Data)"이라고 표시되었습니다. 이 두 가지를 모두 해결하자 차트는 정말 만족스러웠습니다. 느린 도구에 대해 2.6초의 날카로운 스파이크(spike)가 하나 나타났고, 나머지 모든 것은 0에 가깝게 평탄했습니다. 워터폴(waterfall) 차트가 보여주었던 것과 동일한 통찰을 이제 시계열 차트(time-series chart)로 보게 된 것입니다. 그곳에 도달하기까지 두 번의 실제 버그가 필요했을 뿐입니다.
실제로 발견한 것
여기가 제가 예상하지 못했던 부분입니다.
저는 제 에이전트의 실행 중 하나에 대해 SigNoz가 실제로 무엇을 저장했는지 쿼리했습니다. 스팬(spans), 타이밍(timings), 혹은 약간의 메타데이터(metadata) 정도를 예상했습니다. 하지만 대신에 사용자의 요청 전문과 모델의 전체 응답이 트레이스(trace) 데이터 내에 평문(plaintext)으로 그대로 저장되어 있는 것을 발견했습니다:
gen_ai.prompt.0.content: "/orders API 엔드포인트를 호출하여 실시간 데이터를 가져오세요."
gen_ai.completion.0.content: "현재 /orders API 엔드포인트를 사용할 수 없는 것 같아 필요한 실시간 데이터를 가져올 수 없었습니다. 만약 다른 것이 있다면..."
이것은 정확히 SigNoz의 문제는 아닙니다. SigNoz는 자신이 받는 스팬(span)을 그대로 저장할 뿐입니다. 제가 사용하던 계측 라이브러리(opentelemetry-instrumentation-openai, OpenLLMetry 생태계의 일부)가 해당 콘텐츠를 캡처하기로 결정한 것입니다. 하지만 결과는 어느 쪽이든 같습니다. 실제 제품에서 이는 모든 사용자의 프롬프트와 그들이 그 안에 넣은 모든 내용이 기본적으로 관측 가능성 백엔드(observability backend)에 저장되고 있으며, 아무도 이것이 일어나고 있다는 경고를 해주지 않는다는 뜻입니다.
저는 단순히 그렇게 주장하고 넘어갈 수 없어 두 가지 방식으로 확인해 보았습니다.
첫째, 실제로 설치된 패키지의 소스 코드를 열어 스위치를 찾았는데, should_send_prompts()라는 함수가 있었습니다. 이 함수는 환경 변수 TRACELOOP_TRACE_CONTENT에 의해 제어되는데, 사용자가 설정하지 않으면 기본값이 `
하지만 공정하게 말하자면, 이 모든 데이터가 실제로 저장되는 ClickHouse와 Postgres는 이 설정에서 내 호스트 머신으로 노출된 포트가 전혀 없었으며, 기본적으로 올바르게 잠겨 있었습니다. 반면 텔레메트리 (telemetry)를 수신하는 OTLP 포트는 정반대로, 인증 없이 개방되어 있었습니다. 이 중 어느 것도 패닉에 빠질 이유는 아닙니다. 둘 다 이 시스템을 실제 환경에 적용하기 전에 알아두어야 할 사항일 뿐입니다.
처음부터 다시 시작한다면 나 자신에게 해줄 말
나에게 가장 큰 변화는 SigNoz의 기능이 아니라, 사고 모델 (mental model)이었습니다. 물리적 센서를 위해 구축된 대시보드(dashboards)를 다뤄왔기에 메트릭 (metrics)은 이미 이해하고 있었습니다. 하지만 트레이싱 (tracing)은 이해하지 못했고, 트레이싱은 센서가 아닌 다른 모든 것에 대해 "왜 이것이 느렸는가"에 대한 답을 실제로 제공하는 것이었습니다.
두 번째는 교훈이라기보다 경고에 가깝습니다. 단순히 디버깅을 위해 AI 기능에 관측성 (observability)을 덧붙이려 한다면, 여러분의 계측 (instrumentation)이 기본적으로 무엇을 캡처하는지 확인하십시오. "관측성"과 "사용자가 입력한 내용의 영구적인 기록"은 조용히 같은 것이 될 수 있으며, 그 무엇도 여러분에게 경고를 해주지 않을 것입니다.
그리고 두 가지 작은 실무적 조언을 드리자면: "데이터 없음"이 무언가 고장 났음을 의미한다고 가정하기 전에 타임존 (timezones)을 확인하고, UI 에러가 여러분의 잘못이라고 가정하기 전에 실제 백엔드 로그 (backend logs)를 확인하십시오.
현재 나의 상태
저는 제가 실제로 실행해 본 것들에 대해서만 작성했으므로, 그 범위가 무엇인지 명확히 하겠습니다: 트레이스 (traces), 로그 (logs), 메트릭 (metrics), 작동하는 알림 (alert), 대시보드 패널 (dashboard panel), 그리고 제가 직접 확인한 수정 사항이 포함된 하나의 실제 테스트된 개인정보 보호 동작입니다. 저는 이 중 어떤 것에 대해서도 부하 테스트 (load-test)를 수행하지 않았으며, SigNoz Cloud를 사용해 보지도 않았습니다. 따라서 이것을 보편적인 주장이 아니라, 저의 단일 셀프 호스팅 (self-hosted) 설정에서 일어난 일로 받아들여 주시기 바랍니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기

