
Claude Code OTel 파이프라인을 Splunk Observability Cloud에 연결해 보았다 (Splunk 입문 포함)
요약
Claude Code의 AI 에이전트 텔레메트리를 Splunk Observability Cloud로 전송하는 OTel 파이프라인 구축 방법을 다룹니다. 기존 로컬 저장 방식에 Splunk를 추가하여 클라우드 기반의 가관측성(Observability)을 확보하는 과정을 설명합니다.
핵심 포인트
- Claude Code의 OTel 데이터를 Splunk Observability Cloud로 전송하는 파이프라인 구현
- Splunk Observability Cloud Free Edition의 특징 및 활용법 안내
- Protobuf 엔드포인트, 인증 토큰, Metric Finder 등 구현 시 직면한 기술적 난관 공유
- OpenTelemetry(OTLP)를 활용한 AI 에이전트 모니터링 사례 제시
-
전작(Claude Code의 AI 에이전트 텔레메트리(Telemetry)를 OpenTelemetry로 자체 측정하는 파이프라인)에, 새로운 전송 대상으로 Splunk Observability Cloud를 병행 추가했다
-
기존의 로컬 JSON Lines / SQLite 저장 방식은 그대로 유지하며, 교체가 아닌 추가라는 방침을 세웠다
-
Splunk Observability Cloud Free Edition은 15호스트까지 영구적으로 무료이며, 신용카드 등록이 필요 없고 기능 제한이 없다
-
구현 자체는 작지만, protobuf가 필수인 엔드포인트, 성공 시의 "침묵", 인증 토큰 비교, Metric Finder 사용법에서 4가지 난관이 있었다
-
Splunk라는 회사나 제품명을 들어본 적이 없거나 자세히 모르는 기술자
-
Claude Code와 같은 AI 에이전트의 텔레메트리를 OpenTelemetry로 이미 수집하고 있거나, 앞으로 수집하고 싶은 분
-
OTLP의 기초는 알지만, 외부 가관측성(Observability) 서비스에 실제로 연결한 사례를 알고 싶은 분
-
전작(Claude Code OTel 파이프라인)을 읽지 않았더라도 대략적인 내용은 이해할 수 있도록 작성했지만, 구현의 전제 조건은 해당 글에 의존한다
Splunk라는 이름을 처음 듣는 분들을 위해, 먼저 위치를 정리해 두고자 한다.
Splunk Inc.는 서버나 애플리케이션, 네트워크 장비 등이 생성하는 "machine data"를 검색·분석하는 소프트웨어 기업으로 알려져 왔다. 대표 제품인 Splunk Enterprise는 독자적인 쿼리 언어인 SPL(Search Processing Language)을 사용하여 대량의 로그를 횡단 검색 및 시각화하는, 로그 관리 / SIEM(Security Information and Event Management, 보안 정보 이벤트 관리) 분야의 노포 제품이다.
Splunk는 2024년 3월 18일에 Cisco Systems에 의한 인수가 완료되었으며, 현재는 Cisco의 자회사가 되었다(인수 금액은 약 280억 달러).
이번에 연결한 Splunk Observability Cloud는 기존의 Splunk Enterprise와는 다른, 비교적 새로운 제품 라인이다. 로그 검색을 중심으로 발전해 온 Splunk Enterprise와 달리, Splunk Observability Cloud는 클라우드 네이티브 애플리케이션의 운영 모니터링에 중점을 둔다. 메트릭(Metrics)·트레이스(Trace)·로그(Log)를 조합하여 시스템 내부에서 어떤 일이 일어나고 있는지를 관측하기 위한 제품군이라는 위치를 갖는다. 기술적으로는 Splunk가 2019년에 인수한 SignalFx의 기술이 베이스가 되어 있다.
중요한 점은 Splunk Observability Cloud가 OpenTelemetry(OTLP)를 네이티브로 지원한다는 것이다. OTel Collector에서 직접 데이터를 보낼 수 있다. 전송 대상은 "realm"이라 불리는 지역 단위의 엔드포인트(us0, eu0, jp0 등)로 관리되며, 가입 시 호스팅 지역을 선택한다.
참고로 Splunk에는 "Observability for AI"라는 AI 에이전트 모니터링에 특화된 별도의 기능도 있다. 이 기능의 제공 범위와 이용 조건은 변할 가능성이 있으므로 단정은 피하겠지만, 이번에 시도한 것은 이 기능이 아니라 일반적인 Splunk Observability Cloud로 범용적인 OTLP 메트릭을 전송한 것뿐이다. AI 에이전트 전용 기능에 대한 검증은 다른 기회에 다루고 싶다.
전작에서는 Claude Code의 AI 에이전트 텔레메트리를 OpenTelemetry로 자체 측정하여 로컬에 축적하는 최소 구성을 만들었다.
흐름은 Claude Code에서 OTLP로 보내진 데이터를 OTel Collector가 수신하여, JSON Lines로서 파일로 출력하고, SQLite로 나중에 집계할 수 있도록 하는 것이었다. 이 글에서는 OTel Collector의 기본 설정은 반복하지 않고, Splunk로의 연결 부분에 집중하여 설명한다.
이번에 하고 싶었던 것은 이 로컬 중심의 메커니즘을 교체하는 것이 아니다. 로컬의 JSON Lines와 SQLite를 남겨둔 채, 외부의 관측 기반에도 동일한 메트릭을 보내는 것이다.
Claude Code (OTLP exporter)
↓
OTel Collector (otelcol-contrib v0.156.0)
...
Splunk 측에서 제대로 표시되지 않는 경우라도, 원본 데이터는 로컬에 남는 구성으로 만들었다.
직접 수집하고 있는 텔레메트리 (Telemetry)를 외부 옵저버빌리티 (Observability) 제품에도 연결하여 어떻게 보이는지 확인하고 싶었다. 가능하다면 현재의 OpenTelemetry 구성을 크게 변경하지 않고 시도하고 싶었다. Splunk Observability Cloud는 OTLP를 직접 수용하기 때문에 이 조건에 부합했다. Collector 측에서 새로운 exporter를 추가하고, 기존의 metrics 파이프라인에서 해당 exporter를 호출하기만 하면 된다.
또 다른 이유는 Free Edition의 존재다. 2026년 7월 시점에 Splunk 공식 사이트에 게재된 조건은 다음과 같다.
- 15호스트까지 영구 무료 (기간 한정 트라이얼이 아님)
- 신용카드 등록 불필요
- 기능 제한 없음 (모든 기능 이용 가능)
- 등록 시 비즈니스 이메일 주소, 성명, 기업 정보 필요
단기간만 만져볼 수 있는 시험판이 아니라, 소규모 검증 환경이라면 지속적으로 사용할 수 있다. 이번 사례처럼 우선 소량의 텔레메트리를 보내고, Collector와의 연결이나 UI상의 가시성을 확인하는 용도로는 진입 장벽이 낮았다. 물론 "무료니까"라는 이유만으로 운영 환경의 모니터링 기반을 결정할 수는 없다. 전송량이나 운영 체제, 보존 요건 등은 실제 운영에 가까워질수록 별도로 검토가 필요하다.
Splunk Observability Cloud만 보고 결정하는 것은 공정하지 않으므로, 대표적인 경쟁 서비스의 무료 범위를 나란히 비교해 둔다 (모두 2026년 7월 기준, 공식 정보 기반).
| 서비스 | 무료 범위 내용 | 신용카드 | OTLP 네이티브 대응 |
|---|---|---|---|
| Splunk Observability Cloud | 15호스트까지 영구 무료, 기능 제한 없음 | 불필요 | ○ |
| ... |
이렇게 나열해 보면 "호스트 수", "데이터 양", "메트릭 시리즈 (Metrics Series) 수" 등 무료 범위의 구분 방식이 서비스마다 다르다는 것을 알 수 있다. 이번에는 이미 OTel Collector가 동작하고 있다는 전제하에, otlp_http exporter를 하나 추가하는 것만으로 시도할 수 있는 간편함과 호스트 수 기준으로 기한을 신경 쓰지 않고 사용할 수 있다는 점을 우선하여 Splunk를 선택했다. 다른 서비스들도 마찬가지로 OTLP를 수용하는 것이 많기 때문에, Collector 설정을 교체하는 것만으로도 전환이나 비교 검증이 가능할 것이다.
이번에 사용한 것은 otelcol-contrib v0.156.0에 포함된 otlp_http exporter다.
여기서 주의해야 할 점은 컴포넌트 이름이다. 오래된 설정 예제에서는 otlphttp (언더스코어 없음)라는 표기를 종종 볼 수 있지만, 실제로 설치한 바이너리에서 유효했던 이름은 otlp_http였다. 문서만 보고 판단하지 말고, 다음 명령어로 수중에 있는 바이너리가 인식하는 이름을 확인해 두는 것이 확실하다.
otelcol-contrib components
exporters:
file:
path: data/otel_raw/events.jsonl
...
기존의 file exporter는 삭제하지 않고, metrics 파이프라인의 exporters에 otlp_http를 추가했을 뿐이다. processors에 전작에 이어 transform (계정 식별자(account identity)를 삭제하는 redaction 처리)을 반드시 남겨두어야 한다. 이를 제외하면 로컬 파일뿐만 아니라 Splunk 측에도 가공되지 않은 식별자가 그대로 전송되어 버린다. 이렇게 하면 Collector가 수신한 메트릭은 로컬 파일과 Splunk Observability Cloud 양쪽으로 전송된다.
metrics_endpoint에는 계약 시 선택한 realm에 대응하는 URL을 지정한다. 인증은 HTTP 헤더의 X-SF-Token에 액세스 토큰 (Access Token)을 설정하는 방식으로, 관리 화면에서 발행할 수 있다.
realm과 액세스 토큰은 다른 API 키와 마찬가지로 .env 파일에 저장하고, 설정 파일에서는 환경 변수 참조를 통해 읽어온다.
SPLUNK_REALM=<realm>
SPLUNK_ACCESS_TOKEN=<access-token>
실제 값은 리포지토리에 커밋하지 않는다. 기사, 로그, 설정 예시에도 노출하지 않는다.
수동 실행이라면 source .env를 한 뒤 Collector를 기동하면 환경 변수가 전달된다. 하지만 systemd 서비스로 상주시키고 있는 경우, 그것만으로는 부족하다. 쉘에서 source .env
결과를 얻더라도, 이는 해당 쉘과 그로부터 기동된 자식 프로세스에만 전달될 뿐, systemd가 기동하는 서비스 프로세스에는 자동으로 전파되지 않는다.
서비스 정의에 EnvironmentFile=을 명시할 필요가 있다.
[Service]
EnvironmentFile=/path/to/.env
이것이 없으면, Collector 설정에 ${env:SPLUNK_REALM}이라고 작성되어 있어도 서비스 측에서는 값을 해결(resolve)할 수 없다. "내 로컬에서 직접 기동했을 때는 작동하는데, 상주 서비스에서는 작동하지 않는다"와 같은 종류의 문제로 이어진다. 설정 변경 후에는 잊지 말고 daemon-reload와 서비스 재시작을 수행한다.
sudo systemctl daemon-reload
sudo systemctl restart <collector-service>
연결 확인 과정에서 4가지 장벽에 부딪혔다. 모두 구현 자체보다는 검증 방법이나 방식의 차이에서 기인한 것이었다.
Collector 설정에 확신이 서지 않아, 동작 확인을 목적으로 Splunk 엔드포인트로 curl을 이용해 데이터를 직접 보내 보았다. 보낸 데이터는 평소 Collector의 수신 포트에 투입하는 것과 동일한 형식인, 가공되지 않은 JSON 형태의 OTLP 페이로드였다.
결과는 HTTP 415, Unsupported Media Type이었다. 응답 본문에도 content-type=application/json에 대응하는 디코더(decoder)가 없어, 이 요청 형식을 지원하지 않는다는 취지의 메시지가 포함되어 있었다.
원인: 이번 검증에서 이 application/json 요청은 415 에러로 거부되었다 (OTLP/HTTP 사양상 protobuf와 JSON 양쪽의 인코딩이 정의되어 있으므로, 이는 "JSON 전반이 미지원"이라기보다 이번에 구성한 JSON 요청 형식의 문제였을 가능성이 있다). 반면, Collector의 otlp_http exporter는 표준으로 protobuf를 사용하여 전송하기 때문에, Collector를 경유하는 본래의 파이프라인에서는 문제가 되지 않았다.
대책: 이 415 에러는 "Splunk로 보낼 수 없다"는 증거가 아니었다. 동작 확인을 위해 수동으로 구성한 curl 요청이 실제 exporter와는 다른 형식이었습니다. OTLP 연결 확인 시, URL과 JSON 본문만 흉내 낸 curl 요청을 Collector exporter의 대체 테스트라고 생각하지 않는 것이 좋다. 실제로 사용할 exporter를 경유시킨 상태에서 테스트하는 방침으로 전환했다.
Collector를 통해 올바른 액세스 토큰(access token)을 사용하여 합성 데이터(synthetic data)를 전송했다. 그러자 로컬 Collector 로그에는 에러나 재시도(retry)가 전혀 나타나지 않았다.
이는 좋은 징조임에 틀림없지만, 성공을 나타내는 명확한 로그도 없다. 그 때문에 다음 두 가지를 구분할 수 없었다.
- 정상적으로 전송되어 Splunk 측에서 수락됨
- 설정이 어딘가 잘못되었으나 조용히 실패함
Collector의 로그는 성공한 전송을 매번 눈에 띄게 표시하지 않을 때가 있다. 실패 시에는 에러가 발생하는 반면, 정상적인 전송은 아무것도 표시되지 않아 로그상으로는 "침묵" 상태가 된다. 통상적인 운영에서는 로그 양을 늘리지 않는 합리적인 동작이지만, 첫 연결 확인 시에는 판단 근거가 적어 곤란했다.
두 번째 장벽에 대한 불안을 해소하기 위해, 동일한 Collector 설정과 동일한 테스트 데이터를 사용하되 액세스 토큰만 의도적으로 잘못된 값으로 변경하여 다시 전송해 보았다.
그러자 이번에는 Collector 로그에 HTTP Status Code 401, Unauthenticated라는 에러가 즉시 나타났다.
잘못된 토큰 → HTTP 401 (Unauthenticated)
올바른 토큰 → 에러 없음 (침묵)
이 대비를 통해 검증 대상 경로가 실제로 사용되고 있다는 점, 그리고 적어도 인증 에러는 이 exporter가 제대로 로그에 남기는 구현이라는 점에 대한 확신이 높아졌다. 모든 실패 패턴이 반드시 로그에 남는 것도, 데이터가 완전히 수락되었음을 증명하는 것도 아니지만 (그것은 후술할 네 번째 장벽에서 별도로 확인한다), "올바른 토큰에서 에러가 발생하지 않는다"는 것은 "실제로 수락되었다"는 점에 대한 나름대로 강력한 간접 증거가 되었다.
정상적인 케이스(Normal case)만으로 판단하지 않고, 의도적인 이상 케이스(Abnormal case)를 비교 대상으로 사용하면 확신도가 높아진다. 연결 테스트에서는 성공 조건뿐만 아니라 "기대한 대로 실패하는 조건"을 하나 만들어 두는 것이 좋다. 설정값을 변경했을 때 관측 결과도 변한다면, 적어도 검증 대상 경로가 실제로 사용되고 있음을 확인할 수 있다. 참고로, 테스트에서 사용하는 잘못된 값에 실제 토큰과 유사한 정보를 사용할 필요는 없다. 테스트 후에는 올바른 설정으로 되돌리고 서비스를 재시동한다.
로그상의 확인만으로는 완전한 확증이 되지 않기 때문에, 마지막으로 Splunk Observability Cloud의 Web UI를 열어 실제로 전송한 테스트 데이터를 육안으로 확인하고자 했다.
테스트 데이터에는 식별하기 쉬운 session.id를 붙였다 (한 가지 덧붙이자면, 이러한 고유하고 무제한적인 값을 메트릭의 dimension에 붙이는 것은 일회성인 짧은 테스트라면 문제없지만, 운영 메트릭(Production metrics)에서 상시화하면 메트릭의 시계열(Series) 수가 과도하게 증가하는 원인이 된다. 어디까지나 테스트용 기법으로 취급하고 싶다). 그래서 화면 상단에 있는 일반적인 검색 박스에 그 값을 직접 입력했다. 결과는 No Result였다.
Collector 측에서는 올바른 토큰이라면 에러가 없고, 잘못된 토큰이라면 401이 발생한다. 전송은 이루어지고 있는 것 같은데, UI에서는 아무것도 발견되지 않는다.
원인: 사용 중인 검색 화면을 잘못 선택했다. Splunk Observability Cloud에는 일반적인 검색 박스와 별개로, 메트릭과 그 dimension(속성)을 찾기 위한 "Metric Finder"라는 전용 화면이 있다. session.id는 메트릭 이름이 아니라, 전송한 메트릭에 부수되는 dimension 중 하나였다. 그 값만 일반 검색에 입력해서는 의도한 검색이 되지 않았다.
대책: 내가 성공했던 절차는 다음과 같다. Metric Finder를 열고, 먼저 전송한 메트릭 이름을 검색한다. 대상 메트릭을 찾은 후, 해당 dimension에서 session.id를 선택하고 테스트 시 설정한 값으로 필터링한다. 이로써 전송한 데이터를 UI상에서도 확인할 수 있었다 (Metric Finder에는 dimension 값으로 직접 검색하는 방법도 별도로 있을지 모른다. 메트릭 이름부터 찾아가는 것은 어디까지나 내가 실제로 확인한 절차다).
No Result를 보았을 때는 데이터가 도착하지 않았다고 결론 내리기 전에, 현재 검색 화면이 무엇을 대상으로 하고 있는지 확인하는 것이 좋다. 메트릭은 메트릭 이름과 dimension의 조합으로 찾을 때가 있다.
기존 Claude Code 텔레메트리(Telemetry) 기반에 Splunk Observability Cloud를 전송 대상으로 추가했다.
변경의 본질은 작다. OTel Collector의 metrics 파이프라인에 otlp_http exporter를 추가했을 뿐이며, 기존의 JSON Lines와 SQLite를 이용한 로컬 저장은 변경하지 않았다.
반면, 통신 확인 과정에서는 몇 가지 혼란이 있었다. Splunk의 OTLP 엔드포인트로 생(raw) JSON을 curl로 보내면, 이번 검증에서는 HTTP 415가 발생했다 (Collector를 경유하면 표준으로 protobuf를 전송하기 때문에 문제가 없다). 정상 전송 시에는 로그가 침묵하므로 처음 접했을 때는 성공 여부를 판단하기 어렵다. 잘못된 토큰에 의한 401과 비교하면 정상 시의 판단 근거가 늘어난다. UI에서는 일반 검색이 아니라 Metric Finder에서 메트릭 이름 $\rightarrow$ dimension 순으로 찾아야 했다.
가장 큰 배움은 "에러가 없다"와 "목표한 데이터를 확인했다" 사이에는 거리가 있다는 점이었다. 전송 처리 로그만 보는 것이 아니라, 의도적인 실패 테스트를 거치고, 마지막에는 수신 측 UI에서 대상 메트릭과 dimension을 확인한다. 거기까지 도달해야 비로소 파이프라인 전체가 연결되었다고 말할 수 있다.
Splunk를 전혀 모르는 상태라도 OpenTelemetry 파이프라인이 이미 구축되어 있다면, 연결 자체는 거창하지 않았다. Free Edition으로 작게 시작할 수 있으므로, 우선 기존의 로컬 저장을 유지한 채 두 번째 전송 대상으로 시도해 보는 것이 다루기 쉽다고 생각한다.
참고:
- Zenn 기고 콘테스트 「OpenTelemetry 지식을 글로 쓰기」(Splunk × OpenTelemetry, 2026년)
- Splunk (최상위 페이지)
- Splunk Observability Cloud Free Edition
- Cisco, Splunk 인수 완료
- OTLP/HTTP exporter | Splunk Observability Cloud 문서
- Metric finder and metadata catalogue | Splunk Observability Cloud 문서
- OTLP 사양 | OpenTelemetry
- AI를 위한 Splunk Observability 소개
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기