미니멀 .NET LLM Observability: 15분 안에 타임아웃 재현 및 트라이애지

발행: (2026년 3월 2일 오후 04:30 GMT+9)
13 분 소요
원문: Dev.to

Source: Dev.to

LLM 엔드포인트가 타임아웃되면, 대시보드만으로는 거의 도움이 되지 않습니다. 필요한 것은 증상에서 원인으로 빠르게 연결되는 경로입니다.

이 글에서는 제어된 504 오류를 강제로 발생시키고 metrics → trace → logs 워크플로우를 반복적으로 수행하면서 디버깅할 수 있는 작은 .NET 실험실을 소개합니다. 스택은 ASP.NET Core, Blazor, .NET Aspire, Ollama, OpenTelemetry이며, 목표는 실용적입니다: 배포 전에 진단 시간을 단축하는 것입니다.

핵심 아이디어는 다음과 같습니다: 관측 가능성은 대시보드가 아니라 진단 시간입니다.

저는 로그를 보면서 로그, 트레이스, 메트릭을 신뢰할 수 있게 연관시키는 방법이 없어 이미 너무 많은 시간을 낭비했기 때문에 이 프로젝트를 만들었습니다. 이 글에서 “LLM 워크로드”란 모델 호출과 프롬프트 또는 툴 변경으로 인해 꼬리 지연 시간과 오류가 발생하는 엔드포인트를 의미하며, 단순히 HTTP 핸들러만을 의미하지는 않습니다.

이 글은 레포지토리 우선이며, 동반 레포지토리를 직접 사용합니다:

Repo:

  • 정상, 지연, 타임아웃, 실제 모델 호출 시나리오를 트리거할 수 있는 Blazor UI가 포함되어 있습니다.

The Stack in One Minute

ComponentDescription
ASP.NET Core API노이즈 없이 엔드‑투‑엔드로 계측할 수 있는 작은 요청 표면입니다.
Blazor Web UI원클릭으로 정상, 지연, 타임아웃, 실제 모델 호출 시나리오를 확인할 수 있습니다.
.NET Aspire AppHost로컬 오케스트레이션과 빠른 전환을 위한 Aspire 대시보드를 제공합니다.
Ollama (ollama/ollama:0.16.3)클라우드 토큰 비용 없이 실제 로컬 모델 호출 동작을 제공합니다.
OpenTelemetry로그는 무엇을 알려주고, 트레이스는 어디서를 알려주며, 메트릭은 얼마나 자주를 알려줍니다.

핵심은 간단합니다: 실패를 유발하고 엔드‑투‑엔드로 관찰할 수 있는 하나의 로컬 환경을 제공하여 추측 없이 문제를 파악할 수 있게 하는 것입니다.

Why LLM Timeouts Feel Different

  • 프롬프트 변경은 배포와 같습니다: 코드는 동일할 수 있지만 지연 시간과 실패 모드가 바뀔 수 있습니다.
  • 모델 및 런타임 변경은 꼬리 지연 시간을 바꿀 수 있습니다.
  • 도구나 종속성 호출은 변동성을 확대합니다 — 하나의 느린 호출이 타임아웃이 될 수 있습니다.

최소 상관 관계 필드

신속한 트라이지를 위해, 모든 곳에 몇 가지 필드가 존재하길 원합니다:

필드목적
run_id하나의 요청 라이프사이클을 추적
trace_id스팬 및 서비스 전반에 걸친 실행을 추적
prompt_version프롬프트 변경에 행동을 연결
tool_version통합 변경에 따른 실패를 연결

상관 관계가 어떻게 보여야 하는가

POST /ask → trace_id in the trace span → run_id + trace_id in logs → timeout metric increases

내가 사용하는 명명 규칙

  • snake_case in logs and JSON: run_id, trace_id, prompt_version, tool_version
  • camelCase in C# variables: runId, traceId, promptVersion, toolVersion

예시 로그 라인

timeout during /ask run_id=9f0f2f3a6fdd4f5f9e9a1f4d8f6c6f3e trace_id=4c4f3b2e86d4d6a6b1f69a0d9d0d9f0a prompt_version=v1 tool_version=local-llm-v1

그 체인에서 하나의 링크라도 누락되면, 트라이지가 즉시 느려집니다.

디버깅 흐름이 어떻게 보이는가

실제로는 다음과 같은 절차를 따릅니다:

  1. 웹 UI에서 Simulated Timeout (504) 를 클릭합니다.
  2. Aspire Metrics를 열어 llm_timeouts_total 가 증가했는지 확인합니다.
  3. Traces로 이동하여 실패한 llm.run 을 엽니다.
  4. trace_id 를 복사한 뒤, 로그로 전환하여 trace_id 또는 run_id 로 필터링합니다.
  5. 실패가 특정 prompt_version 혹은 tool_version 과 일치하는지 확인합니다.

이것이 실험실의 핵심 목적입니다: 타임아웃 증상에서 몇 단계의 의도적인 절차를 통해 가능한 원인으로 이동함으로써 추측에 의존하지 않게 합니다.

사전 요구 사항

  • Docker Desktop 또는 Docker Engine이 설치되어 실행 중
  • 저장소의 global.json에 지정된 .NET SDK 버전이 설치됨
  • Aspire 워크로드(설정에 따라 필요할 경우)
dotnet workload install aspire
  • 로컬 포트 사용 가능(또는 실행 설정 조정): 18888, 18889, 11434
  • 안정적인 API 포트 부록을 사용하는 경우 17100 포트도 비워두어야 함

Step 1 — Clone and Run the Repository

git clone https://github.com/ovnecron/minimal-llm-observability.git
cd minimal-llm-observability
dotnet run --project LLMObservabilityLab.AppHost/LLMObservabilityLab.AppHost.csproj

터미널에 출력된 Aspire Dashboard URL을 엽니다. 인증 프롬프트가 표시되면 터미널에 표시된 일회성 URL을 사용하세요.

Fixed local HTTP launch settings

  • Aspire Dashboard: http://localhost:18888
  • OTLP endpoint (Aspire Dashboard): http://localhost:18889
  • Web UI (LLMObservabilityLab.Web): Aspire Dashboard 리소스 목록에서 엽니다

ASPIRE_ALLOW_UNSECURED_TRANSPORT=true 로 설정된 AppHost 실행 프로파일에 이미 보안되지 않은 로컬 전송이 활성화되어 있습니다.

로컬에서 11434 포트로 Ollama를 이미 실행 중이라면 중지하거나 AppHost에서 컨테이너 포트 매핑을 변경하세요.

Real Ollama Call 이 “model not found” 를 반환하면, 실행 중인 컨테이너에서 기본 모델을 가져옵니다:

docker exec -it "$(docker ps --filter "name=local-llm" --format "{{.Names}}" | head -n 1)" \
  ollama pull llama3.2:1b

Source:

단계 2 — 웹 UI에서 시나리오 트리거

  1. Aspire Dashboard → Resources → web-ui 엔드포인트 클릭

  2. LLMObservabilityLab.Web의 루트 페이지에서 원클릭 액션을 제공합니다:

    • Healthy Run
    • Simulate Delay
    • Real Ollama Call
    • Simulated Timeout (504)

    각 실행에서는 다음을 표시합니다:

    • run_id
    • trace_id
    • 상태
    • 경과 시간
  3. 웹 UI에는 고정된 15분 트리아지 체크리스트가 포함된 /drill도 있습니다.

3단계 — 정상 기준 생성 (선택 사항)

Web UI에서 Healthy Run을 약 20번 클릭합니다. 이렇게 하면 다음 메트릭에 대한 빠른 기준을 얻을 수 있습니다:

  • llm_runs_total
  • llm_success_total

그런 다음 이후의 실패 실행을 이 기준과 비교할 수 있습니다.

Step 4 — 타임아웃 강제 및 트리아지

  1. 웹 UI에서 Simulated Timeout (504) 를 클릭합니다.
  2. 즉시 Aspire Dashboard 를 엽니다.

버튼이 제어된 504 를 반환하므로 관측 파이프라인을 필요할 때마다 테스트할 수 있습니다.

나의 트리아지 루프 (목표: ~15 분)

PhaseAction
SpotMetrics 에서 llm_timeouts_total 확인
Drill실패한 llm.run 트레이스 열기
Pivottrace_idrun_id 로 로그 필터링
Inspectprompt_versiontool_version 비교
Mitigate가장 작은 안전한 수정부터 적용
Verify타임아웃 시나리오를 다시 실행하고 복구 확인

따라 하기 쉬운 흐름

  • Metrics → 스파이크가 있는지 llm_latency_ms 확인
  • Tracesscenario=simulate_timeout 로 필터링 → 실패한 llm.run 열기

빠른 결정을 위해 사용하는 최소 신호

이 저장소에서 직접 제공됨

  • llm_runs_total
  • llm_success_total
  • llm_timeouts_total
  • llm_errors_total
  • llm_latency_ms

파생 메트릭

task_success_rate = llm_success_total / llm_runs_total * 100

시작 알림 휴리스틱

(이것들은 시드이며 — 기준에 맞게 조정하세요.)

  • task_success_rate 가 30 분 안에 > 5 pp 감소
  • llm_latency_ms 로부터 파생된 지연 시간 백분위가 기준 대비 > 30 % 상승
  • tool_version 로 태그된 실행에 대한 도구‑버전‑범위 성공률이 < 90 % 하락

문제 해결

증상해결 방법
포트 11434가 이미 사용 중로컬 Ollama 인스턴스를 중지하거나 AppHost 포트 매핑을 변경하십시오
추적 또는 메트릭이 없음Aspire 대시보드가 실행 중인지, OTLP 엔드포인트에 접근할 수 있는지 확인하십시오
모델을 찾을 수 없음컨테이너 내부에서 ollama pull … 명령을 실행하십시오
CLI 또는 API 호출 실패Aspire 대시보드(llm‑api → Endpoints)에서 정확한 API 엔드포인트를 복사하십시오

검증된 내용 vs. 의견

Observability advice often mixes hard facts with personal workflow.
관측 가능성 조언은 종종 확실한 사실과 개인적인 워크플로우를 섞어 놓습니다.

검증됨 (이 저장소에서 재현 가능)

  • 시나리오(정상, 지연, 타임아웃, 실제 호출)는 웹 UI에서 트리거됩니다.
  • 상관 관계 체인이 존재합니다: 메트릭 카운터 → llm.run 트레이스 → run_idtrace_id가 포함된 로그.

의견 (제 경우에 작동하지만 필요에 따라 조정하세요)

  • “15‑분” 목표 루프.
  • 위의 알림 임계값(시작용 시드이며 보편적인 진리는 아닙니다).
  • 정확히 네 개의 상관 필드(시스템에 필요하면 더 추가하세요).

최종 생각

목표는 완벽한 대시보드가 아니라 time‑to‑diagnosis를 단축하는 것입니다.
시간 초과에서 정확한 트레이스와 로그 라인으로 전환할 수 없다면, 여전히 추측하고 있는 것입니다.

저는 이 실험실을 사용해 저에게 맞는 워크플로우를 찾았으며, 여러분도 여러분에게 맞는 관측 파이프라인을 구축하는 데 도움이 되길 바랍니다.

문제가 발생하면 GitHub 이슈를 열어 주세요. 기꺼이 도와드리겠습니다.

참고 문헌

0 조회
Back to Blog

관련 글

더 보기 »

일이 정신 건강 위험이 될 때

markdown !Ravi Mishrahttps://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fu...