Grafana の Backend plugin を利用して BigQuery と連携した監視をする

はじめに

こんにちは、UZABASE SREの鈴木(@sshota0809)です。

今回は、Grafana の Backend plugin という仕組みを利用して、データソースを BigQuery とした監視設定を行う方法を紹介します。

目次

TL;DR

  • Grafana v7.0 から正式に Backend plugin という仕組みに対応した

  • この仕組みを利用することで、任意のデータソースから得たメトリクスを利用した監視を実現することが可能1

  • Backend plugin を利用することで Grafana 上で BigQuery をデータソースにした監視設定を実現した

はじめに

現在のログ収集構成

SPEEDA ではユーザがサービスにアクセスする際、まずはじめに GCP の GCLB を経由して Kong にアクセスを行います。そして、Kong から各マイクロサービスやモノリスなアプリケーションに HTTP Path ベースでアクセスの振り分けを行っています。また、 この Kong は GKE 上で稼働しているためアクセスログは自動的に Stackdriver に送信されます。

弊社では、更に Stackdriver に送信された GKE のログを BigQuery にインポートしています。BigQuery にログインポートを行うことで、各 Path へのアクセス統計の分析や HTTP ステータスコードの分析などに利用していました。

やりたかったこと

SRE チーム内で、サービス全体の監視をもっと最適化できないか議論をしていたところ、ユーザがアクセスをする入り口となる Kong5xx エラー の数や割合をもとにした監視を行えないか、という話になりました。

現状、Kubernetes 上で運用している各マイクロサービスに関しては Istio を利用しているため Istio 経由で収集されるメトリクスの監視を行うことで、SLO/SLI に関わる監視は比較的簡単に行うことができます。しかし、Kong が稼働しているクラスタは現状各マイクロサービスが稼働しているクラスタとは別の インフラリソース専用クラスタ といった役割のクラスタで動作させており、このクラスタでは Istio を利用していませんでした。2

なので、上記のような監視をどのように実現するかは検討する必要がありました。

前述した通り、BigQuery にすでにアクセスログを送信しているため、BigQuery でアラート設定を行うことができれば一番簡単だったのですが、BigQuery にはクエリ実行結果を元にして閾値を超えた場合に通知設定をする、といった機能はありません。

Elasticsearch を利用するという案も上がりましたがすでに BigQuery にログを送信している中で、アラート監視のためだけにElastic Search にもログを送るのもコスト対効果が低のでは3という結論に至り、BigQuery をベースにどうにか監視設定する方向で検討することになりました。

Grafana Backend plugin を利用した BigQuery ベースの監視設定

Grafana と Backend plugin

Grafana

Grafana とは Grafana Labs から公開されているダッシュボードツールです。Prometheus や MySQL、CloudWatch など様々なデータソースから取得したメトリクスを元にグラフ描画などのビジュアライズができます。

github.com

また、Grafana にはアラート機能が実装されており、閾値を超えた場合に Slack などにアラートを発報することができます。

grafana.com

しかし、上記のページにも記載されている通り、アラート機能が対応しているのは現状特定のデータソース4から取得したメトリクスのみとなっています。

Backend plugin

Grafana は第三者が自由にパネルやデータソースのプラグインを作成することで、機能を拡張できるプラグインの仕組みがあります。しかし前述の通り、Grafana はダッシュボードツールであるため、ユーザがブラウザから Grafana にアクセスをしてグラフなどを参照する、という使われ方になります。そのため、このプラグインもクライアントサイドで実行される仕組みとなっていました。

一方、メトリクス監視などは当然ユーザがブラウザでアクセスしたタイミングで閾値を超えていないかチェックするのではなく、バックエンドで決められたスパンでシステムが継続的に行われる状態が理想だと思います。しかし、Grafana のプラグインはクライアントサイドで実行されるがゆえ、このようなバックエンドでの継続的な動作ができませんでした。

これらを含む課題を解決するために新たに登場したのが Backend plugin です。ちなみに、前述した課題については Backend Plugin のページでも語られています。

grafana.com

Backend plugin は Grafana v7.0 から正式にサポートされるようになりました。Backend plugin はその名の通り、クライアントサイドではなくバックエンドで実行されるプラグインです。Backend plugin を利用することで、任意のデータソースを元にしたメトリクスの監視やクエリキャッシュの保持等を行うことができるようになります。

grafana.com

つまり、BigQuery をデータソースとするバックエンドプラグインを利用することで、BigQuery から取得したメトリクスをベースとした監視を Grafana のアラート機能経由で実現することができます。

BigQuery プラグイン

BigQuery プラグインは下記の通り GitHub で公開されているものがあります。

github.com

また、Issue や PR を見てみるとどうやら v2.0.0 から Backend plugin としての実装が追加されアラート機能を利用できるようだったので、こちらのプラグインを利用させていただくことにしました。

プラグインのインストール

まずはじめにプラグインを Grafana にインストールする必要があります。インストールの仕方は下記 README を参考に実施できます。

doitintl.github.io

プラグインをインストールしたら Grafana の画面上で確認するとプラグイン一覧に下記のように Google BigQuery という DATASOURCE のプラグインが追加されていることが確認できると思います。

f:id:sshota0809:20210126105053p:plain

プラグインが追加されていることが確認できたら、いよいよデータソースの追加です。データソース一覧画面から Add data source をクリックすると、データソースの一覧に BigQuery プラグインが追加されているはずなので、そちらを選択して初期設定を行います。

このプラグインは GCP の ServiceAccount を利用して BigQuery API へ対して認証を行います。Service Account のキーファイルをインポートする方法と、GCE であれば Default Service Account を利用する方法がありますが、いずれにしても BigQuery に対する Role を付与する必要があるのでご注意ください。こちらの設定についても GitHub の README に詳細に記載されているので参考に設定してみてください。

グラフの描画

データソースの追加ができたら、いよいよダッシュボードにパネルを追加してみましょう。

パネルを追加してデータソースを前述の手順で追加したデータソースに指定すると下記のように Query Builder が開かれます。この Query Builder は下記のように

  • 各項目に任意の値を入れてクエリを組み立ててくれるモード

f:id:sshota0809:20210126110217p:plain

  • Raw のクエリを直接入力できるモード

f:id:sshota0809:20210126110256p:plain

の 2 種類があります。

弊社では、多少複雑なクエリを利用してグラフの描画を行いたかったため、後者の Raw クエリを描画できるモードでクエリを組み立てました。かなり環境に依存したクエリとなっているので詳細なクエリの内容は掲載しませんが、ユーザアクセスのフロントに位置する Kong における 5xx ステータスのログ数 をカウントするグラフを下記のように作成してみました。

f:id:sshota0809:20210126110649p:plain

アラート設定

続いて、描画したグラフを元にアラート設定を行います。アラート設定はクエリの設定と同様にパネル設定の画面から行うことができ、画面から下記を設定すれば良いです。

  • Rule: アラート名、チェック間隔、どのくらい閾値超過が持続していたらアラート発報するか

  • Condition: アラート監視に利用するクエリや閾値

  • No Data & Error Handling: データソースへのクエリがエラーになったりデータがなかったりした場合のアクション

  • Notifications: 通知先等の設定

f:id:sshota0809:20210126111749p:plain

以上で、実現したかった目的である BigQuery から取得したメトリクスベースでアラート発報を行う という目的を達成することができました...と言いたいのですが 実はエラー無く Slack にアラート発報するよう設定できるまでハマったポイントがいくつかある のでそちらについて解説していきたいと思います。

ハマったポイント

この Backend plugin として動作をする部分についてですが v2.0.0 でリリースしたということもあって、README にもドキュメントがまだ整っていない状態です。なので、ソースコードを参照しつつデバッグする方向で各エラーを潰していきました。

Backend plugin ソースコードは下記の通りになっていて、コード量も多くなくすぐに読めてしまう程度なので一度目を通してみることをオススメします。

github.com

それではハマりポイントを解説していきます。

出力されるエラーログのエラー内容がわからない

問題

後述しますが、GCP への認証が通らない・クエリがエラーで怒られる等色々とハマりましたが、Backend plugin が出力するエラーログに関して詳細エラー内容が出力されず、なぜエラーを吐いているのかわからないという問題がありました。

これについては関連する Issue を作成したので、どういったことかは Issue を参考にしてください。( %v に実際のエラー内容が格納された err オブジェクトの中身が展開されるはずが正しく展開されない、といった内容です。)

github.com

解決策

このままだと何がなんだかわからなくデバッグもできない状態だったので、こちらに関してはソースコードの修正を行いました。なお、PR 作成し先日 Merge されたので、今後リリースされるバージョンではこのポイントについては考慮する必要はなくなりました。

github.com

GCP への認証が通らない

問題

詳細のエラー内容が確認できるようになった後、最初にぶつかったのがこのエラーです。Backend plugin 内では下記のように BigQuery のライブラリを利用してクエリの実行を行っているのですが、実行した時に 403 エラーが返ってくるというものでした。

https://github.com/doitintl/bigquery-grafana/blob/4977056af9819dbe406fcb347ecbd0ed931d18d1/pkg/plugin.go#L184

前述の手順でデータソースを設定をする際に(Default Service Account を利用していない場合)キーファイルを読み込む設定をしたと思いますが、その際に読み込んだキーファイルを Backend plugin の中で読み込む実装は特にされていませんでした。GCE の Default Service Account を利用している場合は、ライブラリ側でその情報を自動的に読み込みその Service Account の内容で認証を通しにいく仕様となっています。弊社環境ではキーファイルを読み込む方法で認証をしていたので、Backend plugin としてはキーファイルを読み込む術がありませんでした。

解決策

ライブラリは別途、GOOGLE_APPLICATION_CREDENTIALS という環境変数に指定されたキーファイルを read して、その Service Account の情報を利用するという仕様にもなっています。なので、Grafana を動作させている VM にキーファイルを配置及び環境変数の設定をすることで対応しました。

Backend plugin で利用できるカラム名に縛りがある

問題

Backend plugin は BigQuery から取得したクエリ結果を 時間軸に対するメトリクスの値 として識別することである時間軸にメトリクスの値が閾値を超えていないか、判断します。

さて、問題は Backend Plugin がどのように 時間軸メトリクスの値 を認識しているかです。実は、これに関しては BigQuery にクエリ発行をする際に適切にカラム名を特定の名前に指定する必要があります。

まだ、README の中で Backend plugin について記載されている内容がなくこの部分をソースコードを見るまで知らずにハマりました。

解決策

どのようなロジックになっているか Backend plugin のソースコードを確認すると下記のようにクエリ実行結果の各行を queryResult 型の構造体に取り出しています。

https://github.com/doitintl/bigquery-grafana/blob/4977056af9819dbe406fcb347ecbd0ed931d18d1/pkg/plugin.go#L203-L218

ポイントとなるこの queryResult 構造体を確認すると

type queryResult struct {
    Time   time.Time `bigquery:"time"`
    Values int64     `bigquery:"metric"`
}

のように定義されており、BigQuery の time カラムと metric カラムにマッピングしていることがわかります。

なので、この名前に従うようにクエリ側も定義する必要があります。そのため、下記のように AS 構文でカラム名を指定することで、正しく Backend plugin から参照可能になります。

SELECT 
       時間になるカラム AS time,
       メトリクスになるカラム AS metric

Grafana で利用できる変数が Backend plugin からクエリ実行する時に展開されない

問題

クエリで参照している BigQuery のテーブルは日毎にパーティショニングされたテーブルとなっているのですが、クエリ実行時の課金額も考慮して、Grafana のダッシュボードで指定した日付レンジ内のテーブルにのみクエリを実行するため下記のようなクエリを利用しています。

SELECT 
      ...
FROM `プロジェクト名.データセット名.テーブル名_*`
WHERE
    _TABLE_SUFFIX BETWEEN '${__from:date:YYYYMMDD}' AND '${__to:date:YYYYMMDD}'
    AND ...

上記クエリで指定している ${__from:date:YYYYMMDD}${__to:date:YYYYMMDD} にダッシュボードで指定した日付レンジが代入されるようになっています。なお、Grafana で利用できる Global Variables に関しては下記を参照してください。

grafana.com

しかし、あくまで上記の date の変数は画面上から日付レンジを指定した場合にのみ展開される変数となっています。すなわち、バックエンドで定期的にクエリを発行する Backend plugin では当然指定されるものではないので、Backend plugin から上記のクエリを BigQuery に問い合わせすると、変数が展開されず問い合わせされてしまい結果的にクエリがエラーになると言った事象が発生しまいた。

解決策

非常に単純な対処方法ではあるのですが、グラフ描画をするクエリ ( A ) とは別に Backend plugin が問い合わせするようのクエリ ( B ) を作成して、Backend plugin ではそれを利用するようにしました。

どういうことかというと、下記のようにクエリ B を追加して、Disable に設定します。こうすることでグラフ上にはクエリ B の内容が描画されません。

f:id:sshota0809:20210126115753p:plain

その上で、クエリ B では上記クエリの _TABLE_SUFFIX BETWEEN ... の部分を下記のように変更しました。

_TABLE_SUFFIX BETWEEN FORMAT_TIMESTAMP('%Y%m%d', TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)) AND  FORMAT_TIMESTAMP('%Y%m%d', CURRENT_TIMESTAMP())

FORMAT_TIMESTAMP は BigQuery で利用できる関数になっており、クエリ発行をした日付の 前日〜当日 のレンジを指定しています。こうすることで Backend plugin から発行されるクエリでエラーが発生することがなくなり、常に前日〜当日のレンジにのみクエリを発行するのでクエリ実行のか金額も抑えることができるようになりました。

おわりに

以上で Grafana と BigQuery を連携したグラフ描画やアラート設定は完了です。BigQuery と Grafana の連携はかなり便利なので色々使い所はあると思います。

しかし、常に BigQuery 側の課金を気にしつつクエリの設計等はした方が良いと思います。特に Backend plugin を利用してアラート設定をしている場合、一定スパンで Backend plugin が BigQuery にクエリを発行することになります。なので、クエリ発行(監視)スパンやクエリで読み込まれるテーブルのサイズを気にせずに設定してしまうと、気づかぬうちに BigQuery の課金額が爆発してしまうリスクもあります。

仲間募集中

弊社 UZABASE では現在 SRE ポジションの募集を行っております!Wantedly弊社公式ホームページ にて募集要項等記載していますので、興味があればぜひご応募お願い致します。


  1. データソースの plugin で Backend plugin としての実装が行われている必要はある

  2. 弊社では現状、Istio はマイクロサービスの Blue/Green デプロイを行うために利用している目的が強いが、インフラリソースに関しては Blue/Green デプロイを行うメリットが現状あまりない、等の理由でこの部分に関しては Istio を導入しておりません。

  3. BigQuery 自体必要に使いやすく、BigQuery の利用をやめて Elasticsearch に乗り換えるという選択もメリットがあまりないという話になりました。

  4. Core プラグインと呼ばれる Grafana に予めバンドルされた物やエンタープライズ向けの物

© Uzabase, Inc. All rights reserved.