🩺

eBPF ベースの計測ツール Grafana Beyla を Rust で試す

2023/12/03に公開

本記事はLabBase テックカレンダー Advent Calendar 2023 3 日目です。
https://qiita.com/advent-calendar/2023/labbase

Grafana Beyla

Grafana Beyla は eBPF を利用して、Web アプリケーションの trace やメトリクス情報を収集するツールです。
eBPF を利用しているため、アプリケーションのコードを変更することなく、trace やメトリクス情報を収集することができます。
似たようなツールとして、Pixie があります。

今回は Pixie については触れないため、詳細については他の方にて紹介されているので参考にしてください。

eBPF を利用して、Linux カーネル関数の呼び出しをもとに trace やメトリクス情報を収集するため、収集対象の Web アプリケーションの実装言語の縛りは実質ありません。
Grafana Beyla のドキュメントにも対応言語は、"Go, C/C++, Rust, Python, Ruby, Java (including GraalVM Native), NodeJS, .NET, and others" とあります

しかしながら、現在 Grafana Beyla を使うには Linux 上でしか動作せず、また BTF が有効化された Kernel 4.18 以上である必要があります。

機能

以下のような機能があります。

  • HTTP/HTTPS, gRPC の RED メトリクス(リクエストレート、エラーレート)の収集、trace の発行
  • 収集したデータの OpenTelemetry(trace, metrics) format もしくは Prometheus metrics としてのエクスポート
  • Kubernetes API とインテグレーションして、メトリクスと trace にコンテキストの追加

Rust HTTP サーバーで試す

公式のチュートリアルでは Go を利用していたため、Rust を利用して Grafana Beyla を試してみます。
GitHub 上では Rust を含めて、 C/C++, Python, Ruby, Java, NodeJS, .NET の例があります。

コードは https://github.com/tyrwzl/beyla-rust/tree/main/basic で公開しています。
今回は actix-web を利用して HTTP サーバーを起動しています。

use std::io;

use actix_web::{web, App, HttpServer};

async fn hello() -> &'static str {
    "Hello world! from beyla-rust"
}


#[actix_web::main]
async fn main() -> io::Result<()> {

    HttpServer::new(move || {
        App::new()
            .service(web::resource("/hello").to(hello))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await?;

    Ok(())
}

ローカル PC 上で HTTP サーバーを起動した後に、Grafana Beyla を起動します。
Grafana Beyla のバイナリは GitHub のリリースページにあります。

sudo ./beyla --config ./config.yaml

ここで、config.yaml という Grafana Beyla の設定ファイルを利用しています。
中身は下記のようになっていて、open_port で計測対象の HTTP サーバーが listen しているポートを指定します。
また、今回は Grafana Cloud に対して trace とメトリクスを送信しています。

open_port: 8080
grafana:
  otlp:
    cloud_submit:
      - traces
      - metrics
    cloud_zone: prod-ap-southeast-1
    cloud_instance_id: ******
    cloud_api_key: **********

設定内容の詳細や他の利用できるものは、ドキュメントに記載されいてます。

続いて、HTTP サーバーに対して HTTP リクエストを送信します。

curl localhost:8080/hello

その後、Grafana Cloud を確認すると以下のように trace 情報や RED metrics が収集されていることがわかります。

trace
metrics

コードに変更を加えずtrace 情報や metrics が収集できました。

total request times

ドキュメントで言及されていますが、Grafana Beyla は eBPF ベースの計測ツールであるため、HTTP リクエストのレスポンスタイムをよりユーザーが感じているレスポンスタイムとして計測できる、としています。

UserSpace で計測したレスポンスタイムは、Kernel 上での処理時間が含まれないため、高負荷状態などの原因で Kernel 上でのレイテンシが増えたケースを見過ごしてしまうおそれがあります。

実際に、Rust アプリケーション上で tracing 情報を出力て、かつ、Grafana Beyla でも tracing 情報を出力してみます。
コードは: https://github.com/tyrwzl/beyla-rust/tree/main/total_request_times

Grafana Beyla では traceparent リクエストヘッダーをもとに Trace Context を構築できるため、下記のように traceparent ヘッダを付与することで同じ trace 情報として統合できます。

curl  http://localhost:8080/hello -H "traceparent: 00-1af92f3577b34da6a3ce929d0e0e0000-00f067aa0ba902a8-01"

また、今回は jaeger に trace 情報を送信してみました。
config は次のようになります。

open_port: 8080
otel_traces_export:
  endpoint: http://localhost:4318
ebpf:
  track_request_headers: true

traceparent リクエストヘッダーをもとに Trace Context
を構築できるため、track_request_headerstrue にする必要があります。

trace 情報は下記のようになりました。

total request times

ここで分かる通り、Grafana Beyla 上では 892マイクロ秒ですが、Rust アプリケーション上では 326マイクロ秒と報告されており、乖離していることがわかります。

まとめ

Grafana Beyla を利用して eBPF ベースの計測を試してみました。
コードに変更を加えずに trace や RED メトリクスを収集できるのですぐに利用することができます。
また、Kernel 上での計測となるため、特定の条件下ではより正確な計測をすることができます。

Go 言語で作成された HTTP サーバーの方が計測できる項目は多く、例えばサーバーから発行された SQL についても trace 情報を取得することができます。
Rust などの他の言語でも利用できるようになれば良いと思いつつ、今後の機能開発について見ていきたいと思います。

明日は https://qiita.com/yiwi さんです。お楽しみに!

Discussion