分散トレースをデプロイしてマイクロサービスのレイテンシをモニタリングする

Last reviewed 2023-08-11 UTC

このドキュメントでは、分散トレースを使用してマイクロサービスのレイテンシをモニタリングするで説明されているリファレンス アーキテクチャをデプロイする方法について説明します。このドキュメントで説明するデプロイでは、OpenTelemetry と Cloud Trace を使用して、マイクロサービス アプリケーションのトレース情報をキャプチャしています。

このデプロイのサンプル アプリケーションは、Go で記述された 2 つのマイクロサービスで構成されています。

このドキュメントでは、次の内容を理解していることを前提としています。

目標

  • GKE クラスタを作成してサンプル アプリケーションをデプロイする。
  • OpenTelemetry 計測コードを確認する。
  • 計測により生成されたトレースとログを確認する。

アーキテクチャ

次の図は、デプロイするアーキテクチャを示しています。

2 つの GKE クラスタからなるデプロイのアーキテクチャ。

フルマネージドの継続的インテグレーション、デリバリー、デプロイのためのプラットフォームである Cloud Build を使用して、コンテナ イメージをサンプルコードからビルドし、Artifact Registry に保存します。GKE クラスタは、デプロイ時に Artifact Registry からイメージを pull します。

フロントエンド サービスは、/ URL で HTTP リクエストを受け入れ、バックエンド サービスを呼び出します。バックエンド サービスのアドレスは環境変数により定義されます。

バックエンド サービスは / URL で HTTP リクエストを受け入れ、環境変数で定義された外部 URL に対してアウトバウンド呼び出しを実行します。外部呼び出しが完了すると、バックエンド サービスは HTTP ステータス呼び出し(200 など)を呼び出し元に返します。

費用

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。

始める前に

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

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

  4. Enable the GKE, Cloud Trace, Cloud Build, Cloud Storage, and Artifact Registry APIs.

    Enable the APIs

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

    Go to project selector

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

  7. Enable the GKE, Cloud Trace, Cloud Build, Cloud Storage, and Artifact Registry APIs.

    Enable the APIs

環境を設定する

このセクションでは、デプロイ全体で使用するツールで環境を設定します。このデプロイでは、すべてのターミナル コマンドを Cloud Shell から実行します。

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

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  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 を選択し、モードとして Standard を選択します。

  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 ファイルのコードを確認します。コンテキスト伝播を使用して、複数のリクエストのスパンを 1 つの親トレースに追加する方法を確認できます。

アプリケーション コードのインポートを確認する

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 などのトランスポートでトレース コンテキストを保存するために使用される形式とキーが決定されます。具体的には、otelhttp は HTTP ヘッダーをプロパゲータに渡します。プロパゲータは、ヘッダーからスパン コンテキストを Go コンテキストに抽出するか、Go コンテキストのスパン コンテキストをエンコードしてヘッダーに挿入します(これはクライアントかサーバーかによって異なります)。デフォルトでは、autoprop パッケージは W3C トレース コンテキストの伝播形式を使用して、スパン コンテキストの挿入と抽出を行います。
  • github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace インポートはトレースを Cloud Trace にエクスポートします。
  • github.com/gorilla/mux インポートは、サンプル アプリケーションがリクエスト処理に使用するライブラリです。
  • go.opentelemetry.io/contrib/detectors/gcp インポートにより、Google Cloud 内でアプリケーションが実行される場所を表す cloud.availability_zone などの属性がスパンに追加されます。
  • go.opentelemetry.io/otelgo.opentelemetry.io/otel/sdk/tracego.opentelemetry.io/otel/sdk/resource インポート。OpenTelemetry の設定に使用されます。

main 関数を確認します。

main 関数は、Cloud 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))
}

このコードについては次の点に注意してください。

  • OpenTelemetry TracerProvider を構成します。これにより、Google Cloud 上での実行時に属性が検出され、Cloud Trace にトレースがエクスポートされます。
  • TracerProviderPropagator のグローバル設定を指定するには、otel.SetTracerProvider 関数と otel.SetTextMapPropagators 関数を使用します。デフォルトでは、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 Deployment の YAML ファイルを作成します。

    export PROJECT_ID=$(gcloud info --format='value(config.project)')
    envsubst < backend-deployment.yaml | kubectl apply -f -
    
  3. Pod が実行されていることを確認します。

    kubectl get pods
    

    出力では、Status の値が Running になります。

    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 Deployment の YAML ファイルを作成します。

    export PROJECT_ID=$(gcloud info --format='value(config.project)')
    envsubst < frontend-deployment.yaml | kubectl apply -f -
    
  3. Pod が実行されていることを確認します。

    kubectl get pods
    

    出力では、Status の値が Running になります。

    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')
    

アプリケーションを読み込んでトレースを確認する

このセクションでは、Apache Bench ユーティリティを使用して、アプリケーションに対するリクエストを作成します。その後、Cloud Trace で結果のトレースを確認します。

  1. Cloud Shell で Apache Bench を使用し、3 つの同時実行スレッドを使用して 1,000 件のリクエストを生成します。

    ab -c 3 -n 1000 http://${FRONTEND_IP}:8081/
    
  2. Google Cloud コンソールで、[トレースリスト] ページに移動します。

    トレースリストに移動

  3. タイムラインを確認するには、server というラベルの付いた URI のいずれかをクリックします。

    トレースの散布図グラフ。

    このトレースには、次の名前の 4 つのスパンが含まれています。

    • 最初の server スパンは、フロントエンド サーバーで HTTP リクエストを処理するエンドツーエンドのレイテンシをキャプチャします。
    • 最初の HTTP GET スパンは、フロントエンドのクライアントがバックエンドに対して行う GET 呼び出しのレイテンシをキャプチャします。
    • 2 番目の server スパンは、バックエンド サーバーで HTTP リクエストを処理するエンドツーエンドのレイテンシをキャプチャします。
    • 2 番目の HTTP GET スパンは、バックエンドのクライアントが google.com に対して行う GET 呼び出しのレイテンシをキャプチャします。

    スパンの棒グラフ。

クリーンアップ

課金を停止する最も簡単な方法は、デプロイ用に作成した 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.

リソースを個別に削除する

プロジェクト全体を削除するのではなく、個々のリソースを削除するには、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

次のステップ