분산 trace를 배포하여 마이크로서비스 지연 시간 관찰

Last reviewed 2023-08-11 UTC

이 문서에서는 분산 추적을 사용하여 마이크로서비스 지연 시간 관찰에 설명된 참조 아키텍처를 배포하는 방법을 보여줍니다. 이 문서에 설명된 배포는 OpenTelemetry 및 Cloud Trace를 사용하여 마이크로서비스 애플리케이션의 trace 정보를 캡처합니다.

이 배포의 샘플 애플리케이션은 Go로 작성된 마이크로서비스 두 개로 구성됩니다.

이 문서에서는 사용자가 다음에 익숙하다고 가정합니다.

목표

  • GKE 클러스터를 만들고 샘플 애플리케이션을 배포합니다.
  • OpenTelemetry 계측 코드를 검토합니다.
  • 계측에서 생성된 trace 및 로그를 검토합니다.

아키텍처

다음 다이어그램은 배포할 아키텍처를 보여줍니다.

2개의 GKE 클러스터로 구성된 배포 아키텍처

완전 관리형 지속적 통합, 전송, 배포 플랫폼인 Cloud Build를 사용하여 샘플 코드에서 컨테이너 이미지를 만들고 Artifact Registry에 저장합니다. GKE 클러스터는 배포 시 Artifact Registry에서 이미지를 가져옵니다.

프런트엔드 서비스는 / URL에서 HTTP 요청을 수락하고 백엔드 서비스를 호출합니다. 백엔드 서비스의 주소는 환경 변수로 정의됩니다.

백엔드 서비스는 /URL에서 HTTP 요청을 수락하고 환경 변수에 정의된 외부 URL로 아웃바운드 호출을 수행합니다. 외부 호출이 완료되면 백엔드 서비스가 호출자에게 HTTP 상태 호출(예: 200)을 반환합니다.

비용

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

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

이 문서에 설명된 태스크를 완료했으면 만든 리소스를 삭제하여 청구가 계속되는 것을 방지할 수 있습니다. 자세한 내용은 삭제를 참조하세요.

시작하기 전에

  1. Google Cloud 계정에 로그인합니다. Google Cloud를 처음 사용하는 경우 계정을 만들고 Google 제품의 실제 성능을 평가해 보세요. 신규 고객에게는 워크로드를 실행, 테스트, 배포하는 데 사용할 수 있는 $300의 무료 크레딧이 제공됩니다.
  2. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.

    프로젝트 선택기로 이동

  3. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  4. API GKE, Cloud Trace, Cloud Build, Cloud Storage, and Artifact Registry 사용 설정

    API 사용 설정

  5. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.

    프로젝트 선택기로 이동

  6. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  7. API GKE, Cloud Trace, Cloud Build, Cloud Storage, and Artifact Registry 사용 설정

    API 사용 설정

환경 설정하기

이 섹션에서는 배포 전반에서 사용하는 도구로 환경을 설정합니다. 이 배포의 모든 터미널 명령어는 Cloud Shell에서 실행해야 합니다.

  1. Google Cloud 콘솔에서 Cloud Shell을 활성화합니다.

    Cloud Shell 활성화

    Google Cloud 콘솔 하단에서 Cloud Shell 세션이 시작되고 명령줄 프롬프트가 표시됩니다. Cloud Shell은 Google Cloud CLI가 사전 설치된 셸 환경으로, 현재 프로젝트의 값이 이미 설정되어 있습니다. 세션이 초기화되는 데 몇 초 정도 걸릴 수 있습니다.

  2. 환경 변수를 Google Cloud 프로젝트의 ID로 설정합니다.
    export PROJECT_ID=$(gcloud config list --format 'value(core.project)' 2>/dev/null)
    
  3. 연결된 Git 저장소를 클론하여 이 배포에 필요한 파일을 다운로드합니다.
    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples.git
    cd kubernetes-engine-samples/observability/distributed-tracing
    WORKDIR=$(pwd)
    

    저장소 폴더를 이 배포와 관련된 모든 태스크를 수행하는 작업 디렉터리($WORKDIR)로 만듭니다. 이렇게 하면 리소스를 유지하지 않는 경우 배포를 완료했을 때 폴더를 삭제하면 됩니다.

도구 설치

  1. Cloud Shell에서 kubectxkubens를 설치합니다.

    git clone https://github.com/ahmetb/kubectx $WORKDIR/kubectx
    export PATH=$PATH:$WORKDIR/kubectx
    

    이 두 도구를 사용하여 여러 Kubernetes 클러스터, 컨텍스트, 네임스페이스 관련 작업을 수행할 수 있습니다.

  2. Cloud Shell에서 오픈소스 부하 생성 도구인 Apache Bench를 설치합니다.

    sudo apt-get install apache2-utils
    

Docker 저장소 만들기

이 배포의 샘플 이미지를 저장할 Docker 저장소를 만듭니다.

콘솔

  1. Google Cloud 콘솔에서 저장소 페이지를 엽니다.

    저장소 페이지 열기

  2. 저장소 만들기를 클릭합니다.

  3. distributed-tracing-docker-repo를 저장소 이름으로 지정합니다.

  4. 형식은 Docker를 선택하고 모드는 표준을 선택합니다.

  5. 위치 유형에서 리전을 선택한 후 us-west1 위치를 선택합니다.

  6. 만들기를 클릭합니다.

저장소가 저장소 목록에 추가됩니다.

gcloud

  1. Cloud Shell에서 us-west1 위치에 docker repository이라는 설명과 함께 distributed-tracing-docker-repo라는 새 Docker 저장소를 만듭니다.

    gcloud artifacts repositories create distributed-tracing-docker-repo --repository-format=docker \
    --location=us-west1 --description="Docker repository for distributed tracing deployment"
    
  2. 저장소가 만들어졌는지 확인합니다.

    gcloud artifacts repositories list
    

GKE 클러스터 만들기

이 섹션에서는 샘플 애플리케이션을 배포하는 2개의 GKE 클러스터를 만듭니다. GKE 클러스터는 기본적으로 Cloud Trace API에 대해 쓰기 전용 액세스 권한으로 생성됩니다. 따라서 클러스터를 만들 때 액세스 권한을 정의할 필요가 없습니다.

  1. Cloud Shell에서 클러스터를 만듭니다.

    gcloud container clusters create backend-cluster \
        --zone=us-west1-a \
        --verbosity=none --async
    
    gcloud container clusters create frontend-cluster \
        --zone=us-west1-a \
        --verbosity=none
    

    이 예시에서 클러스터는 us-west1-a 영역에 위치합니다. 자세한 내용은 위치 및 리전을 참조하세요.

  2. 클러스터 사용자 인증 정보를 가져와 로컬에 저장합니다.

    gcloud container clusters get-credentials backend-cluster --zone=us-west1-a
    gcloud container clusters get-credentials frontend-cluster --zone=us-west1-a
    
  3. 나중에 컨텍스트에서 쉽게 액세스할 수 있도록 클러스터 컨텍스트 이름을 바꿉니다.

    kubectx backend=gke_${PROJECT_ID}_us-west1-a_backend-cluster
    kubectx frontend=gke_${PROJECT_ID}_us-west1-a_frontend-cluster
    

OpenTelemetry 계측 검토

다음 섹션에서는 샘플 애플리케이션의 main.go 파일에서 코드를 검토합니다. 이렇게 하면 컨텍스트 전파를 사용하여 여러 요청의 스팬을 단일 상위 trace에 추가하도록 허용하는 방법을 알아보는 데 도움이 됩니다.

애플리케이션 코드의 가져오기 검토

import (
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
	"github.com/gorilla/mux"
	"go.opentelemetry.io/contrib/detectors/gcp"
	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
	"go.opentelemetry.io/contrib/propagators/autoprop"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/sdk/resource"
	"go.opentelemetry.io/otel/sdk/trace"
)

가져오기와 관련하여 다음 사항에 유의하세요.

  • go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp 패키지에는 HTTP 서버 또는 HTTP 클라이언트를 계측할 수 있는 otelhttp 플러그인이 포함됩니다. 서버 계측은 HTTP 요청에서 스팬 컨텍스트를 검색하고 서버의 요청 처리를 위한 스팬을 기록합니다. 클라이언트 계측은 스팬 컨텍스트를 발신 HTTP 요청에 삽입하고 응답을 대기하는 데 소요된 시간 스팬을 기록합니다.
  • go.opentelemetry.io/contrib/propagators/autoprop 패키지는 전파를 처리하기 위해 otelhttp에서 사용하는 OpenTelemetry TextMapPropagator 인터페이스의 구현을 제공합니다. 전파는 HTTP와 같은 전송의 trace 컨텍스트를 저장하는 데 사용되는 형식과 키를 결정합니다. 특히 otelhttp는 HTTP 헤더를 전파에 전달합니다. 전파는 헤더에서 스팬 컨텍스트를 Go 컨텍스트로 추출하거나, Go 컨텍스트의 스팬 컨텍스트를 인코딩하여 헤더에 삽입합니다(클라이언트 또는 서버인지에 따라 다름). 기본적으로 autoprop 패키지는 W3C trace 컨텍스트 전파 형식을 사용하여 스팬 컨텍스트를 삽입하고 추출합니다.
  • github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace 가져오기는 trace를 Cloud Trace로 내보냅니다.
  • github.com/gorilla/mux 가져오기는 샘플 애플리케이션에서 요청 처리에 사용하는 라이브러리입니다.
  • go.opentelemetry.io/contrib/detectors/gcp 가져오기는 애플리케이션이 Google Cloud 내에서 실행되는 위치를 식별하는 cloud.availability_zone과 같은 스팬에 속성을 추가합니다.
  • OpenTelemetry를 설정하는 데 사용되는 go.opentelemetry.io/otel, go.opentelemetry.io/otel/sdk/trace, go.opentelemetry.io/otel/sdk/resource 가져오기

main 함수 검토

main 함수는 Cloud Trace로의 trace 내보내기를 설정하고 mux 라우터를 사용하여 / URL에 대한 요청을 처리합니다.

func main() {
	ctx := context.Background()
	// Set up the Cloud Trace exporter.
	exporter, err := cloudtrace.New()
	if err != nil {
		log.Fatalf("cloudtrace.New: %v", err)
	}
	// Identify your application using resource detection.
	res, err := resource.New(ctx,
		// Use the GCP resource detector to detect information about the GKE Cluster.
		resource.WithDetectors(gcp.NewDetector()),
		resource.WithTelemetrySDK(),
	)
	if err != nil {
		log.Fatalf("resource.New: %v", err)
	}
	tp := trace.NewTracerProvider(
		trace.WithBatcher(exporter),
		trace.WithResource(res),
	)
	// Set the global TracerProvider which is used by otelhttp to record spans.
	otel.SetTracerProvider(tp)
	// Flush any pending spans on shutdown.
	defer tp.ForceFlush(ctx)

	// Set the global Propagators which is used by otelhttp to propagate
	// context using the w3c traceparent and baggage formats.
	otel.SetTextMapPropagator(autoprop.NewTextMapPropagator())

	// Handle incoming request.
	r := mux.NewRouter()
	r.HandleFunc("/", mainHandler)
	var handler http.Handler = r

	// Use otelhttp to create spans and extract context for incoming http
	// requests.
	handler = otelhttp.NewHandler(handler, "server")
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", os.Getenv("PORT")), handler))
}

이 코드에 대해서는 다음 사항에 유의하세요.

  • Google Cloud에서 실행될 때 속성을 감지하고 Cloud Trace로 trace를 내보내는 OpenTelemetry TracerProvider를 구성합니다.
  • otel.SetTracerProviderotel.SetTextMapPropagators 함수를 사용하여 전역 TracerProviderPropagator 설정을 지정합니다. 기본적으로 otelhttp와 같은 계측 라이브러리는 전역으로 등록된 TracerProvider를 사용하여 스팬 및 Propagator를 만들고 컨텍스트를 전파합니다.
  • HTTP 서버를 otelhttp.NewHandler로 래핑하여 HTTP 서버를 계측합니다.

mainHandler 함수 검토

func mainHandler(w http.ResponseWriter, r *http.Request) {
	// Use otelhttp to record a span for the outgoing call, and propagate
	// context to the destination.
	destination := os.Getenv("DESTINATION_URL")
	resp, err := otelhttp.Get(r.Context(), destination)
	if err != nil {
		log.Fatal("could not fetch remote endpoint")
	}
	defer resp.Body.Close()
	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("could not read response from %v", destination)
	}

	fmt.Fprint(w, strconv.Itoa(resp.StatusCode))
}

대상에 대한 아웃바운드 요청의 지연 시간을 캡처하려면 otelhttp 플러그인을 사용하여 HTTP 요청을 수행합니다. 또한 다음과 같이 r.Context 함수를 사용하여 수신 요청을 송신 요청에 연결합니다.

// Use otelhttp to record a span for the outgoing call, and propagate
// context to the destination.
resp, err := otelhttp.Get(r.Context(), destination)

애플리케이션 배포

이 섹션에서는 Cloud Build를 사용하여 백엔드 및 프런트엔드 서비스의 컨테이너 이미지를 빌드한 다음 이를 GKE 클러스터에 배포합니다.

Docker 컨테이너 빌드

  1. Cloud Shell의 작업 디렉터리에서 빌드를 제출합니다.

    cd $WORKDIR
    gcloud builds submit . --tag us-west1-docker.pkg.dev/$PROJECT_ID/distributed-tracing-docker-repo/backend:latest
    
  2. 컨테이너 이미지가 성공적으로 생성되었고 Artifact Registry에서 사용할 수 있는지 확인합니다.

    gcloud artifacts docker images list us-west1-docker.pkg.dev/$PROJECT_ID/distributed-tracing-docker-repo
    

    출력이 다음과 비슷하면 컨테이너 이미지가 성공적으로 생성된 것입니다. 여기서 PROJECT_ID는 Google Cloud 프로젝트의 ID입니다.

    NAME
    us-west1-docker.pkg.dev/PROJECT_ID/distributed-tracing-docker-repo/backend
    

백엔드 서비스 배포

  1. Cloud Shell에서 kubectx 컨텍스트를 backend 클러스터로 설정합니다.

    kubectx backend
    
  2. backend 배포를 위한 YAML 파일을 만듭니다.

    export PROJECT_ID=$(gcloud info --format='value(config.project)')
    envsubst < backend-deployment.yaml | kubectl apply -f -
    
  3. 포드가 실행 중인지 확인합니다.

    kubectl get pods
    

    출력에 RunningStatus 값이 표시됩니다.

    NAME                       READY   STATUS    RESTARTS   AGE
    backend-645859d95b-7mx95   1/1     Running   0          52s
    backend-645859d95b-qfdnc   1/1     Running   0          52s
    backend-645859d95b-zsj5m   1/1     Running   0          52s
    
  4. 부하 분산기를 사용하여 backend 배포를 노출합니다.

    kubectl expose deployment backend --type=LoadBalancer
    
  5. backend 서비스의 IP 주소를 가져옵니다.

    kubectl get services backend
    

    출력은 다음과 비슷합니다.

    NAME      TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)          AGE
    backend   LoadBalancer   10.11.247.58   34.83.88.143   8080:30714/TCP   70s
    

    EXTERNAL-IP 필드 값이 <pending>인 경우 IP 주소 값이 나올 때까지 명령어를 반복합니다.

  6. 이전 단계의 IP 주소를 변수로 캡처합니다.

    export BACKEND_IP=$(kubectl get svc backend -ojson | jq -r '.status.loadBalancer.ingress[].ip')
    

프런트엔드 서비스 배포

  1. Cloud Shell에서 kubectx 컨텍스트를 백엔드 클러스터로 설정합니다.

    kubectx frontend
    
  2. frontend 배포를 위한 YAML 파일을 만듭니다.

    export PROJECT_ID=$(gcloud info --format='value(config.project)')
    envsubst < frontend-deployment.yaml | kubectl apply -f -
    
  3. 포드가 실행 중인지 확인합니다.

    kubectl get pods
    

    출력에 RunningStatus 값이 표시됩니다.

    NAME                        READY   STATUS    RESTARTS   AGE
    frontend-747b445499-v7x2w   1/1     Running   0          57s
    frontend-747b445499-vwtmg   1/1     Running   0          57s
    frontend-747b445499-w47pf   1/1     Running   0          57s
    
  4. 부하 분산기를 사용하여 frontend 배포를 노출합니다.

    kubectl expose deployment frontend --type=LoadBalancer
    
  5. frontend 서비스의 IP 주소를 가져옵니다.

    kubectl get services frontend
    

    출력은 다음과 비슷합니다.

    NAME       TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)         AGE
    frontend   LoadBalancer   10.27.241.93   34.83.111.232   8081:31382/TCP  70s
    

    EXTERNAL-IP 필드 값이 <pending>인 경우 IP 주소 값이 나올 때까지 명령어를 반복합니다.

  6. 이전 단계의 IP 주소를 변수로 캡처합니다.

    export FRONTEND_IP=$(kubectl get svc frontend -ojson | jq -r '.status.loadBalancer.ingress[].ip')
    

애플리케이션 로드 및 trace 검토

이 섹션에서는 Apache Bench 유틸리티를 사용하여 애플리케이션에 대한 요청을 만듭니다. 그런 다음 Cloud Trace에서 결과 trace를 검토합니다.

  1. Cloud Shell에서 Apache Bench를 사용하여 3개의 동시 스레드를 통해 1,000개의 요청을 생성합니다.

    ab -c 3 -n 1000 http://${FRONTEND_IP}:8081/
    
  2. Google Cloud Console에서 Trace 목록 페이지로 이동합니다.

    Trace 목록으로 이동

  3. 타임라인을 검토하려면 server 라벨이 지정된 URI 중 하나를 클릭합니다.

    trace의 분산형 그래프

    이 trace에는 다음 이름을 가진 4개의 스팬이 포함됩니다.

    • 첫 번째 server 스팬은 프런트엔드 서버에서 HTTP 요청을 처리할 때의 엔드 투 엔드 지연 시간을 캡처합니다.
    • 첫 번째 HTTP GET 스팬은 프런트엔드 클라이언트가 백엔드로 요청한 GET 호출의 지연 시간을 캡처합니다.
    • 두 번째 server 스팬은 백엔드 서버에서 HTTP 요청을 처리할 때의 엔드 투 엔드 지연 시간을 캡처합니다.
    • 두 번째 HTTP GET 스팬은 백엔드 클라이언트가 google.com에 요청한 GET 호출의 지연 시간을 캡처합니다.

    스팬의 막대 그래프

삭제

비용이 청구되지 않도록 하는 가장 쉬운 방법은 배포하면서 만든 Google Cloud 프로젝트를 삭제하는 것입니다. 또는 개별 리소스를 삭제할 수 있습니다.

프로젝트 삭제

  1. Google Cloud 콘솔에서 리소스 관리 페이지로 이동합니다.

    리소스 관리로 이동

  2. 프로젝트 목록에서 삭제할 프로젝트를 선택하고 삭제를 클릭합니다.
  3. 대화상자에서 프로젝트 ID를 입력한 후 종료를 클릭하여 프로젝트를 삭제합니다.

개별 리소스 삭제

전체 프로젝트를 삭제하는 대신 개별 리소스를 삭제하려면 Cloud Shell에서 다음 명령어를 실행합니다.

gcloud container clusters delete frontend-cluster --zone=us-west1-a
gcloud container clusters delete backend-cluster --zone=us-west1-a
gcloud artifacts repositories delete distributed-tracing-docker-repo --location us-west1

다음 단계