프로덕션 요청 트레이싱

프로덕션 요청 트레이싱 (Production Request Tracing)

이 페이지는 SGLang에서 OpenTelemetry Collector 기반의 요청 트레이스 데이터를 내보내는 방법을 설명해요. 서버 실행 시 --enable-trace 플래그와 --otlp-traces-endpoint로 OpenTelemetry Collector 엔드포인트를 구성하면 트레이싱을 활성화할 수 있어요. 요청의 실행 흐름을 시각화하고, 비동기 트레이싱으로 성능 오버헤드를 줄이는 방법까지 다뤄요.

출처: 문서

본문

SGLang은 OpenTelemetry Collector를 기반으로 요청 트레이스 데이터를 내보내요. 서버 실행 시 --enable-trace를 추가하고 --otlp-traces-endpoint로 OpenTelemetry Collector 엔드포인트를 구성하면 트레이싱을 활성화할 수 있어요.

시각화의 예시 스크린샷은 https://github.com/sgl-project/sglang/issues/8965에서 확인할 수 있어요.

설정 가이드 (Setup Guide)

요청 트레이싱을 구성하고 트레이스 데이터를 내보내는 방법을 설명할게요.

  1. 필요한 패키지와 도구 설치

    • Docker와 Docker Compose 설치
    • 의존성 설치
    # enter the SGLang root directory
    pip install -e "python[tracing]"
    
    # or manually install the dependencies using pip
    pip install opentelemetry-sdk opentelemetry-api opentelemetry-exporter-otlp opentelemetry-exporter-otlp-proto-grpc
    
  2. OpenTelemetry collector와 Jaeger 실행

    docker compose -f examples/monitoring/tracing_compose.yaml up -d
    
  3. 트레이싱을 활성화한 SGLang 서버 시작

    # set env variables
    export SGLANG_OTLP_EXPORTER_SCHEDULE_DELAY_MILLIS=500
    export SGLANG_OTLP_EXPORTER_MAX_EXPORT_BATCH_SIZE=64
    # start the prefill and decode server
    python -m sglang.launch_server --enable-trace --otlp-traces-endpoint 0.0.0.0:4317 <other option>
    # start the model-gate-way
    python -m sglang_router.launch_router --enable-trace --otlp-traces-endpoint 0.0.0.0:4317 <other option>
    

    0.0.0.0:4317을 실제 OpenTelemetry collector 엔드포인트로 바꿔 주세요. tracing_compose.yaml로 openTelemetry collector를 실행했다면 기본 수신 포트는 4317이에요.

    HTTP/protobuf span exporter를 사용하려면 다음 환경 변수를 설정하고 HTTP 엔드포인트(예: http://0.0.0.0:4318/v1/traces)를 가리키세요.

    export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
    
  4. 몇 가지 요청 보내기

  5. 트레이스 데이터가 내보내지는지 관찰

    • 웹 브라우저로 Jaeger의 16686 포트에 접속해 요청 트레이스를 시각화해요.
    • OpenTelemetry Collector는 트레이스 데이터를 JSON 형식으로 /tmp/otel_trace.json에도 내보내요. 후속 패치에서 이 데이터를 Perfetto 호환 형식으로 변환하는 도구를 제공하여, Perfetto UI에서 요청을 시각화할 수 있게 할 예정이에요.
  6. 트레이스 레벨 동적 조정 트레이스 레벨은 0부터 3까지 설정 가능한 값을 받아요. 각 트레이스 레벨 값의 의미는 다음과 같아요:

    0: disable tracing
    1: Trace important slices
    2: Trace all slices except nested ones
    3: Trace all slices (default)
    

    시작 시 — 서버 실행 전에 SGLANG_TRACE_LEVEL 설정:

    SGLANG_TRACE_LEVEL=2 python -m sglang.launch_server --enable-trace --otlp-traces-endpoint 0.0.0.0:4317 <other options>
    

    런타임 시 — 재시작 없이 HTTP API로 동적 조정:

    curl http://0.0.0.0:30000/set_trace_level?level=2
    

    0.0.0.0:30000을 실제 서버 주소로, level=2를 설정하고 싶은 레벨로 바꿔 주세요.

    주의: --enable-trace 파라미터를 반드시 설정해야 해요. 그렇지 않으면 트레이스 레벨을 아무리 동적으로 조정해도 트레이스 기능이 활성화되지 않아요.

비동기 트레이싱 (Async Tracing, 성능 오버헤드 줄이기)

배치 크기가 클 때 동기식(synchronous) OTel span 생성은 thread-safe 잠금과 백그라운드 내보내기 스레드 때문에 추론 처리량을 떨어뜨릴 수 있어요. 비동기 트레이싱은 모든 span 생성을 ZMQ를 통한 전용 exporter 프로세스로 옮겨, 스케줄러와 토크나이저 핫 경로에서 OTel 오버헤드를 제거해요.

비동기 트레이싱 활성화:

SGLANG_TRACE_ASYNC=1 python -m sglang.launch_server --enable-trace --otlp-traces-endpoint 0.0.0.0:4317 <other options>

작동 방식:

  • 워커 프로세스(스케줄러, 토크나이저 등)마다 데몬 exporter 프로세스가 시작돼요.
  • 루트 span은 여전히 호출 프로세스에서 생성돼요(요청당 하나, 비용 미미). traceparent를 통한 프로세스 간 span 연결을 보존해요.
  • 스레드 span과 슬라이스 span은 가벼운 operation dict로 버퍼링되고 ZMQ PUSH/PULL을 통해 exporter로 전달돼요.
  • Span ID는 TraceCustomIdGenerator.preset_next_span_id()를 사용해 호출 프로세스에서 미리 생성되므로, 내보내진 span 트리는 동기식 모드와 동일해요.
  • 스레드 정보(스케줄러 라벨, TP/DP/PP 랭크)는 trace_set_thread_info() 콜백으로 한 번 등록돼요.

튜닝:

Environment Variable Description Default
SGLANG_TRACE_ASYNC Enable async tracing false
SGLANG_TRACE_ASYNC_FLUSH_THRESHOLD Max buffered ops before auto-flush 100

관심 있는 슬라이스에 트레이싱을 추가하는 방법 (API 소개)

토크나이저와 스케줄러 메인 스레드에는 이미 계측 지점(instrumentation points)이 삽입되어 있어요. 추가 요청 실행 세그먼트를 추적하거나 더 세밀한 트레이싱을 원한다면, 아래처럼 tracing 패키지의 API를 사용해 주세요.

아래 모든 구현은 python/sglang/srt/observability/req_time_stats.py에서 이루어져요. 다른 슬라이스를 추가하고 싶다면 여기에서 추가해 주세요.

  1. 초기화 트레이싱에 관여하는 모든 프로세스는 초기화 단계에서 다음을 실행해야 해요:

    process_tracing_init(otlp_traces_endpoint, server_name)
    

    otlp_traces_endpoint는 인자에서 얻으며, server_name은 자유롭게 설정할 수 있지만 모든 프로세스에서 일관되어야 해요.

    트레이싱에 관여하는 모든 스레드는 초기화 단계에서 다음을 실행해야 해요:

    trace_set_thread_info("thread label", tp_rank, dp_rank)
    

    "thread label"은 스레드의 이름으로 생각할 수 있으며, 시각화 뷰에서 서로 다른 스레드를 구분하는 데 사용돼요.

  2. 요청의 트레이스 컨텍스트 생성 각 요청은 TraceReqContext()를 호출해 요청 컨텍스트를 초기화해야 하며, 이를 사용해 슬라이스 span을 생성하고 요청 단계 정보를 기록해요. 요청 객체 안에 저장하거나 전역 변수로 유지할 수 있어요.

  3. 요청의 시작과 끝 표시

    trace_ctx.trace_req_start().
    trace_ctx.trace_req_finish()
    

    trace_req_start()와 trace_req_finish()는 동일한 프로세스 안에서 호출되어야 해요. 예를 들어 토크나이저에서요.

  4. 슬라이스 트레이싱 추가

    • 일반적인 슬라이스 트레이싱 추가:
      trace_ctx.trace_slice_start(RequestStage.TOKENIZER.stage_name)
      trace_ctx.trace_slice_end(RequestStage.TOKENIZER.stage_name)
      
      or
      trace_ctx.trace_slice(slice: TraceSliceContext)
      
    • 스레드의 마지막 슬라이스 끝은 thread_finish_flag=True로 표시하거나 명시적으로 trace_ctx.abort()를 호출해야 해요. 그렇지 않으면 해당 스레드의 span이 제대로 생성되지 않아요.
      trace_ctx.slice_end(RequestStage.D.stage_name, thread_finish_flag = True)
      trace_ctx.abort()
      
  5. 요청 실행 흐름이 다른 스레드로 이동할 때 스레드 컨텍스트를 명시적으로 재구축

    • 수신자(receiver): ZMQ로 요청을 받은 후 다음 코드를 실행해요.
      trace_ctx.rebuild_thread_context()
      

복잡한 트레이싱 시나리오를 위한 프레임워크 확장 방법

현재 제공되는 tracing 패키지에는 더 발전할 여지가 있어요. 그 위에 더 고급 기능을 구축하고 싶다면, 먼저 기존 설계 원칙을 이해해야 해요.

트레이싱 프레임워크의 핵심 구현은 span 구조와 트레이스 컨텍스트의 설계에 있어요. 흩어진 슬라이스를 집계하고 여러 요청을 동시에 추적하기 위해 3단계 트레이스 컨텍스트(또는 span) 구조를 설계했어요: TraceReqContext, TraceThreadContext, TraceSliceContext. 이들의 관계는 다음과 같아요:

TraceReqContext (req_id="req-123")
├── TraceThreadContext(thread_label="scheduler", tp_rank=0)
|     └── TraceSliceContext(slice_name="prefill")
|
└── TraceThreadContext(thread_label="scheduler", tp_rank=1)
      └── TraceSliceContext(slice_name="prefill")

각 추적 요청은 전역 TraceReqContext를 유지하고 해당 요청 span을 생성해요. 요청을 처리하는 각 스레드마다 TraceThreadContext를 기록하고 스레드 span을 생성해요. TraceThreadContextTraceReqContext 안에 중첩되며, 현재 추적 중인 각 코드 슬라이스(중첩 가능)는 연관된 TraceThreadContext에 저장돼요.

위 계층 외에도 각 슬라이스는 Span.add_link()로 이전 슬라이스를 기록하는데, 이를 사용해 실행 흐름을 추적할 수 있어요.

더 알아보기