【第2回】Zabbix を現地管理者の「計器盤」にする — 時系列 DB だけでは足りなかった理由
前回のおさらい
第1回では、電力監視システムの 5 層アーキテクチャと、Rust モジュールによる 10 秒周期の全センサー一括取得 を解説しました。Rust モジュールは取得データを 2 方向に分配します。
-
Zabbix 方向:
zabbix_senderでトラッパーアイテムへ(蓄積・アラート用) - Web 方向:バックエンドサービス群を経由し WebSocket で(リアルタイム表示用)
第2回では、この 2 つの経路の「受け手」──Zabbix によるデータ蓄積 と バックエンドサービス群の JSON 設計 を掘り下げます。
Zabbix 側の設計 ── Rust からデータを「受け取る」構成
従来型との違い:Zabbix 自身はポーリングしない
一般的な Zabbix の構成では、Zabbix サーバーが能動的にターゲットをポーリングしてデータを取得します。しかし本システムでは、データ収集パイプラインが zabbix_sender プロトコルで Zabbix にデータを送り込む 構成です。
[従来] Zabbix ──(ポーリング)──▶ 対象機器
[本構成] データ収集パイプライン ──(zabbix_sender)──▶ Zabbix(受動的に受け取るだけ)
Zabbix 側のアイテムタイプは 「Zabbix トラッパー」 を使用します。
トラッパーアイテムの設定
ホスト名: plc-building-a
アイテム名: 電力量(1F 東側)
タイプ: Zabbix トラッパー
キー: power.kwh[panel-1f-east]
データ型: 数値(浮動小数)
単位: kWh
ヒストリ保存期間: 30d
トレンド保存期間: 365d
トラッパーアイテムは Zabbix 側のポーリング負荷がゼロで、外部から送信されたタイミングでそのまま記録 されます。つまりデータ収集側の 10 秒周期がそのまま Zabbix のヒストリに反映されます。
きれいに揃ったヒストリ ── Zabbix ネイティブポーリングでは得られなかったもの
第1回でも触れましたが、この構成がもたらす副産物として Zabbix のヒストリデータが完全に等間隔で並ぶ という点は、運用上非常に大きなメリットです。
Zabbix のネイティブポーリングで 10 秒間隔を設定した場合、実際にはポーラープロセスの負荷状況によってタイムスタンプにジッターが生じます。10 台の PLC × 数十アイテムを監視するような規模では、設定上 10 秒でも実測 9〜12 秒程度のばらつきが出ることは珍しくありません。
Rust + トラッパー構成では、この問題が構造的に解消されます。
[Zabbix ヒストリテーブル]
clock | itemid | value
---------------|--------|------
1712890800 | 10001 | 342.5 ← 10:00:00 全アイテム同一タイムスタンプ
1712890800 | 10002 | 210.8
1712890800 | 10003 | 18.4
1712890810 | 10001 | 342.7 ← 10:00:10 きっちり10秒後
1712890810 | 10002 | 211.1
1712890810 | 10003 | 18.6
全アイテムが同一の clock 値を持つため、Zabbix の「最新データ」画面やグラフで複数アイテムを並べて表示したときに、同じ瞬間の値同士が正確に比較 できます。Zabbix API でデータを取り出して分析する際も、タイムスタンプの補間やアライメント処理が不要になります。
現地管理者にとっても、「データ一覧を開いたときに全センサーの時刻がきれいに揃っている」のは、データの信頼性を直感的に感じられる要素 です。タイムスタンプが微妙にバラバラだと「本当に正しく取れているのか?」という不安が生じますが、きっちり揃っていれば「このシステムはちゃんと動いている」と安心できます。
なぜ時系列 DB だけでは足りないのか ── Zabbix を採用した理由
電力データの蓄積には、TimescaleDB や InfluxDB のような時系列特化 DB も有力な選択肢です。実際、純粋な書き込みスループットや圧縮効率では Zabbix の内蔵 DB を上回る場面もありますし、本システムのデータパイプラインでも時系列 DB は活用しています。
しかし、時系列 DB はあくまで「データを貯める箱」 です。現地管理者が日常的にセンサーの状態を確認し、閾値を調整し、アラートに対応するための 「管理 UI」は別途構築しなければなりません。
Zabbix をアーキテクチャに組み込んだ最大の理由は、「現地の管理者が、少しの学習で自分自身でセンサーデータを確認できる UI」が最初から付いてくる ことです。
現地管理者の視点 ── 時系列 DB + Grafana では遠い
時系列 DB を使う場合、データの可視化には通常 Grafana を別途構築します。Grafana は強力ですが、管理者が「ちょっとセンサーの生値を確認したい」「閾値を変えたい」「新しいアイテムを追加したい」と思ったとき、最低でも以下の知識が求められます。
- SQL クエリ(
SELECT time_bucket(...) FROM ...) - Grafana のパネル・ダッシュボード編集
- アラートルールの Prometheus 式 or Grafana Alert 構文
- データソース設定の理解
一方 Zabbix は、「ホスト → アイテム → グラフ」 というシンプルな階層構造で、GUI 上でクリックしていくだけで値の確認・グラフ表示・閾値設定が完結します。SQL を書く必要はありません。
[時系列 DB + Grafana だけの場合]
管理者が値を確認したい
→ Grafana にログイン
→ ダッシュボードを探す(なければ SQL を書いてパネルを作成)
→ 時間範囲を指定して確認
管理者が「昨日14時に異常値が出た」と報告を受けた
→ SQL: SELECT * FROM sensor_data WHERE time BETWEEN ... AND ... AND sensor_id = ...
→ 結果をパネルに貼る or psql で直接確認
→ 前後の文脈を見たければ別クエリ
管理者がアイテムを探したい
→ テーブル定義(スキーマ)を把握していないと WHERE 句が書けない
→ カラム名は sensor_id? device_name? item_key?
[Zabbix の場合]
管理者が値を確認したい
→ Zabbix にログイン
→ ホスト → アイテム一覧 → 「最新データ」をクリック
→ グラフも自動生成されている
管理者が「昨日14時に異常値が出た」と報告を受けた
→ アイテムのグラフを開く → 時間軸をドラッグして14時付近を拡大
→ 前後の値、トリガー発火履歴もすべて同一画面で確認
管理者がアイテムを探したい
→ アイテム一覧でフリーテキスト検索(日本語名でも検索可能)
→ 該当アイテムをクリック → 即座にグラフ・ヒストリ表示
特に運用で効いてくるのが、「ある時刻に何が起きていたか」を SQL なしで調べられる 点です。時系列 DB では特定時刻のデータを見るだけで WHERE time BETWEEN ... AND ... を書かなければなりませんが、Zabbix ならグラフの時間軸をマウスで操作するだけで、その瞬間のセンサー値、トリガーの発火状況、関連する他のアイテムの値まで GUI 上で辿れます。
現地管理者にとって、「自分が知りたいことを、SQL を書かずに自分の手で確認できる」 という体験は、単なる利便性を超えた 安心感 に直結します。「何かあっても自分で調べられる」という自信が、システムへの信頼と日常的な活用につながっています。
この差は、エンジニアが常駐しない現場 では決定的です。施設管理の担当者が異動で交代しても、Zabbix であれば数時間の引き継ぎで基本操作を習得できます。時系列 DB + Grafana の運用引き継ぎには、それよりもはるかに長い学習時間が必要です。
開発側のメリット ── 管理 UI のためのスキーマ設計が不要
Zabbix を管理者向け UI として採用した恩恵は、現地管理者の UX だけではありません。管理画面周りの開発工程が大幅に短縮 されます。
時系列 DB に Grafana を組み合わせて管理者向け画面を構築する場合、まず閲覧用のスキーマ設計やダッシュボード構築が必要です。
-- 時系列 DB で管理画面を構築する場合:スキーマ設計が必須
CREATE TABLE sensor_data (
time TIMESTAMPTZ NOT NULL,
building_id TEXT NOT NULL,
floor INTEGER NOT NULL,
zone_id TEXT NOT NULL,
item_type TEXT NOT NULL,
value DOUBLE PRECISION,
unit TEXT
);
-- インデックス設計も必要
CREATE INDEX ON sensor_data (zone_id, time DESC);
-- 管理者が見る集約ビューの設計
CREATE MATERIALIZED VIEW hourly_stats ...
監視対象が電力だけでなく温湿度・CO2・風速と横展開していくと、管理画面のためのスキーマ進化管理(マイグレーション)自体がひとつの運用負荷になります。
一方 Zabbix では、管理画面上で「アイテムを追加」するだけで、ヒストリテーブルへの格納・トレンド集約・グラフ生成がすべて自動的に行われます。管理者向けの閲覧機能のためにテーブル設計や Grafana ダッシュボード構築をする必要がありません。
[時系列 DB + Grafana で管理画面を構築する場合]
新しいセンサー種別を追加
→ スキーマ変更の検討 → マイグレーション作成 → テスト → デプロイ
→ Grafana のパネル/ダッシュボード更新
→ 所要時間:数時間〜数日
[Zabbix の場合]
新しいセンサー種別を追加
→ Zabbix 管理画面でアイテムを追加(GUI で数クリック)
→ データ収集側の設定にエントリを追記
→ 所要時間:数分〜数十分
管理 UI 構築のための開発時間がゼロになることで、開発リソースを本来注力すべきデータ収集パイプラインや React フロントエンドの UX 改善に集中 できます。
管理者 UI としての技術比較
※ 以下は「管理者が日常的にセンサー状態を確認する UI」としての比較です。データパイプライン上で時系列 DB を併用することを否定するものではありません。
| 要件 | 時系列 DB + Grafana のみ | Zabbix |
|---|---|---|
| データ受け入れ | 自前 API / INSERT 実装 | トラッパー(標準機能) |
| スキーマ設計 | 必須(テーブル・インデックス・集約ビュー) | 不要(アイテム追加で自動管理) |
| 保存期間の自動管理 | retention policy 設定 | ハウスキーピング(GUI 設定) |
| 閾値アラート | Grafana Alert or 別途構築 | トリガー機能(GUI で完結) |
| 管理画面 | Grafana(構築・保守が必要) | 標準 Web UI(即座に利用可能) |
| アイテム検索 | SQL の WHERE 句 or Grafana 変数 | GUI でフリーテキスト検索 |
| 特定時刻の状況確認 | SQL クエリが必要 | グラフの時間軸操作で完結 |
| 現地管理者の運用コスト | 高い(SQL + Grafana 操作) | 低い(GUI 操作のみ) |
| API(データ取り出し) | SQL / REST API | JSON-RPC API |
| 時系列クエリ性能 | ◎ 優秀 | ○ 十分(トレンド集約あり) |
純粋な DB 性能では時系列特化 DB に軍配が上がりますが、「誰がこのシステムを日常的に見るのか」 を考えたとき、Zabbix の管理 UI は現地担当者にとって非常に優秀なインターフェースです。これは時系列 DB の 代わりに Zabbix を使うという話ではなく、管理者 UI 層として Zabbix が担う役割は、時系列 DB だけでは埋められないという話です。
役割の整理:Zabbix は「管理者のための計器盤」
本システムにおける Zabbix の位置づけを改めて整理します。
- 利用者(施設管理部門)→ React ダッシュボードを見る(Zabbix を知らない)
- 現地管理者(設備担当)→ Zabbix UI で直接センサーの生値やグラフを確認、閾値の調整やアラート対応を行う
- 開発者(当社)→ データ収集パイプライン・バックエンド・React の開発保守
現地管理者が Zabbix を「自分の計器盤」として日常的に使えること──これは時系列 DB 単体では得られない、運用上の大きなアドバンテージです。Zabbix の UI は決してモダンとは言えませんが、「設定もデータ確認も同じ画面で完結する」 一体感が、現場での自走を可能にしています。
データ収集パイプラインが収集の責務を担ってくれるおかげで、Zabbix は 「受け取って貯めてアラートを出す、そして現地管理者に計器盤を提供する」 という本来の強みに専念できます。
ヒストリとトレンドの使い分け
Zabbix のデータストレージを理解する上で最も重要なのが、ヒストリ と トレンド の違いです。
ヒストリ(生データ)
データ収集パイプラインから送り込まれるたびに記録される生の値。10 秒周期なら 1 日 8,640 件/アイテム。
timestamp | value (kWh)
---------------------|-----------
2026-04-12 10:00:00 | 342.50
2026-04-12 10:00:10 | 342.53
2026-04-12 10:00:20 | 342.57
...
ヒストリの保存期間を長くしすぎると DB が肥大化するため、本システムでは 7〜30 日 に設定しています。
トレンド(集約データ)
1 時間ごとに min / avg / max を自動集計した集約データ。ストレージ効率が圧倒的に良く、365 日以上 の長期保存に適しています。
timestamp | min | avg | max
---------------------|--------|--------|-------
2026-04-12 10:00:00 | 342.50 | 345.20 | 348.80
2026-04-12 11:00:00 | 341.00 | 343.90 | 347.20
...
データ経路の使い分け ── リアルタイムは 永続DBを経由しない直送、長期は Zabbix API
ここが本システムのアーキテクチャ上の最も重要なポイントです。リアルタイムデータと長期データで、データの経路が異なります。
[リアルタイム表示]
PLC → PLC-GW(gRPC) → PLC-Collector → Redis → WEB-API(TypeScript) → React
※ Zabbix DB を経由しない。10秒遅延のみ。
[長期トレンド表示]
Zabbix API (trend.get / history.get) → バックエンド → React
※ Rust が過去に Zabbix へ送ったデータを API で取り出す。
| 表示目的 | データ経路 | ソース |
|---|---|---|
| 直近のリアルタイムグラフ(10秒更新) | DB を経由しない直送パス | Rust → バックエンド → WebSocket |
| 直近数時間の詳細グラフ | Zabbix history.get
|
ヒストリ |
| 日別・月別の電力量推移 | Zabbix trend.get
|
トレンド |
| 前年同月比較 | Zabbix trend.get
|
トレンド |
| 瞬間ピーク値の検出 | Zabbix history.get
|
ヒストリ |
リアルタイム表示は Rust から直接 WebSocket で配信されるため、Zabbix の API 応答速度やポーリング間隔に一切依存しません。一方、過去データの参照は Zabbix の豊富な API をフル活用します。
「グラフ以外は DB が介在しない」という構造的優位
この 2 経路の設計をさらに掘り下げると、本システムのスケーラビリティの源泉が見えてきます。
React ダッシュボードの画面要素を「DB を使うもの」と「使わないもの」に分けると、こうなります。
| 画面要素 | DB 介在 | データ経路 |
|---|---|---|
| サマリーカード(現在値) | なし | Rust → JSON → WebSocket → React |
| フロアマップ(ゾーン別現在値) | なし | Rust → JSON → WebSocket → React |
| インフラ状態(機器死活等) | なし | Rust → JSON → WebSocket → React |
| リアルタイム折れ線(直近10分) | なし | Rust → JSON → WebSocket → React(ブラウザ内バッファ) |
| 接続状態インジケーター | なし | WebSocket の接続状態そのもの |
| 日別・月別トレンドグラフ | あり | Zabbix API → バックエンド → React |
| 統計テーブル(集計値) | あり | Zabbix API → バックエンド → React |
| 前年同月比較 | あり | Zabbix API → バックエンド → React |
日常の監視で最も頻繁に見る画面──「今どうなっているか」──は、すべて DB を経由しない JSON 直送パスで完結しています。
データ収集パイプラインが 10 秒ごとに生成する JSON は、PLC からの電力データだけでなく、ping による機器死活、HTTP による Web サービス応答まで──異なるプロトコルの全データを 1 つの JSON オブジェクト に統合したものです。この JSON がメモリ上で生成され、そのまま WebSocket で配信されます。DB への書き込み待ちも、DB からの読み出しも発生しません。
Rust の SensorSnapshot(メモリ上の JSON)
│
├──▶ WebSocket fan-out ──▶ 閲覧者 A のブラウザ
├──▶ WebSocket fan-out ──▶ 閲覧者 B のブラウザ
├──▶ WebSocket fan-out ──▶ 閲覧者 C のブラウザ
│ ...(何人増えても DB 負荷ゼロ)
│
└──▶ zabbix_sender ──▶ Zabbix DB(蓄積用。閲覧者数と無関係)
この構造は 2 つの意味でスケーラブルです。
閲覧者方向のスケーラビリティ: 閲覧者が 10 人でも 100 人でも、DB への問い合わせは増えません。WebSocket の fan-out はネットワーク帯域のみを消費します。従来の「画面を開くたびに DB から SELECT」する構成では、閲覧者が増えるほど DB がボトルネックになりますが、本構成ではリアルタイム画面に関してその心配は構造上ありません。
センサー方向のスケーラビリティ: データソースが増えても、JSON のサイズが大きくなるだけで、DB のテーブル設計やインデックス追加は不要です。Rust の TOML 設定に PLC レジスタ・ping 先・HTTP エンドポイントを追記し、拠点 JSON にエントリを足すだけで、新しいデータが次の 10 秒周期から即座にリアルタイム画面に現れます。
DB が登場するのは、過去を振り返る場面だけ です。そしてその用途には、Zabbix のヒストリ/トレンドという枯れた仕組みが控えています。「現在」と「過去」でデータの経路を分けたことが、リアルタイム性と蓄積の信頼性を同時に満たす設計になっています。
Zabbix API の活用 ── 長期データの取り出し
Zabbix API は JSON-RPC 2.0 ベースです。バックエンドサービスが Zabbix API を呼び出し、フロントエンド向けに整形して返します。
認証
{
"jsonrpc": "2.0",
"method": "user.login",
"params": {
"username": "api_readonly",
"password": "********"
},
"id": 1
}
フロントエンド用の API ユーザーは 読み取り専用 のロールに限定します。Zabbix 6.0 以降では API トークン機能も利用可能です。
トレンド取得(月次グラフ用)
{
"jsonrpc": "2.0",
"method": "trend.get",
"params": {
"output": ["itemid", "clock", "num", "value_min", "value_avg", "value_max"],
"itemids": "12345",
"time_from": 1709251200,
"time_till": 1712880000
},
"auth": "YOUR_AUTH_TOKEN",
"id": 2
}
トレンドデータは 1 時間ごとの集約なので、1 ヶ月分でもわずか 720 件/アイテム。フロントエンドでの月次グラフ描画に最適です。
トリガー・アラートの活用
データ収集パイプラインが PLC からの生データを Zabbix に送り込んでいるため、Zabbix のトリガー機能もそのまま使えます。
トリガー名: デマンド超過警告(1F 東側)
条件式: last(/plc-building-a/power.demand[panel-1f-east])>50
深刻度: 警告
アクション: メール通知 + Slack 通知
アラート管理は Zabbix に任せることで、Rust モジュール側にアラートロジックを持たせる必要がなくなります。
バックエンドサービス群 ── 2 つの経路を統合する翻訳者
なぜバックエンドサービスが必要か
React フロントエンドから見ると、データの出どころは 2 つあります。
- データ収集パイプライン から流れてくるリアルタイムデータ
- Zabbix API から取り出す長期蓄積データ
バックエンドサービス群はこの 2 つを統合し、フロントエンドに対して 統一的な JSON インターフェース を提供します。
[React SPA]
│
├─ WebSocket: /ws/realtime ← リアルタイムデータを中継
├─ GET: /api/power/history ← Zabbix history.get を整形
├─ GET: /api/power/trend ← Zabbix trend.get を整形
└─ GET: /api/locations ← 拠点メタ情報 JSON
│
[中間 API サーバー(レンタルサーバー / VPS)]
│
├── Rust WebSocket 受信 → クライアント中継
├── Zabbix API 呼び出し → データ整形
└── 拠点メタ情報 JSON 読み込み・合成
その他の役割:
- 認証情報の隠蔽:Zabbix API トークンをブラウザに露出させない
- データ整形:UNIX タイムスタンプを ISO 8601 に変換し、拠点メタ情報を付加
- キャッシュ:トレンドデータなど更新頻度の低いデータをキャッシュして Zabbix の負荷を軽減
- CORS / ネットワーク制約の解消:Zabbix サーバーが閉域網にある場合の外部公開窓口
拠点メタ情報の JSON 管理
本システムの特徴的な機能のひとつが、拠点(建物・フロア・区画)の位置情報を JSON で管理し、フロントエンド上のマップに動的にマッピングする 仕組みです。
{
"locations": [
{
"id": "building-a",
"name": "A棟",
"floors": [
{
"floor": 1,
"label": "1F 試験エリア",
"zones": [
{
"zone_id": "1f-east",
"label": "東側試験室",
"zabbix_host": "plc-building-a",
"zabbix_items": {
"power_kwh": "power.kwh[panel-1f-east]",
"power_demand": "power.demand[panel-1f-east]"
},
"map_position": { "x": 320, "y": 150 },
"area_sqm": 120
},
{
"zone_id": "1f-west",
"label": "西側計測室",
"zabbix_host": "plc-building-a",
"zabbix_items": {
"power_kwh": "power.kwh[panel-1f-west]",
"power_demand": "power.demand[panel-1f-west]"
},
"map_position": { "x": 80, "y": 150 },
"area_sqm": 95
}
]
}
]
}
]
}
この JSON が Zabbix アイテムキー / Rust の zone_id / 画面上の表示位置を紐づける唯一のマスターデータ です。拠点が増えた場合も、この JSON と Rust の TOML 設定にエントリを追加するだけで、コード変更なしにフロントエンドへ自動反映されます。
API レスポンスの設計例
中間サーバーは、Rust のリアルタイムデータや Zabbix API のレスポンスを拠点 JSON と合成し、フロントエンドが直接使える形で返します。
// GET /api/power/trend?building=building-a&floor=1&period=monthly
{
"building": "A棟",
"floor": 1,
"period": "2026-03",
"zones": [
{
"zone_id": "1f-east",
"label": "東側試験室",
"daily_kwh": [
{ "date": "2026-03-01", "min": 280, "avg": 345, "max": 412 },
{ "date": "2026-03-02", "min": 265, "avg": 330, "max": 398 }
],
"total_kwh": 10695,
"map_position": { "x": 320, "y": 150 }
}
]
}
React 側はこのレスポンスを受け取るだけで、Zabbix の内部構造(ホスト ID やアイテム ID)は一切漏れません。
クラウド化 ── 「どこでも電力監視」の実現
本システムのもうひとつの特徴が、オンプレの PLC / Rust / Zabbix 環境をクラウド経由で閲覧可能にしている 点です。
[現場 ─ 閉域網] [クラウド]
MELSEC Q → Rust Collector ─WebSocket/VPN─▶ バックエンドサービス
│ (クラウド / VPS)
▼ │
Zabbix Server ───Zabbix API/VPN──▶ │
(ヒストリ/トレンド) │
▼
React Dashboard
(任意のブラウザ)
- Rust モジュールと Zabbix は閉域網内に設置し、セキュリティを確保
- バックエンドサービスはクラウドまたは VPS 上に配置
- Rust → バックエンド間は WebSocket over VPN で 10 秒周期のリアルタイムデータを配信
- Zabbix → バックエンド間は VPN / SSH トンネル経由で長期データを取得
- フロントエンドは静的ホスティング(CDN 配信も可能)
この構成により、施設に行かなくてもスマートフォンやタブレットから電力状況を確認できます。
第2回まとめ
本記事では、データ収集パイプラインから送り込まれたデータを Zabbix がどう蓄積し、バックエンドサービス群がどう統合・配信するかを解説しました。
ここまでのキーポイント:
- Zabbix はトラッパーアイテムで Rust からデータを受動的に受け取り、収集の負荷を持たない
- Rust + トラッパー構成の副産物として、Zabbix ネイティブポーリングでは困難だった 全アイテム同一タイムスタンプ・きっちり 10 秒刻みのきれいなヒストリ が実現
- リアルタイム表示は DB を経由しない直送パス、長期データは Zabbix API という 2 経路の使い分けが設計の核心
- ヒストリ(短期・緻密)とトレンド(長期・集約)の使い分けで API 負荷を最適化
- 拠点メタ情報を JSON で一元管理し、Zabbix のアイテムとフロントエンドの表示を疎結合に保つ
- バックエンドサービス群が 2 経路の統合・認証隠蔽・クラウド公開を担う
次回はいよいよ、利用者が実際に触れるフロントエンド── 「Zabbix にログインさせない」監視画面 の構築について、WebSocket によるリアルタイム描画の実装パターンを含めて解説します。
→次回:【#3】電力監視システムをモダンWebに載せた話 PLC × Rust × Zabbix × React
関連記事
本シリーズは、筆者が所属するクイックイタレート株式会社での電力監視システム開発の知見をもとに執筆しています。関連する公開事例はこちら。