1. はじめに
この Codelab では、gRPC を使用して、C++ で記述されたルート マッピング アプリケーションの基盤となるクライアントとサーバーを作成します。
このチュートリアルを完了すると、gRPC OpenTelemetry プラグインで計測されたシンプルな gRPC HelloWorld アプリケーションが作成され、エクスポートされたオブザーバビリティ指標を Prometheus で確認できるようになります。
学習内容
- 既存の gRPC C++ アプリケーションに OpenTelemetry プラグインを設定する方法
- ローカル Prometheus インスタンスの実行
- Prometheus への指標のエクスポート
- Prometheus ダッシュボードから指標を表示する
2. 始める前に
必要なもの
gitcurlbuild-essentialclang- この Codelab の例をビルドする
bazel
次コマンドで前提条件をインストールします。
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl build-essential clang
bazel は bazelisk を介してインストールできます。最新バージョンはこちらで確認できます。
簡単な設定方法は、次のように PATH に bazel バイナリとしてインストールすることです。
sudo cp bazelisk-linux-amd64 /usr/local/bin/bazel
sudo chmod a+x /usr/local/bin/bazel
CMake を使用することもできます。CMake の使用手順については、こちらをご覧ください。
コードを取得する
学習を効率化するため、この Codelab では、すぐに始められるように、あらかじめ作成されたソースコード スキャフォールドが用意されています。次の手順では、アプリケーションで gRPC OpenTelemetry プラグインを計測する方法について説明します。
grpc-codelabs
この Codelab のスキャフォールディング ソースコードは、こちらの GitHub のディレクトリで入手できます。コードを自分で実装しない場合は、completed ディレクトリで完成したソースコードを利用できます。
まず、grpc codelab リポジトリのクローンを作成し、grpc-cpp-opentelemetry フォルダに移動します。
git clone https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-cpp-opentelemetry/
または、Codelab ディレクトリのみを含む .zip ファイルをダウンロードして、手動で解凍することもできます。
bazel を使用して gRPC ライブラリをビルドします。
bazel build start_here/...
3. OpenTelemetry プラグインを登録する
gRPC OpenTelemetry プラグインを追加するには、gRPC アプリケーションが必要です。この Codelab では、gRPC OpenTelemetry プラグインでインストルメンテーションするシンプルな gRPC HelloWorld クライアントとサーバーを使用します。
まず、クライアントで Prometheus エクスポータで構成された OpenTelemetry プラグインを登録します。任意のエディタで codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_client.cc を開き、main() を次のように変換します。
int main(int argc, char **argv) {
absl::ParseCommandLine(argc, argv);
// Codelab Solution: Register a global gRPC OpenTelemetry plugin configured
// with a prometheus exporter.
opentelemetry::exporter::metrics::PrometheusExporterOptions opts;
opts.url = absl::GetFlag(FLAGS_prometheus_endpoint);
auto prometheus_exporter =
opentelemetry::exporter::metrics::PrometheusExporterFactory::Create(opts);
auto meter_provider =
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
// The default histogram boundaries are not granular enough for RPCs. Override
// the "grpc.client.attempt.duration" view as recommended by
// https://github.com/grpc/proposal/blob/master/A66-otel-stats.md.
AddLatencyView(meter_provider.get(), "grpc.client.attempt.duration", "s");
meter_provider->AddMetricReader(std::move(prometheus_exporter));
auto status = grpc::OpenTelemetryPluginBuilder()
.SetMeterProvider(std::move(meter_provider))
.BuildAndRegisterGlobal();
if (!status.ok()) {
std::cerr << "Failed to register gRPC OpenTelemetry Plugin: "
<< status.ToString() << std::endl;
return static_cast<int>(status.code());
}
// Continuously send RPCs.
RunClient(absl::GetFlag(FLAGS_target));
return 0;
}
次のステップでは、OpenTelemetry プラグインをサーバーに追加します。codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_server.cc を開き、main を次のように変換します。
int main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
// Register a global gRPC OpenTelemetry plugin configured with a prometheus
// exporter.
opentelemetry::exporter::metrics::PrometheusExporterOptions opts;
opts.url = absl::GetFlag(FLAGS_prometheus_endpoint);
auto prometheus_exporter =
opentelemetry::exporter::metrics::PrometheusExporterFactory::Create(opts);
auto meter_provider =
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
// The default histogram boundaries are not granular enough for RPCs. Override
// the "grpc.server.call.duration" view as recommended by
// https://github.com/grpc/proposal/blob/master/A66-otel-stats.md.
AddLatencyView(meter_provider.get(), "grpc.server.call.duration", "s");
meter_provider->AddMetricReader(std::move(prometheus_exporter));
auto status = grpc::OpenTelemetryPluginBuilder()
.SetMeterProvider(std::move(meter_provider))
.BuildAndRegisterGlobal();
if (!status.ok()) {
std::cerr << "Failed to register gRPC OpenTelemetry Plugin: "
<< status.ToString() << std::endl;
return static_cast<int>(status.code());
}
RunServer(absl::GetFlag(FLAGS_port));
return 0;
}
必要なヘッダー ファイルとビルド依存関係は、便宜上すでに追加されています。
#include "opentelemetry/exporters/prometheus/exporter_factory.h"
#include "opentelemetry/exporters/prometheus/exporter_options.h"
#include "opentelemetry/sdk/metrics/meter_provider.h"
#include <grpcpp/ext/otel_plugin.h>
ビルドの依存関係も BUILD ファイルにすでに追加されています。
cc_binary(
name = "greeter_callback_client",
srcs = ["greeter_callback_client.cc"],
defines = ["BAZEL_BUILD"],
deps = [
"//util:util",
"@com_github_grpc_grpc//:grpc++",
"@com_github_grpc_grpc//:grpcpp_otel_plugin",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/flags:parse",
"@io_opentelemetry_cpp//exporters/prometheus:prometheus_exporter",
"@io_opentelemetry_cpp//sdk/src/metrics",
],
)
4. 例を実行して指標を表示する
サーバーを実行するには、次のコマンドを実行します。
bazel run start_here:greeter_callback_server
設定が成功すると、サーバーに次の出力が表示されます。
Server listening on 0.0.0.0:50051
サーバーが実行されている間に、別のターミナルでクライアントを実行します。
bazel run start_here:greeter_callback_client
実行が成功すると、次のようになります。
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
gRPC OpenTelemetry プラグインは、Prometheus を使用して指標をエクスポートするように設定されているためです。これらの指標は、サーバーの場合は localhost:9464、クライアントの場合は localhost:9465 で利用できます。
クライアント指標を表示するには -
curl localhost:9465/metrics
結果は次の形式になります。
# HELP exposer_transferred_bytes_total Transferred bytes to metrics services
# TYPE exposer_transferred_bytes_total counter
exposer_transferred_bytes_total 0
# HELP exposer_scrapes_total Number of times metrics were scraped
# TYPE exposer_scrapes_total counter
exposer_scrapes_total 0
# HELP exposer_request_latencies Latencies of serving scrape requests, in microseconds
# TYPE exposer_request_latencies summary
exposer_request_latencies_count 0
exposer_request_latencies_sum 0
exposer_request_latencies{quantile="0.5"} Nan
exposer_request_latencies{quantile="0.9"} Nan
exposer_request_latencies{quantile="0.99"} Nan
# HELP target Target metadata
# TYPE target gauge
target_info{otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",service_name="unknown_service",telemetry_sdk_version="1.13.0",telemetry_sdk_name="opentelemetry",telemetry_sdk_language="cpp"} 1 1721958543107
# HELP grpc_client_attempt_rcvd_total_compressed_message_size_bytes Compressed message bytes received per call attempt
# TYPE grpc_client_attempt_rcvd_total_compressed_message_size_bytes histogram
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev"} 96 1721958543107
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev"} 1248 1721958543107
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="0"} 0 1721958543107
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="5"} 0 1721958543107
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="10"} 0 1721958543107
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="25"} 96 1721958543107
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="50"} 96 1721958543107
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",le="75"} 96 1721958543107
同様に、サーバーサイドの指標の場合 -
curl localhost:9464/metrics
5. Prometheus で指標を表示する
ここでは、Prometheus を使用して指標をエクスポートする gRPC サンプル クライアントとサーバーをスクレイピングする Prometheus インスタンスを設定します。
ご利用のプラットフォーム用の Prometheus の最新リリースをダウンロードし、展開して実行します。
tar xvfz prometheus-*.tar.gz
cd prometheus-*
次の内容で Prometheus 構成ファイルを作成します。
cat > grpc_otel_cpp_prometheus.yml <<EOF
scrape_configs:
- job_name: "prometheus"
scrape_interval: 5s
static_configs:
- targets: ["localhost:9090"]
- job_name: "grpc-otel-cpp"
scrape_interval: 5s
static_configs:
- targets: ["localhost:9464", "localhost:9465"]
EOF
新しい構成で Prometheus を起動します。
./prometheus --config.file=grpc_otel_cpp_prometheus.yml
これにより、クライアントとサーバーの Codelab プロセスから 5 秒ごとに指標がスクレイピングされるように構成されます。
http://localhost:9090/graph に移動して、指標を表示します。たとえば、次のクエリがあるとします。
histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))
は、分位値の計算に 1 分間のタイム ウィンドウを使用して、試行レイテンシの中央値を示すグラフを表示します。
クエリのレート -
increase(grpc_client_attempt_duration_seconds_bucket[1m])
6. (省略可)ユーザー向けの演習
Prometheus ダッシュボードで、QPS が低いことがわかります。QPS を制限している疑わしいコードを特定できるかどうか、サンプルで確認します。
熱心なユーザーのために、クライアント コードは、特定の時点で保留中の RPC を 1 つだけにするように制限されています。これを変更して、クライアントが前の RPC の完了を待たずに、より多くの RPC を送信するようにできます。(この解決策は提供されていません)。