기본 콘텐츠로 건너뛰기

[Opencensus - Trace] Opencensus를 활용하는 Trace 정리

Trace 정리

Tracing (이하 트레이싱)

추적은 응용 프로그램을 구성하는 다른 서비스에 의해 처리되는 단일 사용자 요청의 진행과정을 추적하는 것이다.

진행 과정의 각 작업은 Span 으로 부르며, 각 스팬에는 단계에 소요된 시간 (대기 시간), 상태, 시간 이벤트, 속성, 링크를 포함하여 작업을 나타낼 수 있는 메타 데이터가 포함된다. 이 추적 정보를 활용해서 애플리케이션의 오류 및 대기 시간 문제를 디버깅 할 수 있다.

Trace (이하 추적)

추적은 스팬 트리로 구성된다. 즉, 작업의 흐름 경로를 보여주는 관찰 가능한 신호의 집합이다. 추적 자체는 고유한 16바이트 시퀀스로 표현되는 TraceID로 식별된다. 트레이싱을 통해서 수집된 추적 정보는 다음과 같이 표현된다.

Trace

위의 그림에서와 같이 여러 단계의 작업(스팬)이 호출된 것을 확인할 수 있다.

  • auth: 사용자 인증 여부 검사
  • cache.Get: 캐시 검증
  • mysql.Query: 캐시에 존재하지 않아 DB 조회
  • cache.Put: DB 조회결과 캐시 처리

Exporting (이하 내보내기)

수집된 추적 정보를 다양한 분석을 위해서 여러 가지 백엔드들에 대한 내보내기를 구성해서 처리할 수 있다.
다양한 내보내기 목록등은 Opencensus 를 참고하면 된다.

Span (이하 스팬)

스팬은 추적의 단일 작업을 나타낸다. 주로 HTTP 요청, RPC (원격 프로시저 호출: Remote Procedure Call), 데이터베이스 쿼리 또는 코드가 사용자 코드에서 사용하는 경로 등을 나타낸다.

  • 스팬은 트리 구조를 이루기 때문에 상위 스팬의 존재여부에 따라서 상위 스팬과 하위 스팬으로 나뉜다.
  • 각 스팬은 SpanID로 식별된다.
  • SpanID 와 옵션 바이트들을 합쳐서 Span Context라고 한다.
  • 동일 프로세스 내에서 스팬 컨텍스트는 컨텍스트 객체로 전달된다. 프로세스 경계를 넘어서면 프로토콜 헤더로 직렬화 된다. 따라서 수신 측은 스팬 컨텍스트를 읽고 하위 스팬을 만들 수 있다.

스팬은 다음과 같은 필드 정보로 구성된다.

  • Name

    • 작업을 설명할 수 있는 문자열, 일반적으로 통계적으로 의미가 있을 수 있는 이름을 사용하는 것이 좋다. 백엔드 및 분석 도구가 이 이름을 기준으로 보고서를 생성한다.

    • 스팬 생성자에서 지정한다.

      cox, span := trace.StartSpan(ctx, "cache.Get")
  • SpanID

    • 스팬 식별자
    • 무작위로 생성된 8바이트 구성이며 전역적으로 고유한 값이어야 한다.
    • 하위 스팬이 존재할 경우 ParentSpanID로 지정된다. ParentSpanID 가 null인 경우는 최상위 스팬으로 판단한다.
  • TraceID

    • 추적 식별자
    • 무작위로 생성된 16바이트 구성이며 전역적으로 고유한 값이어야 한다.
    • 모든 프로세스에서 동일한 추적에 속하는 모든 스팬들을 그룹화하는 기준이다.
  • ParentSpanID

    • 현재 작업 단위 스팬의 상위 스팬 여부를 의미한다.
    • null이거나 SpanID 값이 존재할 수 있다.
    • 동일한 ParentSpanID를 가지는 하위 스팬들이 존재할 수 있지만, 하나의 스팬은 하나의 ParentSpanID만 가질 수 있다.
  • StartTime / EndTime

    • StartTime은 작업이 시작된 시간을 기록하는 타임스탬프 값이다.
    • EndTime은 작업이 종료된 시간을 기록하는 타임스탬프 값이다.
    • 처리(지연) 시간은 EndTime과 StartTime 의 차이를 의미한다.
    • 스팬의 수명은 스팬 객체에 시작 시간과 종료 시간을 기록하는 프로세스를 나타낸다.
      • 시작 시간은 스팬이 생성될 떄 기록되며, 시작 시간이 기록된 경우만 살아있다.
      • 종료 시간은 작업의 종료 시간으로 기록하며, 추적이 종료된 후에 스팬을 종료하는 것이 중요하다.
  • Status

    • 특정 시점에 스팬의 필터링 가능한 상태를 나타내는 논리적 모델을 정의한다.
    • int32 코드로 구성된다.
    • 상태 코드 매핑된 정보는 다음과 같다.
    STATE CODE DESCRIPTION HTTP STATUS CODE EQUIVALENT
    OK 0 Not an error, returned on success 200 and 2XX HTTP statuses
    CANCELLED 1 The operation was cancelled, typically by the caller 499
    UNKNOWN 2 An unknown error raised by APIs that don’t return enough error information 500
    INVALID_ARGUMENT 3 The client specified an invalid argument 400
    DEADLINE_EXCEEDED 4 The deadline expired before the operation could succeed 504
    NOT_FOUND 5 Content was not found or request was denied for an entire class of users 404
    ALREADY_EXISTS 6 The entity attempted to be created already exists 409
    PERMISSION_DENIED 7 The caller doesn’t have permission to execute the specified operation 403
    RESOURCE_EXHAUSTED 8 The resource has been exhausted e.g. per-user quota exhausted, file system out of space 429
    FAILED_PRECONDITION 9 The client shouldn’t retry until the system state has been explicitly handled 400
    ABORTED 10 The operation was aborted 409
    OUT_OF_RANGE 11 The operation was attempted past the valid range e.g. seeking past the end of a file 400
    UNIMPLEMENTED 12 The operation is not implemented or is not supported/enabled for this operation 501
    INTERNAL 13 Some invariants expected by the underlying system have been broken. This code is reserved for serious errors 500
    UNAVAILABLE 14 The service is currently available e.g. as a transient condition 503
    DATA_LOSS 15 Unrecoverable data loss or corruption 500
    UNAUTHENTICATED 16 The requester doesn’t have valid authentication credentials for the operation 401
    span.SetStatus(trace.Status{Code: int32(trace.StatusCodeNotFound), Message: "Cache miss"})
  • Time events

    • 일정 기간 동안 특정 시점에 발생한 이벤트를 설명한다. 아래와 같은 필드 중에 하나를 사용할 수 있지만 모두 사용할 수는 없다.

      • 주석 : 일정 기간 동안 발생한 이벤트에 대해 텍스트로 설명하는 스토리지 제공

        • Description: 이벤트를 설명하는 사용자 메시지

        • Attributes: 주석을 표현하는데 필요한 속성들

              import "go.opencensus.io/trace"
              ...
              span.Annotation([]trace.Attribute{
                  trace.StringAttribute("store", "memcache"),
                  trace.BoolAttribute("cache_miss", true),
                  trace.Int64Attribute("age_ns", 13488999),
              }, "Cache miss durtin GC")
      • 메시지 이벤트 : 스팬 간에 주고 받은 메시지 설명

        • 유형 (Type)

          유형 기술
          SEND 1 이 메시지가 전송되었음을 나타냅니다.
          RECEIVE 2 이 메시지가 수신되었음을 나타냅니다.
          UNKNOWN 0 알 수없는 이벤트 유형 또는 기본값
        • 신분증명 (ID) : SEND/RECEIVE 간의 메시지 이벤트 상관 관계를 위한 메시지 ID로 예를 들어 프로토콜 핸드 셰이크 또는 스트리밍 RPC간에 시퀀스/상태 번호를 일치시킬 떄 유용할 수 있다.

        • 압축되지 않은 크기 (Uncompressed Size) : 송/수신 상에 압축되지 않은 바이트 수

        • 압축된 크기 (Compressed Size) : 송/신 상에 압축된 바이트 수, 0 이면 압축되지 않은 바이트 수와 동일한 것으로 간주

          // On the client
          span.AddMessageReceiveEvent(seqNumber, 1024, 512)
          // On the server
          span.AddMessageSendEvent(seqNumber, 1024, 512)
    • 시간 이벤트의 모음 이지만, 삭제 된 주석 수와 삭제된 메시지 이벤트 수에 대한 정보도 관리한다.

      • 시간 이벤트 모음
      • 삭제된 주석 수
      • 삭제된 메시지 이벤트 수
  • Link

    • 동일하거나 다른 추적에 속하는 스팬 간의 상호 관계를 설명한다. 예를 들어 서로 다른 추적 또는 서로 다른 프로세스로 구성된 일괄 작업이 수행된 경우에 한 스팬에서 다른 스팬으로의 링크는 관련 스팬들을 상호 연결하는데 도움이 될 수 있다.

    • 다음과 같은 필드로 구성된다.

      • TraceID
      • SpanID
      • 유형
        • CHILD
        • PARENT
        • UNKNOWN
      • Attributes
      # SpanA : 신뢰할 수 없는 경계 (예, 클라우드에 대한 클라이언트 요청)에서 시작됨
      # SpanB : 신뢰할 수 있는 경계 (예, 서비스/클라우드 프런트엔드 서버)에서 시작됨
      
      _, spanA := trace.StartSpan(context.Background(), "SpanA")
      spanASC := spanA.SpanContext()
      
      _, spanB := trace.StartSpan(context.Background(), "SpanB")
      spanB.AddLink(trace.Link{
        TraceID: spanASC.TraceID,
        SpanID: spanASC.SpanID,
        Type: trace.LinkTypeChild,
        Attributes: map[string]interface{}{
            "reason": "client-RPC unverified source",
        }
      })
  • SpanKind

    • 스팬 간의 상/하위 관계 뿐만 아니라 추가적인 관계를 설명하는데 사용한다.

      유형 의미
      SERVER 1 범위는 RPC의 서버 측 처리를 다룹니다.
      CLIENT 2 범위는 RPC의 클라이언트 측 처리를 포함합니다.
      UNSPECIFIED 0 지정되지 않음
      // Started on the client
      ctx, cSpan := trace.StartSpan(ctx, "SpanStarted", trace.WithSpanKind(trace.SpanKindClient))
      // Received from the server
      ctx, sSpan := trace.StartSpan(ctx, "SpanStarted", trace.WithSpanKind(trace.SpanKindServer))
  • TraceOptions

    TraceOptions는 각 Opencensus 스팬의 바이트로 마지막 비트는 스팬이 샘플링된 경우에 설정된다.

    16 진수 상태 바이너리 상태 의미
    0x00 00000000 스팬이 샘플링되지 않았습니다.
    0x01 00000001 스팬이 샘플링 됨
  • Tracestate

    여러 분산 추적 그래프에서 위치 / 순서에 대한 정보를 설정하기 위한 것으로 최대 32개의 정렬된 목록 사용이 가능한 Key-Value 쌍이다.

    필드 기술 제한
    캐릭터 모음 소문자로 시작해야하며 소문자 영숫자, 대시, 별표 및 슬래시를 포함 &할 수 있습니다.
    캐릭터 모음 인쇄 가능한 ASCII 문자 만

Sampling (이하 샘플링)

견본 추출

추적 데이터는 대량으로 생성되는 경우가 많기 때문에 수집 및 저장 비용뿐만 아니라 전송 비용도 많이 소비된다. 따라서 관찰 가능성과 비용 간의 균형을 맞춰서 추적을 샘플링하는 것이 필요하다. 즉, 전체 범위 중에 추적 대상을 정하는 범위와 내보낼지 여부를 결정하는 프로세스라고 생각하면 된다.

샘플러

  • Always : 모든 샘플링 결정에 true를 반환한다.

     import "go.opencensus.io/traces"
    
     _ = trace.AlwaysSample()
  • Never : 모든 샘플링 결정에 false를 반환한다.

      import "go.opencensus.io/traces"
    
      _ = trace.NeverSample()
  • Probabilistic : 동전 던지기와 같이 샘플링 결정에 true/false를 확률적으로 반환한다. (기본적으로 확률적 샘플링은 10,000분의 1이다)

      import "go.opencensus.io/traces"
    
      theSampler = trace.ProbabilitySampler(1/1000.0)
  • RateLimiting : 초당 0.1개의 추적 샘플링을 시도한다. 만일 상위 스팬이 샘플링되었다면 하위 스팬은 샘플링을 유지한다. 이와 같은 샘플링을 사용하는 이유는 아래와 같은 문제들을 해결하기 위한 용도로 사용한다.

    • QPS 기반 샘플링 얻기

    • 실제 샘플링 확률 제공

    • 최소한의 오버헤드

      // 속도 제한을 달성하기 위해서 마지막 QPS 기반 샘플링 결정을 내린 시간이 원자 변수 (Atomic Variable)에 저장되고 마지막 확률적 결정을 내린 이후의 시간도 저장된다. 그리고 원하는 샘플링 QPS를 얻기 위한 확률 함수를 사용한다.
      
      P(Z) = min(Z * X, 1)
      // X : 원하는 QPS, Z : 마지막 샘플링 이후 경과된 시간 (초)

샘플러의 결정은 샘플러가 각각 true 또는 false를 반환하는 경우에 설정하거나 지워 Span.TraceOptions의 샘플링 비트 에 영향을 주게 된다.

샘플러 설정

  • 글로벌 샘플러 : TraceConfig 설정을 통해서 전역으로 적용되는 샘플러를 말한다.

      package main
    
      import "go.opencensus.io/traces"
    
      func main() {
          // Having already created your sampler "theSampler"
          trace.ApplyConfig(trace.Config{DefaultSampler: theSampler})
      }
  • 스팬별 샘플러 (스팬 범위) : 스팬을 생성할 때 지정해서 스팬에 적용되는 샘플러를 말한다.

      import "go.opencensus.io/traces"
    
      func doWork() {
          // Having already created your sampler "theSampler" and "ctx"
          ctx, span := trace.StartSpan(ctx, "DoWork", trace.WithSampler(theSampler))
      }

규칙

ParentSpan의 샘플링 결정은 추적 구성과 관련 없이 항상 모든 항목에 상속된다. 이를 통해서 추적의 연속성이 보장된다. 예를 들어 부모가 샘플링 되었는데 자식이 샘플링이 되지 않거나, 자식이 샘플링이 되었는데 부모가 샘플링이 되지 않는 경우들이 발생하는 것을 방지하기 위한 것이다.

참고자료

댓글