1. はじめに

最終更新日: 2022 年 7 月 15 日
アプリケーションのオブザーバビリティ
オブザーバビリティと OpenTelemetry
オブザーバビリティは、システムの属性を表す用語です。オブザーバビリティを備えたシステムでは、チームがシステムを積極的にデバッグできます。このコンテキストでは、オブザーバビリティの 3 つの柱であるログ、指標、トレースは、システムがオブザーバビリティを取得するための基本的な計測です。
OpenTelemetry は、オブザーバビリティに必要なテレメトリー データ(ログ、指標、トレース)の計測とエクスポートを加速する一連の仕様、ライブラリ、エージェントです。OpenTelemetry は、CNCF の下にあるオープン スタンダードで、コミュニティ主導のプロジェクトです。プロジェクトとそのエコシステムが提供するライブラリを利用することで、デベロッパーはベンダーに依存しない方法で、複数のアーキテクチャに対してアプリケーションを計測できます。
また、オブザーバビリティの 3 つの柱に加えて、継続的プロファイリングはオブザーバビリティのもう 1 つの重要なコンポーネントであり、業界でユーザーベースを拡大しています。Cloud Profiler は、アプリケーションのコールスタックのパフォーマンス指標を簡単にドリルダウンできるインターフェースを提供します。
この Codelab はシリーズの第 1 部で、OpenTelemetry と Cloud Trace を使用してマイクロサービスで分散トレースを計測する方法について説明します。パート 2 では、Cloud Profiler を使用した継続的なプロファイリングについて説明します。
分散トレース
ログ、指標、トレースのうち、トレースはシステムのプロセス内の特定の部分のレイテンシを示すテレメトリーです。特にマイクロサービスの時代には、分散トレースは分散システム全体のレイテンシ ボトルネックを見つけるための強力な推進力となります。
分散トレースを分析する場合、トレースデータの可視化は、システム全体のレイテンシを一目で把握するための鍵となります。分散トレースでは、複数のスパンを含むトレースの形式で、システム エントリポイントへの単一のリクエストを処理するための一連の呼び出しを処理します。
スパンは、分散システムで行われた個々の作業単位を表し、開始時刻と終了時刻を記録します。スパンには階層関係があることがよくあります。下の図では、すべての小さなスパンが大きな /messages スパンの子スパンであり、システム内の作業パスを示す 1 つのトレースにまとめられています。

Google Cloud Trace は、分散トレース バックエンドのオプションの 1 つであり、Google Cloud の他のプロダクトとよく統合されています。
作成するアプリの概要
この Codelab では、Google Kubernetes Engine クラスタで実行される「Shakespeare アプリケーション」(Shakesapp)というサービスでトレース情報を計測します。Shakesapp のアーキテクチャは次のとおりです。

- Loadgen が HTTP でクエリ文字列をクライアントに送信する
- クライアントは、loadgen からサーバーへのクエリを gRPC で渡します。
- サーバーはクライアントからクエリを受け取り、Google Cloud Storage からテキスト形式のシェイクスピアの全作品を取得し、クエリを含む行を検索して、一致した行の数をクライアントに返します。
リクエスト全体でトレース情報を計測します。その後、プロファイラー エージェントをサーバーに埋め込み、ボトルネックを調査します。
学習内容
- Go プロジェクトで OpenTelemetry Trace ライブラリを使ってみる
- ライブラリを使用してスパンを作成する方法
- アプリ コンポーネント間でスパン コンテキストを伝播する方法
- トレースデータを Cloud Trace に送信する方法
- Cloud Trace でトレースを分析する方法
この Codelab では、マイクロサービスを計測する方法について説明します。わかりやすくするために、この例には 3 つのコンポーネント(ロード ジェネレータ、クライアント、サーバー)のみが含まれていますが、この Codelab で説明する同じプロセスを、より複雑で大規模なシステムに適用できます。
必要なもの
- Go の基本的な知識
- Kubernetes の基礎知識
2. 設定と要件
セルフペース型の環境設定
Google アカウント(Gmail または Google Apps)をお持ちでない場合は、1 つ作成する必要があります。Google Cloud Platform のコンソール(console.cloud.google.com)にログインし、新しいプロジェクトを作成します。
すでにプロジェクトが存在する場合は、コンソールの左上にあるプロジェクト選択プルダウン メニューをクリックします。

表示されたダイアログで [NEW PROJECT] ボタンをクリックして、新しいプロジェクトを作成します。

まだプロジェクトが存在しない場合は、次のような最初のプロジェクトを作成するためのダイアログが表示されます。

続いて表示されるプロジェクト作成ダイアログでは、新しいプロジェクトの詳細を入力できます。

プロジェクト ID を忘れないようにしてください。プロジェクト ID は、すべての Google Cloud プロジェクトを通じて一意の名前にする必要があります(上記の名前はすでに使用されているため使用できません)。以降、この Codelab では PROJECT_ID と呼びます。
次に、Google Cloud リソースを使用し、Cloud Trace API を有効にするために、Developers Console で課金を有効にする必要があります。

この Codelab の操作をすべて行っても、費用は数ドル程度です。ただし、その他のリソースを使いたい場合や、実行したままにしておきたいステップがある場合は、追加コストがかかる可能性があります(このドキュメントの最後にある「クリーンアップ」セクションをご覧ください)。Google Cloud Trace、Google Kubernetes Engine、Google Artifact Registry の料金は、公式ドキュメントに記載されています。
- Google Cloud のオペレーション スイートの料金 | オペレーション スイート
- 料金 | Kubernetes Engine ドキュメント
- Artifact Registry の料金 | Artifact Registry のドキュメント
Google Cloud Platform の新規ユーザーの皆さんには、$300 の無料トライアルをご利用いただけます。その場合は、この Codelab を完全に無料でご利用いただけます。
Google Cloud Shell の設定
Google Cloud と Google Cloud Trace はノートパソコンからリモートで操作できますが、この Codelab では、Google Cloud Shell(Cloud 上で動作するコマンドライン環境)を使用します。
この Debian ベースの仮想マシンには、必要な開発ツールがすべて用意されています。仮想マシンは Google Cloud で稼働し、永続的なホーム ディレクトリが 5 GB 用意されているため、ネットワークのパフォーマンスと認証が大幅に向上しています。つまり、この Codelab に必要なのはブラウザだけです(Chromebook でも動作します)。
Cloud Console から Cloud Shell を有効にするには、[Cloud Shell をアクティブにする]
をクリックします(環境のプロビジョニングと接続に若干時間を要します)。


Cloud Shell に接続すると、すでに認証は完了しており、プロジェクトに各自の PROJECT_ID が設定されていることがわかります。
gcloud auth list
コマンド出力
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
コマンド出力
[core] project = <PROJECT_ID>
なんらかの理由でプロジェクトが設定されていない場合は、次のコマンドを実行します。
gcloud config set project <PROJECT_ID>
PROJECT_ID が見つからない場合は、設定手順で使用した ID を確認するか、Cloud Console ダッシュボードで検索します。

Cloud Shell では、デフォルトで環境変数もいくつか設定されます。これらの変数は、以降のコマンドを実行する際に有用なものです。
echo $GOOGLE_CLOUD_PROJECT
コマンド出力
<PROJECT_ID>
最後に、デフォルトのゾーンとプロジェクト構成を設定します。
gcloud config set compute/zone us-central1-f
さまざまなゾーンを選択できます。詳細については、リージョンとゾーンをご覧ください。
Go 言語の設定
この Codelab では、すべてのソースコードに Go を使用します。Cloud Shell で次のコマンドを実行し、Go のバージョンが 1.17 以降であることを確認します。
go version
コマンド出力
go version go1.18.3 linux/amd64
Google Kubernetes クラスタを設定する
この Codelab では、Google Kubernetes Engine(GKE)でマイクロサービスのクラスタを実行します。この Codelab のプロセスは次のとおりです。
- ベースライン プロジェクトを Cloud Shell にダウンロードする
- マイクロサービスをコンテナにビルドする
- コンテナを Google Artifact Registry(GAR)にアップロードする
- コンテナを GKE にデプロイする
- トレース計測用にサービスのソースコードを変更する
- ステップ 2 に進みます
Kubernetes Engine を有効にする
まず、Shakesapp が GKE で実行される Kubernetes クラスタを設定します。そのため、GKE を有効にする必要があります。[Kubernetes Engine] メニューに移動し、[有効にする] ボタンを押します。

これで、Kubernetes クラスタを作成する準備が整いました。
Kubernetes クラスタを作成する
Cloud Shell で次のコマンドを実行して、Kubernetes クラスタを作成します。Artifact Registry リポジトリの作成に使用するリージョンにゾーン値が含まれていることを確認してください。リポジトリ リージョンがゾーンをカバーしていない場合は、ゾーン値 us-central1-f を変更します。
gcloud container clusters create otel-trace-codelab2 \ --zone us-central1-f \ --release-channel rapid \ --preemptible \ --enable-autoscaling \ --max-nodes 8 \ --no-enable-ip-alias \ --scopes cloud-platform
コマンド出力
Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s). Creating cluster otel-trace-codelab2 in us-central1-f... Cluster is being health-checked (master is healthy)...done. Created [https://container.googleapis.com/v1/projects/development-215403/zones/us-central1-f/clusters/otel-trace-codelab2]. To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab2?project=development-215403 kubeconfig entry generated for otel-trace-codelab2. NAME: otel-trace-codelab2 LOCATION: us-central1-f MASTER_VERSION: 1.23.6-gke.1501 MASTER_IP: 104.154.76.89 MACHINE_TYPE: e2-medium NODE_VERSION: 1.23.6-gke.1501 NUM_NODES: 3 STATUS: RUNNING
Artifact Registry と skaffold の設定
これで、デプロイの準備が整った Kubernetes クラスタができました。次に、コンテナを push してデプロイするためのコンテナ レジストリを準備します。これらの手順では、Artifact Registry(GAR)を設定し、それを使用するように skaffold を設定する必要があります。
Artifact Registry の設定
[Artifact Registry] のメニューに移動し、[有効にする] ボタンを押します。

しばらくすると、GAR のリポジトリ ブラウザが表示されます。[リポジトリを作成] ボタンをクリックして、リポジトリの名前を入力します。

この Codelab では、新しいリポジトリに trace-codelab という名前を付けます。アーティファクトの形式は「Docker」、ロケーション タイプは「リージョン」です。Google Compute Engine のデフォルト ゾーンに設定したゾーンに近いリージョンを選択します。たとえば、上記の例では「us-central1-f」を選択したので、ここでは「us-central1(アイオワ)」を選択します。[作成] ボタンをクリックします。

リポジトリ ブラウザに「trace-codelab」が表示されます。

後でここに戻ってレジストリ パスを確認します。
Skaffold の設定
Skaffold は、Kubernetes で実行されるマイクロサービスの構築に取り組む際に便利なツールです。一連のコマンドで、アプリケーションのコンテナのビルド、push、デプロイのワークフローを処理します。Skaffold はデフォルトで Docker Registry をコンテナ レジストリとして使用するため、コンテナを push するときに GAR を認識するように Skaffold を構成する必要があります。
Cloud Shell をもう一度開き、Skaffold がインストールされていることを確認します。(Cloud Shell はデフォルトで環境に skaffold をインストールします)。次のコマンドを実行して、skaffold のバージョンを確認します。
skaffold version
コマンド出力
v1.38.0
これで、skaffold が使用するデフォルトのリポジトリを登録できます。レジストリ パスを取得するには、Artifact Registry ダッシュボードに移動し、前の手順で設定したリポジトリの名前をクリックします。

ページの上部にパンくずリストが表示されます。
アイコンをクリックして、レジストリ パスをクリップボードにコピーします。

コピーボタンをクリックすると、ブラウザの下部に次のようなメッセージが表示されたダイアログが表示されます。
「us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab」がコピーされました
Cloud Shell に戻ります。ダッシュボードからコピーした値を使用して skaffold config set default-repo コマンドを実行します。
skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab
コマンド出力
set value default-repo to us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab for context gke_stackdriver-sandbox-3438851889_us-central1-b_stackdriver-sandbox
また、レジストリを Docker 構成に構成する必要があります。次のコマンドを実行します。
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
コマンド出力
{
"credHelpers": {
"gcr.io": "gcloud",
"us.gcr.io": "gcloud",
"eu.gcr.io": "gcloud",
"asia.gcr.io": "gcloud",
"staging-k8s.gcr.io": "gcloud",
"marketplace.gcr.io": "gcloud",
"us-central1-docker.pkg.dev": "gcloud"
}
}
Adding credentials for: us-central1-docker.pkg.dev
これで、次のステップに進み、GKE で Kubernetes コンテナを設定できます。
概要
この手順では、Codelab 環境を設定します。
- Cloud Shell を設定する
- コンテナ レジストリ用の Artifact Registry リポジトリを作成した
- コンテナ レジストリを使用するように Skaffold を設定する
- Codelab のマイクロサービスが実行される Kubernetes クラスタを作成した
次のステップ
次のステップでは、マイクロサービスをビルドしてクラスタにプッシュし、デプロイします。
3. マイクロサービスをビルド、push、デプロイする
Codelab の資料をダウンロードする
前のステップでは、この Codelab の前提条件をすべて設定しました。これで、これらの上にマイクロサービス全体を実行する準備が整いました。Codelab の資料は GitHub でホストされているため、次の git コマンドを使用して Cloud Shell シェル環境にダウンロードします。
cd ~ git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git cd opentelemetry-trace-codelab-go
プロジェクトのディレクトリ構造は次のようになります。
.
├── README.md
├── step0
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step1
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step2
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step3
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step4
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step5
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
└── step6
├── manifests
├── proto
├── skaffold.yaml
└── src
- manifests: Kubernetes マニフェスト ファイル
- proto: クライアントとサーバー間の通信の proto 定義
- src: 各サービスのソースコードのディレクトリ
- skaffold.yaml: skaffold の構成ファイル
この Codelab では、step0 フォルダにあるソースコードを更新します。次の手順の解答については、step[1-6] フォルダのソースコードも参照してください。(パート 1 ではステップ 0 ~ 4 を、パート 2 ではステップ 5 ~ 6 を説明します)。
skaffold コマンドを実行する
これで、作成した Kubernetes クラスタにコンテンツ全体をビルド、push、デプロイする準備が整いました。複数の手順が含まれているように見えますが、実際には Skaffold がすべてを実行します。次のコマンドで試してみましょう。
cd step0 skaffold dev
コマンドを実行するとすぐに、docker build のログ出力が表示され、レジストリに正常に push されたことを確認できます。
コマンド出力
... ---> Running in c39b3ea8692b ---> 90932a583ab6 Successfully built 90932a583ab6 Successfully tagged us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step1 The push refers to repository [us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice] cc8f5a05df4a: Preparing 5bf719419ee2: Preparing 2901929ad341: Preparing 88d9943798ba: Preparing b0fdf826a39a: Preparing 3c9c1e0b1647: Preparing f3427ce9393d: Preparing 14a1ca976738: Preparing f3427ce9393d: Waiting 14a1ca976738: Waiting 3c9c1e0b1647: Waiting b0fdf826a39a: Layer already exists 88d9943798ba: Layer already exists f3427ce9393d: Layer already exists 3c9c1e0b1647: Layer already exists 14a1ca976738: Layer already exists 2901929ad341: Pushed 5bf719419ee2: Pushed cc8f5a05df4a: Pushed step1: digest: sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe size: 2001
すべてのサービス コンテナの push が完了すると、Kubernetes Deployment が自動的に開始されます。
コマンド出力
sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 size: 1997 Tags used in deployment: - serverservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step4@sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe - clientservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/clientservice:step4@sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 - loadgen -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/loadgen:step4@sha256:eea2e5bc8463ecf886f958a86906cab896e9e2e380a0eb143deaeaca40f7888a Starting deploy... - deployment.apps/clientservice created - service/clientservice created - deployment.apps/loadgen created - deployment.apps/serverservice created - service/serverservice created
デプロイ後、各コンテナの stdout に出力された実際のアプリケーション ログは次のようになります。
コマンド出力
[client] 2022/07/14 06:33:15 {"match_count":3040}
[loadgen] 2022/07/14 06:33:15 query 'love': matched 3040
[client] 2022/07/14 06:33:15 {"match_count":3040}
[loadgen] 2022/07/14 06:33:15 query 'love': matched 3040
[client] 2022/07/14 06:33:16 {"match_count":3040}
[loadgen] 2022/07/14 06:33:16 query 'love': matched 3040
[client] 2022/07/14 06:33:19 {"match_count":463}
[loadgen] 2022/07/14 06:33:19 query 'tear': matched 463
[loadgen] 2022/07/14 06:33:20 query 'world': matched 728
[client] 2022/07/14 06:33:20 {"match_count":728}
[client] 2022/07/14 06:33:22 {"match_count":463}
[loadgen] 2022/07/14 06:33:22 query 'tear': matched 463
この時点では、サーバーからのメッセージを確認する必要があります。これで、サービスの分散トレース用に OpenTelemetry を使用してアプリケーションの計測を開始する準備が整いました。
サービスの計測を開始する前に、Ctrl+C キーを押してクラスタをシャットダウンしてください。
コマンド出力
...
[client] 2022/07/14 06:34:57 {"match_count":1}
[loadgen] 2022/07/14 06:34:57 query 'what's past is prologue': matched 1
^CCleaning up...
- W0714 06:34:58.464305 28078 gcp.go:120] WARNING: the gcp auth plugin is deprecated in v1.22+, unavailable in v1.25+; use gcloud instead.
- To learn more, consult https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke
- deployment.apps "clientservice" deleted
- service "clientservice" deleted
- deployment.apps "loadgen" deleted
- deployment.apps "serverservice" deleted
- service "serverservice" deleted
概要
このステップでは、環境で Codelab の資料を準備し、skaffold が想定どおりに実行されることを確認しました。
次のステップ
次のステップでは、トレース情報を計測するように loadgen サービスのソースコードを変更します。
4. HTTP の計測
トレース計測と伝播のコンセプト
ソースコードを編集する前に、分散トレースの仕組みを簡単な図で説明します。

この例では、Trace と Span の情報を Cloud Trace にエクスポートし、loadgen サービスからサーバー サービスへのリクエスト全体でトレース コンテキストを伝播するようにコードを計測します。
Cloud Trace が同じトレース ID を持つすべてのスパンを 1 つのトレースに組み立てるには、アプリケーションがトレース ID やスパン ID などのトレース メタデータを送信する必要があります。また、アプリケーションは、ダウンストリーム サービスをリクエストするときにトレース コンテキスト(トレース ID と親スパンのスパン ID の組み合わせ)を伝播する必要があります。これにより、処理中のトレース コンテキストを認識できます。
OpenTelemetry は、次のことに役立ちます。
- 一意のトレース ID とスパン ID を生成する
- トレース ID とスパン ID をバックエンドにエクスポートする
- 他のサービスにトレース コンテキストを伝播する
- トレースの分析に役立つ追加のメタデータを埋め込む
OpenTelemetry Trace のコンポーネント

OpenTelemetry を使用してアプリケーション トレースを計測するプロセスは次のとおりです。
- エクスポータを作成する
- 1 でエクスポータをバインドし、グローバルに設定する TracerProvider を作成します。
- TextMapPropagaror を設定して伝播メソッドを設定する
- TracerProvider から Tracer を取得する
- Tracer から Span を生成する
現時点では、各コンポーネントの詳細なプロパティを理解する必要はありませんが、次の点を覚えておくことが重要です。
- ここで、エクスポーターは TracerProvider にプラグイン可能です。
- TracerProvider は、トレース サンプリングとエクスポートに関するすべての構成を保持します
- すべてのトレースが Tracer オブジェクトにバンドルされている
このことを理解したうえで、実際のコーディング作業に進みましょう。
最初のスパンを計測する
ロード ジェネレータ サービスを計測する
Cloud Shell の右上にあるボタン
を押して、Cloud Shell エディタを開きます。左側のペインのエクスプローラから step0/src/loadgen/main.go を開き、メイン関数を見つけます。
step0/src/loadgen/main.go
func main() {
...
for range t.C {
log.Printf("simulating client requests, round %d", i)
if err := run(numWorkers, numConcurrency); err != nil {
log.Printf("aborted round with error: %v", err)
}
log.Printf("simulated %d requests", numWorkers)
if numRounds != 0 && i > numRounds {
break
}
i++
}
}
メイン関数では、ループ内で関数 run が呼び出されています。現在の実装では、このセクションには関数呼び出しの開始と終了を記録する 2 つのログ行があります。次に、関数呼び出しのレイテンシを追跡するために、Span 情報を計測します。
まず、前のセクションで説明したように、OpenTelemetry の構成全体を設定します。次のように OpenTelemetry パッケージを追加します。
step0/src/loadgen/main.go
import (
"context" // step1. add packages
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"net/http"
"net/url"
"time"
// step1. add packages
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
"go.opentelemetry.io/otel/trace"
// step1. end add packages
)
読みやすくするために、initTracer というセットアップ関数を作成し、main 関数で呼び出します。
step0/src/loadgen/main.go
// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// create a stdout exporter to show collected spans out to stdout.
exporter, err := stdout.New(stdout.WithPrettyPrint())
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
OpenTelemetry を設定する手順は、前のセクションで説明したとおりです。この実装では、すべてのトレース情報を構造化された形式で stdout にエクスポートする stdout エクスポーターを使用します。
次に、メイン関数から呼び出します。initTracer() を呼び出し、アプリケーションを閉じる際には TracerProvider.Shutdown() を呼び出すようにしてください。
step0/src/loadgen/main.go
func main() {
// step1. setup OpenTelemetry
tp, err := initTracer()
if err != nil {
log.Fatalf("failed to initialize TracerProvider: %v", err)
}
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
log.Fatalf("error shutting down TracerProvider: %v", err)
}
}()
// step1. end setup
log.Printf("starting worder with %d workers in %d concurrency", numWorkers, numConcurrency)
log.Printf("number of rounds: %d (0 is inifinite)", numRounds)
...
設定が完了したら、一意のトレース ID とスパン ID を持つスパンを作成する必要があります。OpenTelemetry には、これに便利なライブラリが用意されています。計測用の HTTP クライアントに新しいパッケージを追加します。
step0/src/loadgen/main.go
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"net/http"
"net/http/httptrace" // step1. add packages
"net/url"
"time"
// step1. add packages
"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
// step1. end add packages
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
"go.opentelemetry.io/otel/trace"
)
ロードジェネレータは runQuery 関数の net/http で HTTP のクライアント サービスを呼び出しているため、net/http に contrib パッケージを使用し、httptrace パッケージと otelhttp パッケージの拡張機能で計測を有効にします。
まず、パッケージのグローバル変数 httpClient を追加して、計測されたクライアントを介して HTTP リクエストを呼び出します。
step0/src/loadgen/main.go
var httpClient = http.Client{
Transport: otelhttp.NewTransport(http.DefaultTransport)
}
次に、runQuery 関数に計測を追加して、OpenTelemetry を使用したカスタム スパンと、カスタム HTTP クライアントから自動生成されたスパンを作成します。手順は次のとおりです。
- グローバル
TracerProviderからotel.Tracer()で Tracer を取得する Tracer.Start()メソッドでルートスパンを作成する- 任意のタイミングでルートスパンを終了します(この場合は
runQuery関数の終了時)。
step0/src/loadgen/main.go
reqURL.RawQuery = v.Encode()
// step1. replace http.Get() with custom client call
// resp, err := http.Get(reqURL.String())
// step1. instrument trace
ctx := context.Background()
tr := otel.Tracer("loadgen")
ctx, span := tr.Start(ctx, "query.request", trace.WithAttributes(
semconv.TelemetrySDKLanguageGo,
semconv.ServiceNameKey.String("loadgen.runQuery"),
attribute.Key("query").String(s),
))
defer span.End()
ctx = httptrace.WithClientTrace(ctx, otelhttptrace.NewClientTrace(ctx))
req, err := http.NewRequestWithContext(ctx, "GET", reqURL.String(), nil)
if err != nil {
return -1, fmt.Errorf("error creating HTTP request object: %v", err)
}
resp, err := httpClient.Do(req)
// step1. end instrumentation
if err != nil {
return -1, fmt.Errorf("error sending request to %v: %v", reqURL.String(), err)
}
これで、loadgen(HTTP クライアント アプリケーション)の計測が完了しました。go mod コマンドを使用して、go.mod と go.sum を更新してください。
go mod tidy
計測器のクライアント サービス
前のセクションでは、下の図の赤い四角で囲まれた部分を計測しました。ロード ジェネレータ サービスでスパン情報を計測しました。負荷生成ツールのサービスと同様に、クライアント サービスを計測する必要があります。ロード ジェネレータ サービスとの違いは、クライアント サービスが HTTP ヘッダーのロード ジェネレータ サービスから伝播されたトレース ID 情報を抽出し、その ID を使用してスパンを生成する必要があることです。

Cloud Shell エディタを開き、ロード ジェネレータ サービスの場合と同様に必要なパッケージを追加します。
step0/src/client/main.go
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"time"
"opentelemetry-trace-codelab-go/client/shakesapp"
// step1. add new import
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
// step1. end new import
)
ここでも、OpenTelemtry を設定する必要があります。loadgen から initTracer 関数をコピーして貼り付け、クライアント サービスの main 関数でも呼び出します。
step0/src/client/main.go
// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// create a stdout exporter to show collected spans out to stdout.
exporter, err := stdout.New(stdout.WithPrettyPrint())
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
それでは、スパンを計測してみましょう。クライアント サービスは loadgen サービスからの HTTP リクエストを受け入れる必要があるため、ハンドラを計測する必要があります。クライアント サービスの HTTP サーバーは net/http で実装されています。loadgen で行ったように otelhttp パッケージを使用できます。
まず、ハンドラ登録を otelhttp ハンドラに置き換えます。main 関数で、HTTP ハンドラが http.HandleFunc() に登録されている行を見つけます。
step0/src/client/main.go
// step1. change handler to intercept OpenTelemetry related headers
// http.HandleFunc("/", svc.handler)
otelHandler := otelhttp.NewHandler(http.HandlerFunc(svc.handler), "client.handler")
http.Handle("/", otelHandler)
// step1. end intercepter setting
http.HandleFunc("/_genki", svc.health)
次に、ハンドラ内の実際のスパンを計測します。func (*clientService) handler() を見つけて、trace.SpanFromContext() を使用してスパン計測を追加します。
step0/src/client/main.go
func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
...
ctx := r.Context()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// step1. instrument trace
span := trace.SpanFromContext(ctx)
defer span.End()
// step1. end instrument
...
この計測により、handler メソッドの開始から終了までのスパンを取得できます。スパンを簡単に分析できるようにするには、一致した数を保存する追加の属性をクエリに追加します。ログ行の直前に、次のコードを追加します。
func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
...
// step1. add span specific attribute
span.SetAttributes(attribute.Key("matched").Int64(resp.MatchCount))
// step1. end adding attribute
log.Println(string(ret))
...
上記のすべての計測により、loadgen とクライアント間のトレース計測が完了しました。では、その仕組みを見てみましょう。skaffold でコードを再度実行します。
skaffold dev
GKE クラスタでサービスを実行すると、次のような大量のログメッセージが表示されます。
コマンド出力
[loadgen] {
[loadgen] "Name": "query.request",
[loadgen] "SpanContext": {
[loadgen] "TraceID": "cfa22247a542beeb55a3434392d46b89",
[loadgen] "SpanID": "18b06404b10c418b",
[loadgen] "TraceFlags": "01",
[loadgen] "TraceState": "",
[loadgen] "Remote": false
[loadgen] },
[loadgen] "Parent": {
[loadgen] "TraceID": "00000000000000000000000000000000",
[loadgen] "SpanID": "0000000000000000",
[loadgen] "TraceFlags": "00",
[loadgen] "TraceState": "",
[loadgen] "Remote": false
[loadgen] },
[loadgen] "SpanKind": 1,
[loadgen] "StartTime": "2022-07-14T13:13:36.686751087Z",
[loadgen] "EndTime": "2022-07-14T13:14:31.849601964Z",
[loadgen] "Attributes": [
[loadgen] {
[loadgen] "Key": "telemetry.sdk.language",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "go"
[loadgen] }
[loadgen] },
[loadgen] {
[loadgen] "Key": "service.name",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "loadgen.runQuery"
[loadgen] }
[loadgen] },
[loadgen] {
[loadgen] "Key": "query",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "faith"
[loadgen] }
[loadgen] }
[loadgen] ],
[loadgen] "Events": null,
[loadgen] "Links": null,
[loadgen] "Status": {
[loadgen] "Code": "Unset",
[loadgen] "Description": ""
[loadgen] },
[loadgen] "DroppedAttributes": 0,
[loadgen] "DroppedEvents": 0,
[loadgen] "DroppedLinks": 0,
[loadgen] "ChildSpanCount": 5,
[loadgen] "Resource": [
[loadgen] {
[loadgen] "Key": "service.name",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "unknown_service:loadgen"
...
stdout エクスポータはこれらのメッセージを出力します。loadgen によるすべてのスパンの親に TraceID: 00000000000000000000000000000000 があることに気付くでしょう。これはルートスパン、つまりトレースの最初のスパンであるためです。また、埋め込み属性 "query" に、クライアント サービスに渡されるクエリ文字列が含まれていることも確認できます。
概要
このステップでは、HTTP で通信するロード ジェネレータ サービスとクライアント サービスを計測し、サービス間で Trace Context を正常に伝播し、両方のサービスから stdout に Span 情報をエクスポートできることを確認しました。
次のステップ
次のステップでは、クライアント サービスとサーバー サービスを計測して、gRPC 経由でトレース コンテキストを伝播する方法を確認します。
5. gRPC の計測手法
前のステップでは、このマイクロサービスのリクエストの前半を計測しました。このステップでは、クライアント サービスとサーバー サービス間の gRPC 通信を計測します。(下の図の緑と紫の長方形)

gRPC クライアントの事前構築済み計測
OpenTelemetry のエコシステムには、デベロッパーがアプリケーションを計測化するのに役立つ便利なライブラリが多数用意されています。前のステップでは、net/http パッケージの事前ビルド済み計測を使用しました。このステップでは、gRPC を介してトレース コンテキストを伝播しようとしているため、そのためのライブラリを使用します。
まず、otelgrpc という名前の事前構築済み gRPC パッケージをインポートします。
step0/src/client/main.go
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"time"
"opentelemetry-trace-codelab-go/client/shakesapp"
// step2. add prebuilt gRPC package (otelgrpc)
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
今回は、クライアント サービスがサーバー サービスに対する gRPC クライアントであるため、gRPC クライアントを計測する必要があります。mustConnGRPC 関数を見つけて、クライアントがサーバーにリクエストを行うたびに新しいスパンを計測する gRPC インターセプタを追加します。
step0/src/client/main.go
// Helper function for gRPC connections: Dial and create client once, reuse.
func mustConnGRPC(ctx context.Context, conn **grpc.ClientConn, addr string) {
var err error
// step2. add gRPC interceptor
interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
*conn, err = grpc.DialContext(ctx, addr,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor(interceptorOpt)),
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor(interceptorOpt)),
grpc.WithTimeout(time.Second*3),
)
// step2: end adding interceptor
if err != nil {
panic(fmt.Sprintf("Error %s grpc: failed to connect %s", err, addr))
}
}
前のセクションで OpenTelemetry をすでに設定しているため、この操作は必要ありません。
gRPC サーバーの事前構築済み計測
gRPC クライアントの場合と同様に、gRPC サーバーの事前構築済み計測を呼び出します。次のように、インポート セクションに新しいパッケージを追加します。
step0/src/server/main.go
import (
"context"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"regexp"
"strings"
"opentelemetry-trace-codelab-go/server/shakesapp"
"cloud.google.com/go/storage"
// step2. add OpenTelemetry packages including otelgrpc
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
"google.golang.org/grpc"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
)
サーバーを計測するのは今回が初めてなので、loadgen サービスとクライアント サービスの場合と同様に、まず OpenTelemetry を設定する必要があります。
step0/src/server/main.go
// step2. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// create a stdout exporter to show collected spans out to stdout.
exporter, err := stdout.New(stdout.WithPrettyPrint())
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
func main() {
...
// step2. setup OpenTelemetry
tp, err := initTracer()
if err != nil {
log.Fatalf("failed to initialize TracerProvider: %v", err)
}
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
log.Fatalf("error shutting down TracerProvider: %v", err)
}
}()
// step2. end setup
...
次に、サーバー インターセプタを追加する必要があります。main 関数で、grpc.NewServer() が呼び出される場所を見つけて、関数にインターセプタを追加します。
step0/src/server/main.go
func main() {
...
svc := NewServerService()
// step2: add interceptor
interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
srv := grpc.NewServer(
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(interceptorOpt)),
grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(interceptorOpt)),
)
// step2: end adding interceptor
shakesapp.RegisterShakespeareServiceServer(srv, svc)
...
マイクロサービスを実行してトレースを確認する
次に、skaffold コマンドを使用して変更したコードを実行します。
skaffold dev
stdout に大量のスパン情報が表示されます。
コマンド出力
...
[server] {
[server] "Name": "shakesapp.ShakespeareService/GetMatchCount",
[server] "SpanContext": {
[server] "TraceID": "89b472f213a400cf975e0a0041649667",
[server] "SpanID": "96030dbad0061b3f",
[server] "TraceFlags": "01",
[server] "TraceState": "",
[server] "Remote": false
[server] },
[server] "Parent": {
[server] "TraceID": "89b472f213a400cf975e0a0041649667",
[server] "SpanID": "cd90cc3859b73890",
[server] "TraceFlags": "01",
[server] "TraceState": "",
[server] "Remote": true
[server] },
[server] "SpanKind": 2,
[server] "StartTime": "2022-07-14T14:05:55.74822525Z",
[server] "EndTime": "2022-07-14T14:06:03.449258891Z",
[server] "Attributes": [
...
[server] ],
[server] "Events": [
[server] {
[server] "Name": "message",
[server] "Attributes": [
...
[server] ],
[server] "DroppedAttributeCount": 0,
[server] "Time": "2022-07-14T14:05:55.748235489Z"
[server] },
[server] {
[server] "Name": "message",
[server] "Attributes": [
...
[server] ],
[server] "DroppedAttributeCount": 0,
[server] "Time": "2022-07-14T14:06:03.449255889Z"
[server] }
[server] ],
[server] "Links": null,
[server] "Status": {
[server] "Code": "Unset",
[server] "Description": ""
[server] },
[server] "DroppedAttributes": 0,
[server] "DroppedEvents": 0,
[server] "DroppedLinks": 0,
[server] "ChildSpanCount": 0,
[server] "Resource": [
[server] {
...
[server] ],
[server] "InstrumentationLibrary": {
[server] "Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
[server] "Version": "semver:0.33.0",
[server] "SchemaURL": ""
[server] }
[server] }
...
スパン名を埋め込んでおらず、trace.Start() または span.SpanFromContext() を使用してスパンを手動で作成したことに気づきます。ただし、gRPC インターセプタによって生成されたスパンが多数存在します。
概要
このステップでは、OpenTelemetry エコシステム ライブラリのサポートを使用して、gRPC ベースの通信を計測しました。
次のステップ
次のステップでは、Cloud Trace を使用してトレースを可視化し、収集したスパンを分析する方法を学びます。
6. Cloud Trace でトレースを可視化する
OpenTelemetry を使用してシステム全体のトレースを計測しました。これまでに、HTTP サービスと gRPC サービスを計測する方法を学習しました。計測方法については学習しましたが、分析方法についてはまだ学習していません。このセクションでは、stdout エクスポータを Cloud Trace エクスポータに置き換え、トレースを分析する方法について説明します。
Cloud Trace エクスポータを使用する
OpenTelemetry の強力な特徴の一つは、プラグイン性です。インストルメンテーションによって収集されたすべてのスパンを可視化するには、stdout エクスポータを Cloud Trace エクスポータに置き換えるだけです。
各サービスの main.go ファイルを開き、initTracer() 関数を見つけます。行を削除して stdout エクスポーターを生成し、代わりに Cloud Trace エクスポーターを作成します。
step0/src/loadgen/main.go
import (
...
// step3. add OpenTelemetry for Cloud Trace package
cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
)
// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// step3. replace stdout exporter with Cloud Trace exporter
// cloudtrace.New() finds the credentials to Cloud Trace automatically following the
// rules defined by golang.org/x/oauth2/google.findDefaultCredentailsWithParams.
// https://pkg.go.dev/golang.org/x/oauth2/google#FindDefaultCredentialsWithParams
exporter, err := cloudtrace.New()
// step3. end replacing exporter
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
クライアント サービスとサーバー サービスでも同じ関数を編集する必要があります。
マイクロサービスを実行してトレースを確認する
編集後、skaffold コマンドを使用して通常どおりクラスタを実行します。
skaffold dev
エクスポートを Cloud Trace のものに置き換えたため、stdout の構造化ログ形式でスパン情報が表示されなくなりました。
コマンド出力
[loadgen] 2022/07/14 15:01:07 simulated 20 requests
[loadgen] 2022/07/14 15:01:07 simulating client requests, round 37
[loadgen] 2022/07/14 15:01:14 query 'sweet': matched 958
[client] 2022/07/14 15:01:14 {"match_count":958}
[client] 2022/07/14 15:01:14 {"match_count":3040}
[loadgen] 2022/07/14 15:01:14 query 'love': matched 3040
[client] 2022/07/14 15:01:15 {"match_count":349}
[loadgen] 2022/07/14 15:01:15 query 'hello': matched 349
[client] 2022/07/14 15:01:15 {"match_count":484}
[loadgen] 2022/07/14 15:01:15 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:15 query 'insolence': matched 14
[client] 2022/07/14 15:01:15 {"match_count":14}
[client] 2022/07/14 15:01:21 {"match_count":484}
[loadgen] 2022/07/14 15:01:21 query 'faith': matched 484
[client] 2022/07/14 15:01:21 {"match_count":728}
[loadgen] 2022/07/14 15:01:21 query 'world': matched 728
[client] 2022/07/14 15:01:22 {"match_count":484}
[loadgen] 2022/07/14 15:01:22 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:22 query 'hello': matched 349
[client] 2022/07/14 15:01:22 {"match_count":349}
[client] 2022/07/14 15:01:23 {"match_count":1036}
[loadgen] 2022/07/14 15:01:23 query 'friend': matched 1036
[loadgen] 2022/07/14 15:01:28 query 'tear': matched 463
...
すべてのスパンが Cloud Trace に正しく送信されていることを確認します。Cloud コンソールにアクセスし、[トレースリスト] に移動します。検索ボックスから簡単にアクセスできます。左側のペインでメニュー アイコン
をクリックすることもできます。
遅延グラフ全体に青い点が散らばっています。各スポットは 1 つのトレースを表します。

いずれかをクリックすると、トレース内の詳細が表示されます。
この簡単な概要だけでも、多くの分析情報を把握できます。たとえば、ウォーターフォール グラフから、レイテンシの原因のほとんどが shakesapp.ShakespeareService/GetMatchCount という名前のスパンであることがわかります。(上の図の 1 を参照)概要の表で確認できます。(右端の列には、各スパンの期間が表示されます)。また、このトレースは「friend」というクエリのものです。(上の画像の 2 を参照)
これらの簡単な分析を行うと、GetMatchCount メソッド内のより詳細なスパンを知る必要があることがわかります。stdout 情報と比較して、ビジュアリゼーションは強力です。Cloud Trace の詳細については、公式ドキュメントをご覧ください。
概要
このステップでは、stdout エクスポーターを Cloud Trace エクスポーターに置き換え、Cloud Trace でトレースを可視化しました。また、トレースの分析を開始する方法についても学習しました。
次のステップ
次のステップでは、サーバー サービスのソースコードを変更して、GetMatchCount にサブスパンを追加します。
7. 分析を改善するためにサブスパンを追加
前の手順では、loadgen から観測されたラウンド トリップ時間の原因のほとんどは、サーバー サービスの GetMatchCount メソッド内のプロセス(gRPC ハンドラ)であることがわかりました。ただし、ハンドラ以外のものは計測していないため、ウォーターフォール グラフからさらに分析情報を得ることはできません。これは、マイクロサービスの計測を開始する際の一般的なケースです。

このセクションでは、サーバーが Google Cloud Storage を呼び出すサブスパンを計測します。これは、プロセスで外部ネットワーク I/O に時間がかかることがよくあり、呼び出しが原因かどうかを特定することが重要であるためです。
サーバーでサブスパンをインストルメント化する
サーバーで main.go を開き、関数 readFiles を見つけます。この関数は、Google Cloud Storage にリクエストを送信して、シェイクスピアの作品のすべてのテキスト ファイルを取得します。この関数では、クライアント サービスの HTTP サーバー計測で行ったように、サブスパンを作成できます。
step0/src/server/main.go
func readFiles(ctx context.Context, bucketName, prefix string) ([]string, error) {
type resp struct {
s string
err error
}
// step4: add an extra span
span := trace.SpanFromContext(ctx)
span.SetName("server.readFiles")
span.SetAttributes(attribute.Key("bucketname").String(bucketName))
defer span.End()
// step4: end add span
...
これで、新しいスパンの追加は完了です。アプリを実行して、その様子を見てみましょう。
マイクロサービスを実行してトレースを確認する
編集後、skaffold コマンドを使用して通常どおりクラスタを実行します。
skaffold dev
トレースリストから query.request という名前のトレースを 1 つ選択します。shakesapp.ShakespeareService/GetMatchCount の下に新しいスパンが表示される以外は、同様のトレース ウォーターフォール グラフが表示されます。(下の赤い長方形で囲まれた範囲)

このグラフからわかるのは、Google Cloud Storage への外部呼び出しがレイテンシの大部分を占めているものの、他の要因もレイテンシの大部分を占めているということです。
トレース ウォーターフォール グラフのいくつかのルックから、すでに多くの分析情報を得ています。アプリケーションでパフォーマンスの詳細情報を取得するにはどうすればよいですか?ここでプロファイラが登場しますが、ここではこの Codelab を終了し、プロファイラ チュートリアルはすべてパート 2 に委任します。
概要
このステップでは、サーバー サービスの別のスパンを計測し、システム レイテンシに関する詳細な分析情報を取得しました。
8. 完了
OpenTelemery で分散トレースを作成し、Google Cloud Trace でマイクロサービス全体のリクエスト レイテンシを確認しました。
拡張演習については、次のトピックを各自で試してください。
- 現在の実装では、ヘルスチェックによって生成されたすべてのスパンが送信されます。(
grpc.health.v1.Health/Check)Cloud Trace からこれらのスパンを除外するにはどうすればよいですか?ヒントはこちらをご覧ください。 - イベントログとスパンを関連付け、Google Cloud Trace と Google Cloud Logging での動作を確認します。ヒントはこちらをご覧ください。
- 一部のサービスを別の言語のサービスに置き換え、その言語の OpenTelemetry で計測してみます。
また、この後でプロファイラについて学習する場合は、パート 2 に進んでください。その場合は、以下のクリーンアップ セクションをスキップできます。
クリーンアップ
この Codelab の後、Kubernetes クラスタを停止し、プロジェクトを削除して、Google Kubernetes Engine、Google Cloud Trace、Google Artifact Registry で予期しない料金が発生しないようにしてください。
まず、クラスタを削除します。skaffold dev でクラスタを実行している場合は、Ctrl+C キーを押すだけです。skaffold run でクラスタを実行している場合は、次のコマンドを実行します。
skaffold delete
コマンド出力
Cleaning up... - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
クラスタを削除したら、メニュー ペインから [IAM と管理] > [設定] を選択し、[シャットダウン] ボタンをクリックします。

次に、ダイアログのフォームにプロジェクト ID(プロジェクト名ではない)を入力し、シャットダウンを確認します。