🔍

Grafana Faro, Tempo, Loki を kubernetes 上で連携してみる

2023/04/28に公開

はじめに

Grafana Labs から Grafana Faro というリアルユーザモニタリング (RUM) の OSS が ObservabilityCon 2022 で発表されました
Grafana Labs 製品が増えてきたことで、Observability のかなりの範囲がカバーされているのではないかと思い、Grafana Labs 製品の組み合わせでどこまで実現できるようになったのかが気になり、この機会に試しにいくつか連携させて使ってみることにしました。

本記事は、試しに製品間を連携させてみることが主眼であるため、下記にご注意ください:

  • 執筆している 2023年4月28日時点の情報に基づいています。
  • 簡単のため、実環境には適さない構成をとっています。
  • Grafana Faro をはじめとして、開発中の機能を多く使用するため、今後変更が予想されます。

Grafana Faro の概要

Grafana Faro は、RUM を実現するための製品です。
RUM とは、ユーザが Web ブラウザ等で実際に体験した情報を収集することを指します。
リソースの取得タイミングや、ページ描画のタイミングなどを取得することで、サーバ側のモニタリングだけでは得られない情報を得ることが目的です。

Web ページにおける RUM では JavaScript のライブラリが使用されます。
Web ページがブラウザに読み込まれ、JavaScript が動作すると、自動的にデータを収集し、監視サーバに転送する仕組みです。
Grafana Faro はこの JavaScript のライブラリに当たり、NPM のパッケージとして配布されています。
また、CDN にホストされるライブラリとして使用する方法も用意されています。
なお、今回は CDN から利用する構成を取りました。

データ転送先となる監視サーバとしては、Grafana Cloud と、Grafana Agent がサポートされていました。
Grafana Agent からはログを Grafana Loki に、トレースを Grafana Tempo に送ることができました。
今回は、Grafana Agent, Loki, Tempo を使った構成で連携してみました。

+--------------+  データ    +---------------+  ログ    +--------------+
| Web ブラウザ | ---------> | Grafana Agent | -------> | Grafana Loki |
+--------------+            +---------------+          +--------------+
                                   |
                                   | トレース
                                   v
                            +---------------+
                            | Grafana Tempo |
                            +---------------+

kubernetes の用意

今回は Windows 上で Rancher Desktop を使用しました。
Rancher Desktop のバージョンは 1.8.1、kubernetes のバージョンは 1.25 になっていました。

Minio の構築

Grafana Loki と Grafana Tempo は、データの保存にオブジェクトストレージを使用します。
今回は簡単のため Minio を使用しました。

Minio は Helm チャートが用意されているため、それを使用します。
下記パラメータをファイルで用意します。

minio.yaml
mode: standalone

# root ですが、簡単のためこれをアクセスキーとして使います。
rootUser: root
rootPassword: supersecret

buckets:
- name: tempo-traces # Grafana Tempo 用です。
  policy: none
  purge: false
- name: chunks # Grafana Loki 用です。
  policy: none
  purge: false
- name: ruler # Grafana Loki 用です。
  policy: none
  purge: false
- name: admin # Grafana Loki 用です。
  policy: none
  purge: false

# Rancher Desktop の kubernetes ではローカルストレージの PV が使用されます。
persistence: 
  size: 5Gi

# デフォルト値だとメモリが大きくて起動できない場合があるため、適当に減らします。
resources:
  requests:
    cpu: 100m
    memory: 128Mi

下記コマンドで Minio を kubernetes 上に作成します。

helm repo add minio https://charts.min.io/
helm repo update
helm upgrade -i minio minio/minio -f minio.yaml

Grafana Loki の構築

Minio と同様に Helm チャートが用意されているため、それを使用します。

下記パラメータをファイルで用意します。

loki.yaml
loki:
  auth_enabled: false # 簡単のため、認証は無効化します。
  commonConfig:
    replication_factor: 1
  storage:
    bucketNames: # Minio で作成したバケットを指定します。
      chunks: chunks
      ruler: ruler
      admin: admin
    type: s3
    s3:
      # root ユーザとして作成したキーを指定します。
      accessKeyId: root
      secretAccessKey: supersecret

      endpoint: minio:9000 # Helm チャートで作成された Service を指定します。
      insecure: true # Minio に HTTP で接続するため必要な設定です。
      s3ForcePathStyle: true # Minio で必要な設定です。
monitoring:
  selfMonitoring: # Grafana Agent は別途インストールするため、無効化します。
    enabled: false
    grafanaAgent:
      installOperator: false
  lokiCanary: # 今回試す範囲では不要なため、無効化します。
    enabled: false
write:
  replicas: 1 # ノードが1台でも動くようにします。
read:
  replicas: 1 # ノードが1台でも動くようにします。
backend:
  replicas: 1 # ノードが1台でも動くようにします。
test:
  enabled: false # 今回試す範囲では不要なため、無効化します。

下記コマンドで Grafana Loki を kubernetes 上に作成します。

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm upgrade -i loki grafana/loki -f loki.yaml

Grafana Tempo の構築

Minio や Grafana Loki と同様に Helm チャートが用意されているため、それを使用します。

下記パラメータをファイルで用意します。

tempo.yaml
gateway:
  enabled: true
storage:
  trace:
    backend: s3
    s3:
      # root ユーザとして作成したキーを指定します。
      access_key: root
      secret_key: supersecret

      bucket: tempo-traces # Minio で作成したバケットを指定します。
      endpoint: minio:9000 # Helm チャートで作成された Service を指定します。
      insecure: true # Minio に HTTP で接続するため必要な設定です。
traces:
  otlp:
    grpc:
      enabled: true
    http:
      enabled: true
ingester:
  replicas: 1
  config:
    replication_factor: 1 # 1台しかなくても Grafana Agent からトレースを受け取れるように設定します。

下記コマンドで Grafana Tempo を kubernetes 上に作成します。

helm upgrade -i tempo grafana/tempo-distributed -f tempo.yaml

Grafana Agent の構築

やはり Helm チャートが用意されているため、それを使用します。

下記パラメータをファイルで用意します。
GitHub のドキュメントを参考に設定しているのですが、
不必要な設定もあるかもしれません。

grafana-agent.yaml
agent:
  extraArgs: # Grafana Faro からデータを受け付けるための実験的な機能を有効化します。
  - -enable-features=integrations-next
  mode: static # YAML 形式で設定できるように指定します。
  configMap:
    content: |
      metrics: # ちょっと必要性が分かっていません。
        wal_directory: /tmp/wal
        global: {}
        configs:
        - name: default
          # 参考ドキュメントでは remote_write もあるのですが、なくても動いたのでとりあえず消しています。
      logs:
        positions_directory: /tmp/loki-pos
        configs:
        - name: default
          scrape_configs: []
          clients:
          - url: http://loki-gateway/loki/api/v1/push # Grafana Loki で作成した Service を指定します。
      traces:
        configs:
        - batch:
            send_batch_size: 1000
            timeout: 5s
          name: default
          receivers:
            otlp:
              protocols:
                http:
                  cors:
                    allowed_origins:
                    - http://*
                    max_age: 7200
          remote_write:
          - endpoint: tempo-distributor:4317 # Grafana Tempo で作成した Service を指定します。
            insecure: true
          scrape_configs: # ちょっと必要性が分かっていません。
          - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
            job_name: kubernetes-pods
            kubernetes_sd_configs:
            - role: pod
            relabel_configs:
            - action: replace
              source_labels:
              - __meta_kubernetes_namespace
              target_label: namespace
            - action: replace
              source_labels:
              - __meta_kubernetes_pod_name
              target_label: pod
            - action: replace
              source_labels:
              - __meta_kubernetes_pod_container_name
              target_label: container
            tls_config:
                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
                insecure_skip_verify: false
      integrations:
        app_agent_receiver_configs:
        - autoscrape:
            enable: true
            metrics_instance: default
          instance: frontend
          logs_instance: default
          traces_instance: default
          server: # ここで Grafana Faro からのデータを受け付けます。
            api_key: secret # ドキュメントと異なり場所が移動しました。
            host: 0.0.0.0
            port: 12347
            cors_allowed_origins:
            - http://*
          logs_labels:
            app: frontend
          logs_send_timeout: 5000
          sourcemaps:
            download: true

下記コマンドで Grafana Agent を kubernetes 上に作成します。

helm upgrade -i grafana-agent grafana/grafana-agent -f grafana-agent.yaml

Grafana の構築

もちろん Helm チャートが用意されているため、それを使用します。

下記パラメータをファイルで用意します。
Tempo のインストール手順に付随していたパラメータを参考にしているのですが、
不必要な設定もあるかもしれません。

grafana.yaml
grafana.ini:
  server:
    domain: grafana.cluster.local
    root_url: '%(protocol)s://%(domain)s/grafana'
    serve_from_sub_path: true

env:
  GF_AUTH_ANONYMOUS_ENABLED: true
  GF_AUTH_ANONYMOUS_ORG_ROLE: 'Admin'
  GF_AUTH_DISABLE_LOGIN_FORM: true
  GF_FEATURE_TOGGLES_ENABLE: tempoSearch,tempoServiceGraph,tempoApmTable,traceqlEditor

datasources:
  datasources.yaml:
    apiVersion: 1

    datasources:
    - name: Tempo
      type: tempo
      access: proxy
      orgId: 1
      url: http://tempo-gateway # Grafana Tempo で作成した Service を指定します。
      basicAuth: false
      isDefault: true
      version: 1
      editable: false
      apiVersion: 1
      uid: tempo
    - name: Loki
      type: loki
      access: proxy
      orgId: 1
      url: http://loki-gateway # Grafana Loki で作成した Service を指定します。
      basicAuth: false
      isDefault: false
      editable: false
      uid: loki

下記コマンドで Grafana を kubernetes 上に作成します。

helm upgrade -i grafana grafana/grafana -f grafana.yaml

Grafana と Grafana Agent へのアクセス

これまでに作成した Pod がすべて Running になっていることを確認した後、
kubectl のポート転送機能を用いて、Grafana と Grafana Agent にアクセスできるようにします。
コロンの左側に指定するローカルポート番号は、ほかのプロセスで使用していないものにする必要があります。
Grafana Agentで使用している 12347 は使用済みだったため、12345 に変更しました。

kubectl port-forward svc/grafana 3000:80 &
kubectl port-forward svc/grafana-agent 12345:12347 &

Grafana Faro の実行

CDN から利用するため、ドキュメントを参考に、下記 HTML ファイルを用意します。

index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <title>My App</title>

    <script>
        // Initialize Faro when the main bundle loads
        window.init = () => {
            window.GrafanaFaroWebSdk.initializeFaro({
                url: 'http://localhost:12345/collect', // ポート転送で開けたローカルポートを指定します。
                apiKey: 'secret', // Grafana Agent に設定したキーを指定します。
                app: {
                    name: 'test',
                },
            });
        };

        // Dynamically add the tracing instrumentation when the tracing bundle loads
        window.addTracing = () => {
            window.GrafanaFaroWebSdk.faro.instrumentations.add(new window.GrafanaFaroWebTracing.TracingInstrumentation());
        };
    </script>
</head>

<body>
    <!-- Load the bundles from unpkg -->
    <!-- Be sure to use the appropiate version instead of the one below -->
    <script src="https://unpkg.com/@grafana/faro-web-sdk@^1.0.0/dist/bundle/faro-web-sdk.iife.js"
        onload="window.init()"></script>
    <script src="https://unpkg.com/@grafana/faro-web-tracing@^1.0.0/dist/bundle/faro-web-tracing.iife.js"
        onload="window.addTracing()"></script>
</body>

</html>

CORS のため、このファイルには HTTP でアクセスしなければなりません。
今回は、VSCode の Live Preview 拡張機能を使用しました。
これでプレビューすると、適当なポート番号で HTTP サーバが立ち上がります。
開発者ツールを開くと、 http://localhost:12345/collect にデータを送っていることがわかります。

開発者ツールのネットワーク

Grafana にアクセスすると、Grafana Faro から送られてきたデータが確認できます。
ポート転送で開けたローカルポートを指定して、http://localhost:3000 に Web ブラウザでアクセスします。

Explore の画面から Tempo を選択すると、トレースが確認できます。
Query Type で Search を選択し Run Query をクリックすると、下図のようにトレースの一覧が表示されます。

トレースの一覧

トレース ID をクリックすると、下図のようにスパンが表示されます。

スパンの表示

Explore の画面から Loki を選択すると、ログが確認できます。
Label filters で app = frontend と設定した後に、Run Query をクリックすると、下図のようにログの一覧が表示されます。

ログの一覧

まとめ

今回は Grafana Faro で RUM のデータを簡単なページで取得してみました。
取得したデータは Grafana Labs の製品を連携して、可視化できることを確認しました。

実運用では、Grafana Faro に限った話ではありませんが、RUM の性質上、シークレットが Web ブラウザ上に見えてしまうため、データを受け付けるエンドポイントのパフォーマンスやセキュリティ、データの信頼性の確保が課題になりそうです。

Discussion