마이크로서비스 애플리케이션의 분산 추적

Last reviewed 2024-06-26 UTC

이 문서는 마이크로서비스를 설계, 빌드, 배포하는 방법을 다룬 4부로 구성된 시리즈 중 네 번째 문서입니다. 이 시리즈에서는 마이크로서비스 아키텍처의 다양한 요소를 설명하며, 마이크로서비스 아키텍처 패턴의 이점과 단점 및 적용 방법이 포함되어 있습니다.

  1. 마이크로서비스 소개
  2. 모놀리식을 마이크로서비스로 리팩터링
  3. 마이크로서비스 설정의 서비스 간 통신
  4. 마이크로서비스 애플리케이션의 분산 추적(이 문서)

이 시리즈는 모놀리식 애플리케이션을 마이크로서비스 애플리케이션으로 리팩터링하는 마이그레이션을 설계 및 구현하는 애플리케이션 개발자 및 설계자를 대상으로 합니다.

분산형 시스템에서는 요청이 한 서비스에서 다른 서비스로 어떻게 이동하고, 각 서비스에서 태스크를 수행하는 데 얼마나 걸리는지 아는 것이 중요합니다. 이전 문서인 모놀리식을 마이크로서비스로 리팩터링에서 배포한 마이크로서비스 기반의 Online Boutique 애플리케이션을 가정해보세요. 이 애플리케이션은 여러 서비스로 구성됩니다. 예를 들어 다음 스크린샷은 프런트엔드, 권장사항, 광고 서비스에서 정보를 가져오는 제품 세부정보 페이지를 보여줍니다.

제품 세부정보 페이지.

제품 세부정보 페이지를 렌더링하기 위해 프런트엔드 서비스는 다음 다이어그램과 같이 추천 서비스 및 광고 서비스와 통신합니다.

프런트엔드 서비스는 추천 서비스, 제품 카탈로그, 광고 서비스와 통신합니다.

그림 1. 다양한 언어로 작성된 서비스

그림 1에서 프런트엔드 서비스는 Go로 작성되었습니다. Python으로 작성된 추천 서비스는 gRPC를 사용하여 프런트엔드 서비스와 통신합니다. Java로 작성된 광고 서비스도 gRPC를 사용하여 프런트엔드 서비스와 통신합니다. gRPC 외에도 REST HTTP를 통한 서비스 간 통신 방법도 있습니다.

이러한 분산 시스템을 빌드할 때는 관측 가능성 도구를 사용하여 다음 통계를 제공합니다.

  • 요청을 수행한 서비스
  • 요청이 느린 경우 지연이 발생한 위치
  • 요청이 실패한 경우 오류가 발생한 위치
  • 요청 실행이 시스템의 일반 동작과 다른 점
  • 요청 실행의 차이가 성능과 관련이 있는지 여부(일부 서비스 호출에 평소보다 오래 걸렸는지 여부)

목표

  • kustomize 매니페스트 파일을 사용하여 인프라를 설정합니다.
  • Online Boutique 예시 애플리케이션을 Google Kubernetes Engine(GKE)에 배포합니다.
  • Cloud Trace를 사용하여 예시 애플리케이션의 사용자 경험을 검토합니다.

비용

이 문서에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud 구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.

이 문서를 마치면 만든 리소스를 삭제하여 비용이 계속 청구되지 않도록 할 수 있습니다. 자세한 내용은 삭제를 참조하세요.

시작하기 전에

이 시리즈의 이전 문서인 마이크로서비스 설정의 서비스 간 통신을 완료하여 프로젝트를 이미 설정한 경우 해당 프로젝트를 다시 사용할 수 있습니다. 다음 단계에 따라 추가 API를 사용 설정하고 환경 변수를 설정합니다.

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  2. Make sure that billing is enabled for your Google Cloud project.

  3. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

  4. Compute Engine, GKE, Cloud SQL, Artifact Analysis, Trace, Container Registry에 API를 사용 설정합니다.

     gcloud services enable \
       compute.googleapis.com \
       sql-component.googleapis.com \
       servicenetworking.googleapis.com\
       container.googleapis.com \
       containeranalysis.googleapis.com \
       containerregistry.googleapis.com \
       sqladmin.googleapis.com
    

분산 추적

분산 추적은 상황별 메타데이터를 각 요청에 연결하고 메타데이터가 요청 간에 공유되는지 확인합니다. trace 포인트를 사용하여 분산 추적을 구현합니다. 예를 들어 각각 요청을 전송하고 응답을 수신하기 위해 2개의 trace 포인트를 사용해서 제품 세부정보를 보기 위한 클라이언트 요청을 처리하도록 서비스(프런트엔드, 추천, 광고)를 구현할 수 있습니다. 다음 다이어그램은 이러한 trace 포인트 구현의 작동 방법을 보여줍니다.

두 개의 trace 포인트가 있는 trace 포인트 계측

그림 2. 각 서비스 간 호출에는 요청-응답 쌍으로 구성되는 2개의 trace 포인트가 포함됩니다.

trace 포인트가 서비스 호출 시 실행할 요청을 이해할 수 있도록 시작 서비스는 실행 흐름에 따라 trace ID를 전달합니다. trace ID를 전달하는 프로세스를 메타데이터 전파 또는 분산 컨텍스트 전파라고 부릅니다. 컨텍스트 전파는 분산 애플리케이션의 서비스가 지정된 요청을 실행하는 동안 서로 통신할 때 네트워크 호출로 메타데이터를 전송합니다. 다음 다이어그램은 메타데이터 전파를 보여줍니다.

메타데이터 전파가 trace ID를 전달합니다.

그림 3. 서비스 간에 trace 메타데이터가 전달됩니다. 메타데이터에는 서비스 호출 및 해당 타임스페이스와 같은 정보가 포함됩니다.

Online Boutique 예시에서 trace는 사용자가 제품 세부정보를 가져오기 위해 초기 요청을 전송할 때 시작됩니다. 새 trace ID가 생성되고 원래 요청에 대해 상황별 메타데이터를 포함하는 헤더로 각 연속 요청이 데코레이션됩니다.

최종 사용자의 요청을 이행하는 동안 호출되는 각각의 개별 작업을 스팬이라고 부릅니다. 시작 서비스는 각 스팬에 고유 ID 및 상위 스팬의 trace ID를 태그로 지정합니다. 다음 다이어그램은 trace의 Gantt 차트 시각화를 보여줍니다.

개별 작업은 스팬으로 태그 지정됩니다.

그림 4. 상위 스팬에는 하위 스팬의 응답 시간이 포함됩니다.

그림 4는 프런트엔드 서비스가 추천 서비스 및 광고 서비스를 호출하는 trace 트리를 보여줍니다. 프런트엔드 서비스는 최종 사용자에게서 관측되는 대로 응답 시간을 기술하는 상위 스팬입니다. 하위 스팬은 응답 시간 정보를 포함하여 추천 서비스 및 광고 서비스가 호출되고 응답되는 방법을 기술합니다.

Istio와 같은 서비스 메시는 전용 계측 장비 없이 서비스 간 트래픽의 분산 추적을 사용 설정합니다. 하지만 trace를 더 자세히 제어하거나 서비스 메시 내에서 실행되지 않는 코드를 trace해야 할 경우가 있습니다.

이 문서에서는 OpenTelemetry를 사용하여 trace 및 측정항목 수집을 위해 분산 마이크로서비스 애플리케이션의 계측을 사용 설정합니다. OpenTelemetry를 사용하면 측정항목 및 trace를 수집하고 Prometheus, Cloud Monitoring, Datadog, Graphite, Zipkin, Jaeger와 같은 백엔드로 내보낼 수 있습니다.

OpenTelemetry를 사용한 계측

다음 섹션에서는 컨텍스트 전파를 사용하여 여러 요청의 스팬을 단일 상위 trace에 추가하도록 허용하는 방법을 보여줍니다.

이 예시에서는 OpenTelemetry JavaScript, Python, Go 라이브러리를 사용하여 결제, 추천, 프런트엔드 서비스에 대한 trace 구현을 계측합니다. 계측의 세부정보 수준에 따라서는 데이터 추적이 프로젝트 비용(Cloud Trace 청구)에 영향을 줄 수 있습니다. 비용 문제를 완화하기 위해 대부분의 trace 시스템은 여러 양식의 샘플링을 사용하여 관측된 trace의 특정 일부만 캡처합니다. 프로덕션 환경에서는 조직에 샘플링하려는 대상과 그 이유가 있을 수 있습니다. 비용 관리, 관심 있는 trace에 집중, 노이즈 필터링을 기준으로 샘플링 전략을 맞춤설정할 수 있습니다. 샘플링에 대한 자세한 내용은 OpenTelemetry 샘플링을 참조하세요.

이 문서에서는 Trace를 사용하여 분산 trace를 시각화합니다. OpenTelemetry 내보내기 도구를 사용하여 trace를 Trace로 보냅니다.

trace 내보내기 등록

이 섹션에서는 마이크로서비스 코드에 줄을 추가하여 각 서비스에서 trace 내보내기 도구를 등록하는 방법을 보여줍니다.

Go로 작성된 프런트엔드 서비스의 경우 다음 코드 샘플에서 내보내기 도구를 등록합니다.

[...]
exporter, err := otlptracegrpc.New(
        ctx,
        otlptracegrpc.WithGRPCConn(svc.collectorConn))
    if err != nil {
        log.Warnf("warn: Failed to create trace exporter: %v", err)
    }
tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithSampler(sdktrace.AlwaysSample()))
    otel.SetTracerProvider(tp)

추천 서비스(Python으로 작성)의 경우 다음 코드 샘플에서 내보내기 도구를 등록합니다.

if os.environ["ENABLE_TRACING"] == "1":
    trace.set_tracer_provider(TracerProvider())
    otel_endpoint = os.getenv("COLLECTOR_SERVICE_ADDR", "localhost:4317")
    trace.get_tracer_provider().add_span_processor(
        BatchSpanProcessor(
            OTLPSpanExporter(
            endpoint = otel_endpoint,
            insecure = True
            )
        )
    )

결제 서비스(JavaScript로 작성)의 경우 다음 코드 샘플에서 내보내기 도구를 등록합니다.

provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter({url: collectorUrl})));
provider.register();

컨텍스트 전파 설정

trace 시스템은 서비스 간 trace 컨텍스트 전파를 위해 형식을 정의하는 trace 컨텍스트 사양을 따라야 합니다. 전파 형식 예시에는 Zipkin B3 형식X-Google-Cloud-Trace가 있습니다.

OpenTelemetry는 전역 TextMapPropagator를 사용하여 컨텍스트를 전파합니다. 이 예시에서는 W3C traceparent 형식을 사용하는 Trace Context 전파를 사용합니다. OpenTelemetry의 HTTP 및 gRPC 라이브러리와 같은 계측 라이브러리는 전역 전파를 사용하여 trace 컨텍스트를 HTTP 또는 gRPC 요청에 메타데이터로 추가합니다. 컨텍스트 전파가 성공하려면 클라이언트와 서버가 동일한 전파 형식을 사용해야 합니다.

HTTP를 통한 컨텍스트 전파

프런트엔드 서비스가 HTTP 요청 헤더에 trace 컨텍스트를 삽입합니다. 백엔드 서비스는 trace 컨텍스트를 추출합니다. 다음 코드 샘플은 trace 컨텍스트를 구성하도록 프런트엔드 서비스를 계측하는 방법을 보여줍니다.

otel.SetTextMapPropagator(
    propagation.NewCompositeTextMapPropagator(
        propagation.TraceContext{}, propagation.Baggage{}))

if os.Getenv("ENABLE_TRACING") == "1" {
    log.Info("Tracing enabled.")
    initTracing(log, ctx, svc)
} else {
    log.Info("Tracing disabled.")
}

...

var handler http.Handler = r
handler = &logHandler{log: log, next: handler}     // add logging
handler = ensureSessionID(handler)                 // add session ID
handler = otelhttp.NewHandler(handler, "frontend") // add OpenTelemetry tracing

gRPC를 통한 컨텍스트 전파

사용자가 선택하는 제품에 따라 결제 서비스가 주문을 실행하는 흐름을 가정해보세요. 이러한 서비스는 gRPC를 통해 통신합니다.

다음 코드 샘플에서는 송신 호출을 가로채고 trace 컨텍스트를 삽입하는 gRPC 호출 인터셉터를 사용합니다.

var srv *grpc.Server

// Propagate trace context always
otel.SetTextMapPropagator(
    propagation.NewCompositeTextMapPropagator(
        propagation.TraceContext{}, propagation.Baggage{}))
srv = grpc.NewServer(
    grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
    grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor()),
)

요청을 수신하면 결제 또는 제품 카탈로그 서비스(ListProducts)가 요청 헤더에서 컨텍스트를 추출하고 상위 trace 메타데이터를 사용하여 하위 스팬을 생성합니다.

다음 섹션에서는 예시 Online Boutique 애플리케이션에 대해 분산 추적을 설정 및 검토하는 방법을 자세히 설명합니다.

애플리케이션 배포

이 시리즈의 이전 문서인 마이크로서비스 설정의 서비스 간 통신을 완료하면서 실행 중인 애플리케이션이 있는 경우 다음 섹션인 trace 검토로 건너뛸 수 있습니다. 그렇지 않은 경우 다음 단계를 완료하여 Online Boutique 예시를 배포합니다.

  1. 인프라 설정을 위해 Cloud Shell에서 GitHub 저장소를 클론합니다.

    git clone https://github.com/GoogleCloudPlatform/microservices-demo.git
    
  2. 새 배포의 경우 환경 변수를 재설정합니다.

    PROJECT_ID=PROJECT_ID
    REGION=us-central1
    GSA_NAME=microservices-sa
    GSA_EMAIL=$GSA_NAME@$PROJECT_ID.iam.gserviceaccount.com
    

    PROJECT_ID를 사용하려는 Google Cloud 프로젝트 ID로 바꿉니다.

  3. 선택사항: 새 클러스터를 만들거나 기존 클러스터가 있으면 다시 사용합니다.

    gcloud container clusters create-auto online-boutique --project=${PROJECT_ID}
      --region=${REGION}
    
  4. Google 서비스 계정을 만듭니다.

    gcloud iam service-accounts create $GSA_NAME \
      --project=$PROJECT_ID
    
  5. API를 사용 설정합니다.

    gcloud services enable \
    monitoring.googleapis.com \
    cloudtrace.googleapis.com \
    cloudprofiler.googleapis.com \
      --project ${PROJECT_ID}
    
  6. Cloud Trace에 필요한 역할을 서비스 계정에 부여합니다.

    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/cloudtrace.agent
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/monitoring.metricWriter
    
    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member "serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role roles/cloudprofiler.agent
    
    gcloud iam service-accounts add-iam-policy-binding ${GSA_EMAIL} \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/default]"
    
  7. Identity and Access Management(IAM) 서비스 계정을 사용하도록 Kubernetes 서비스 계정(기본 네임스페이스의 경우 default/default)에 주석을 추가합니다.

    kubectl annotate serviceaccount default \
        iam.gke.io/gcp-service-account=${GSA_EMAIL}
    
  8. GKE 구성에 Google Cloud Observability를 사용 설정하여 추적을 사용 설정합니다.

    cd ~/microservices-demo/kustomize && \
    kustomize edit add component components/google-cloud-operations
    

    위 명령어는 다음과 유사한 kustomize/kustomization.yaml 파일을 업데이트합니다.

    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    resources:
    - base
    components:
    - components/google-cloud-operations
    [...]
    
  9. 마이크로서비스를 배포합니다.

    kubectl apply -k .
    
  10. 배포 상태를 확인합니다.

    kubectl rollout status deployment/frontend
    kubectl rollout status deployment/paymentservice
    kubectl rollout status deployment/recommendationservice
    kubectl rollout status deployment/adservice
    

    각 명령어의 출력은 다음과 같이 표시됩니다.

    Waiting for deployment "" rollout to finish: 0 of 1 updated replicas are available...
    deployment "" successfully rolled out
    
  11. 배포된 애플리케이션의 IP 주소를 가져옵니다.

    kubectl get service frontend-external | awk '{print $4}'
    

    부하 분산기의 IP 주소가 게시될 때까지 기다립니다. 명령어를 종료하려면 Ctrl+C를 누르세요. 부하 분산기 IP 주소를 확인한 후 URL http://IP_ADDRESS에서 애플리케이션에 액세스합니다. 부하 분산기가 정상 상태가 되고 트래픽 전달이 시작되는 데 다소 시간이 걸릴 수 있습니다.

Cloud Trace를 사용하여 트레이스 검토

Online Boutique 애플리케이션에서 사용자의 구매 경험은 다음 흐름을 갖습니다.

  • 방문 페이지에 제품 카탈로그가 표시됩니다.
  • 구매를 위해 사용자가 구입을 클릭합니다.
  • 사용자가 장바구니에 추가하는 항목의 세부정보 페이지로 리디렉션됩니다.
  • 사용자가 주문을 완료하기 위해 결제할 수 있는 결제 페이지로 리디렉션됩니다.

제품 세부정보 페이지를 로드할 때 높은 응답 시간 문제를 해결해야 하는 시나리오를 가정해보세요. 앞에서 설명한 것처럼 제품 세부정보 페이지는 여러 마이크로서비스로 구성됩니다. 높은 지연 시간이 발생하는 위치 및 이유를 확인하기 위해서는 분산 추적 그래프를 보고 여러 서비스에서 전체 요청의 성능을 검토할 수 있습니다.

분산 추적 그래프를 검토하려면 다음을 수행합니다.

  1. 애플리케이션에 액세스한 후 아무 제품을 클릭합니다. 제품 세부정보 페이지가 표시됩니다.
  2. Google Cloud 콘솔에서 Trace 목록 페이지로 이동하여 타임라인을 검토합니다.
  3. 분산 추적 결과를 보려면 URI 열에서 프런트엔드를 클릭합니다.
  4. Trace 폭포식 구조 뷰에는 URI와 연결된 스팬이 표시됩니다.

    Trace 폭포식 뷰에는 스팬이 표시됩니다.

    앞의 스크린샷에서 제품의 trace에는 다음 스팬이 포함됩니다.

    • 프런트엔드 스팬이 제품 세부정보 페이지를 로드할 때 클라이언트에서 관측되는 엔드 투 엔드 지연 시간(150.349ms)을 캡처합니다.
    • 권장사항 서비스 스팬은 제품과 관련된 권장사항을 가져올 때 백엔드 호출의 지연 시간(4.246ms)을 캡처합니다.
    • 광고 서비스 스팬은 제품 페이지와 관련된 광고를 가져올 때 백엔드 호출의 지연 시간(4.511ms)을 캡처합니다.

높은 응답 시간 문제를 해결하기 위해 서비스 종속 항목이 해당 서비스 수준 목표(SLO)를 충족하지 않을 때 모든 이상점 요청의 지연 시간 분포 그래프가 포함된 통계를 검토할 수 있습니다. Cloud Trace를 사용하여 성능 통계를 가져오고 샘플링된 데이터로부터 분석 보고서를 생성할 수도 있습니다.

문제 해결

애플리케이션 성능 관리에서 trace가 표시되지 않으면 로그 탐색기에서 권한 거부 오류가 있는지 확인합니다. 서비스 계정에 trace를 내보낼 수 있는 액세스 권한이 없으면 권한 거부가 발생합니다. Cloud Trace에 필요한 역할 부여 단계를 검토하고 서비스 계정에 올바른 네임스페이스를 주석으로 추가합니다. 그런 다음 opentelemetrycollector를 다시 시작합니다.

  kubectl rollout restart deployment opentelemetrycollector

삭제

이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.

프로젝트 삭제

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

리소스 삭제

이 문서에서 사용한 Google Cloud 프로젝트를 유지하려면 개별 리소스를 삭제합니다.

  • Cloud Shell에서 리소스를 삭제합니다.

    gcloud container clusters delete online-boutique --project=${PROJECT_ID} --region=${REGION}
    

다음 단계