Zabbix7 で PostgreSQL待機イベントを監視する

Zabbix の PostgreSQL監視機能

Zabbix は広く使われている OSS のシステム監視ツールです。標準で様々な監視対象サーバソフトウェアに対応したプラグインが付属していて、Zabbix 6.4 以降からは、PostgreSQL のプラグインも含まれています。この Zabbix 標準付属の PostgreSQLプラグインには一通りの監視項目が備わっているのですが、データベースに特化した監視ツールや PostgreSQL専用の監視ツールに比べると、いくらか物足りない点があります。

そのような無い機能の一つが待機イベントの監視です。PostgreSQL では pg_stat_activity ビューである時点のセッションごとの待機イベントを取得することができます。待機イベントとは、何を待っているかを示すもので、「行ロック」「WALファイルの書き込み同期」「ファイルからの読み込み」「クライアントの次の要求」といった待機の種別を把握できます。これを監視ツールで継続的に採取・蓄積して、後でどの時間帯にどのような待機が多く発生しているのかを調べたいわけです。

本稿では Zabbix に PostgreSQL の監視項目を追加する例として待機イベント監視を加える方法を説明します。

前提環境

本稿の記載にあたっては以下の環境で検証を行いました。登場する要素を減らすため、Zabbix の監視データを格納するために使う Zabbix と同サーバ内の PostgreSQL を、監視対象としました。

  • RHEL 9.5
  • Zabbix 7.0.22
  • Apache httpd 2.4.62
  • PHP 8.2.28
  • PostgreSQL 16.10

これらは本稿執筆時点の最新バージョンとは言えません。これらバージョンを選んだのは、SRA OSS Tech Blog の記事「Zabbix 7.0 のインストール」に記載の手順で環境構築ができるためです。Zabbix をまだ手元で動かしていない方は、こちらの記事を参考にしてください。

これから示す手順は、これらすべてのバージョンを揃えなくとも、Zabbix 7.x であり、監視対象 PostgreSQL が 10.x 以降であれば、通用するはずです。

方針と目標

pg_stat_activityビューから次のような情報を取り出せます。これは、ある時点の待機イベントを種類ごとに集計しています。

db1=# SELECT wait_event_type, wait_event, count(*) FROM pg_stat_activity GROUP BY 1,2;
 wait_event_type |     wait_event      | count
-----------------+---------------------+-------
                 |                     |     1
 Activity        | BgWriterHibernate   |     1
 Activity        | WalWriterMain       |     1
 Activity        | LogicalLauncherMain |     1
 Client          | ClientRead          |    27
 Lock            | tuple               |     1
 Activity        | CheckpointerMain    |     1
 Activity        | AutoVacuumMain      |     1
 Lock            | transactionid       |    17
 IO              | WALSync             |     1
(10 rows)

wait_event_type と wait_event が空欄なのは、待機をしていない、CPU を使って処理実行中ということです。ClientRead はクライアント側が次の要求をするまで待っています。また、Activity はクライアントからの SQL実行ではなくバックグラウンドプロセスの動作です。これらは監視対象から除外しても良さそうです。

この結果を Zabbix に蓄積して、下記のように待機の種類ごとの件数を積み上げたグラフで参照できるようにすることが目標です。

ただし、ある瞬間のデータでは変動が激しく傾向が見えにくいので、データ表示では何回か分ごとにまとめることを考えます。10秒ごとに取得したものを 1分~5分ごとに集計する、といった具合です。

実現方針

Zabbix では複数の監視アイテムを指定して、それらの積算グラフを作ることができます。今回の目標が少し難しい点は、監視アイテムの種類がとても多いことです。PostgreSQL の待機イベントは 200種類以上あり、しかも、バージョンによって違いがあります。200種類以上を予め列挙することなく、どのような待機イベントが現れても自動的に対応できるようにしたいところです。そこで、監視アイテムを自動生成するローレベルディスカバリと呼ばれる Zabbix の機能を使用することにします。

ローレベルディスカバリのための監視データには様々な形式が利用可能ですが、JSON形式が Zabbix にとっても、PostgreSQL にとっても、扱いやすい形式ですので、今回は JSON形式を使うことにします。また、グラフを作るときに、監視アイテムの名前のパターンを指定して、件数の定まらない監視アイテム群を対象とすることができます。

Zabbix のエージェントで各待機イベントの件数を JSON で返す問合せを定期的に実行して、ローレベルディスカバリ機能によって、それらを時系列で蓄積するアイテム群が自動生成されるようにして、それらアイテム値を束ねてグラフ表示する、という方針で実現を目指すことにします。

具体的な構成:アイテムの作成

ローレベルディスカバリに渡すための JSON を返す SQL は次ようになものにします。

db1=# SELECT json_agg(q1.*)::jsonb FROM (
        SELECT (wait_event_type || ':' || wait_event) AS wait_event, count(*) FROM pg_stat_activity
          WHERE wait_event_type IS NOT NULL GROUP BY 1) AS q1;
                             json_agg
---------------------------------------------------------------------------------------------------------------
 [{"count": 35, "wait_event": "Client:ClientRead"}, {"count": 1, "wait_event": "Activity:LogicalLauncherMain"}, 
  {"count": 1, "wait_event": "Activity:WalWriterMain"}, {"count": 1, "wait_event": "Activity:AutoVacuumMain"}, 
  {"count": 1, "wait_event": "Activity:CheckpointerMain"}, {"count": 1, "wait_event": "Extension:Extension"}, 
  {"count": 1, "wait_event": "Activity:BgWriterHibernate"}]
(1 row)
(実際には1行ですが改行を加えて記載しています)

イベントタイプ名とイベント名は繋げて1つにして、それぞれの件数を集計しています。本例ではイベントタイプ名とイベント名が NULL となる待機していないプロセスは、この段階で除外しています。集計した表を json_agg関数で JSON配列にしています。

これを Zabbixエージェントの UserParameter に記述します。「アイテムキー名,データ取得のためのコマンド」という形式です。Zabbixエージェントを使う場合と Zabbixエージェント2 を使う場合で記述するファイルの場所が異なりますが、記述する内容は同じです。ファイル名の「postgresql_zabbixserver.conf」は、zabbixserver用 PostgreSQLむけの追加設定を書くファイルという意味で適当に決めたものです。

作成するファイル:

/etc/zabbix/zabbix_agentd.d/postgresql_zabbixserver.conf (Zabbixエージェントの場合)
/etc/zabbix/zabbix_agent2.d/postgresql_zabbixserver.conf (Zabbixエージェント2の場合)

記述内容:

UserParameter=pgsql.wait_events.items,/usr/pgsql-16/bin/psql -U postgres -d postgres -t -c "SELECT json_agg(q1.*)::jsonb FROM (SELECT (wait_event_type || ':' || wait_event) AS wait_event, count(*) FROM pg_stat_activity WHERE wait_event_type IS NOT NULL GROUP BY 1) AS q1;"

次に Zabbix の Web UI 側でアイテムを作っていきます。
まず、対象サーバの「ディスカバリリスト」のページから「ディスカバリルール」を作成します。

キーに UserParameter で指定したキーを指定します。今回のJSONデータは、このままではディスカバリルール用には使えないため、保存前処理も設定します。

要素名が “wait_event”、”count” となっているものを、”{#WAIT_EVENT}”、”{#COUNT}” という Zabbix のマクロ変数の形式に書き換えしています。

さらに、フィルターも設定します。

この例ではクライアント待ちとバックグラウンドプロセスの待機を除外するため、{#WAIT_EVENT} に「Activity:」も「Client:」も含まないエントリだけを採取するという設定を与えています。この正規表現は部分一致で「一致する」と判定されます。フィルターを記載のようにすることは必須ではなくて、欲しいデータに応じて適宜調整してください。

続いて、アイテムのプロトタイプを作成します。

「名前」に {#WAIT_EVENT} を含めることが重要です。こうすることで、待機イベントごとに「PostgreSQL Wait Event “Lock:tuple”」といった異なるアイテムが生成されます。

キーには、適当な名前で引数に{#WAIT_EVENT}を指定したものを指定します。タイプは「計算」でデータ型は件数ですので「数値(整数)」とします。式には「{#COUNT}」を指定します。

一般的な「ディスカバリ」と「アイテムのプロトタイプ」では、ディスカバリでアイテム名を抽出して、アイテムのプロトタイプで各アイテム名を引数に与えてアイテムごとの情報をエージェント等から取り出す、という形が多いのですが、今回は最初の JSONデータの中にアイテム毎の情報(=件数)が既にありますので、タイプを「計算」としています。

対象サーバの「アイテム」一覧の画面で、アイテムが自動生成されているか確認してみます。

名前に「PostgreSQL Wait Event」として「適用」すると、それを含む名前のアイテムが一覧であらわれます。各待機イベントごとのアイテムができていることが確認できました。

具体的な構成:グラフの作成

最後にグラフを作ります。不定の複数アイテムを束ねたグラフを作ることができるのは、今のところダッシュボード上のウィジェットとしてグラフを作る場合に限られます。そのため、ダッシュボードを編集して、次のようにウィジェットの追加を行ないます。

アイテムパターンとして「PostgreSQL Wait Event*」と指定します。これで、「PostgreSQL Wait Event」で始まる名前のアイテムを束ねることができます。グラフの形式を「棒」として、積算グラフにチェックを入れます。アグリゲーション間隔は、当初の計画通り、エージェントの監視間隔よりも長い時間にして、何回か分を1つのグラフの棒にまとめることにします。アグリゲーション関数は「合計」としました。

ここまで設定して、次のようなグラフが得られました。

この例は pgbench で負荷をかけているときの様子で、イベントタイプが Activity と Client である待機イベントはフィルタで除外した場合です。1分毎の待機イベントごとの積算グラフになっています。待機イベントごとの色はグラデーションになるように自動で決められます。マウスカーソルを棒のうえに置くと、待機イベント名と件数値がポップアップで表示されます。

ちょっと不満がある点は、グラデーションの色が近すぎて区別がしにくいことと、ポップアップ上の一覧と積算グラフ上の色の順番が逆になっていることです。これを調整する方法は、Zabbix Web UI 上には無いようです(Zabbix 7.4 からはカラー選択で「パレット」を指定する方法があります)。

まとめ

本稿では、Zabbix による PostgreSQL 監視で、標準プラグインには無い追加の監視項目を作る例として、待機イベントを監視するグラフを作ってみました。Zabbix標準付属の PostgreSQL監視用プラグインに無い監視項目も、このように追加していくことができます。今回の監視項目は比較的面倒な部類です。単一のアイテムで表現可能な監視項目であれば、より簡単に追加できるはずです。