ZabbixのデータをSplunkに取り込む方法あれこれ

## はじめに

ZabbixのデータをSplunkに取り込んで色々可視化をしたいと思ったことはあるはずです。
じゃあどうすればいいか？
なかなか情報が無かったのでまとめてみました。

以下方法を見てみます。

・リアルタイムエクスポート
・DBクエリ
・アラート（Media Type）

## Zabbixデータの取込方法

### リアルタイムエクスポートでZabbixデータを即時連携


https://www.zabbix.com/documentation/6.0/jp/manual/appendix/install/real_time_export

Zabbix v4.0以降でトリガーイベント、アイテム値、トレンドデータをリアルタイムでJSON形式でエクスポートできます。
簡単にデータを出力しSplunkに取り込むことが可能なので最もおすすめです。


#### 設定手順
1. /etc/zabbix/zabbix_server.confのexportDirを設定します。このディレクトリにProblem、Item、Trendのデータが出力されます
1. データサイズが大きくなりがちなので、ExportFileSizeを適切に設定しましょう（デフォルトは1GB）


#### サンプルデータ

・Problem
```
{"clock":1723538464,"ns":968235755,"value":1,"eventid":39,"name":"Memory pressure","severity":4,"hosts":[{"host":"Zabbix server","name":"Zabbix server"}],"groups":["Zabbix servers"],"tags":[{"tag":"component","value":"memory"},{"tag":"class","value":"os"},{"tag":"target","value":"linux"}]}
```

・Item
```
{"host":{"host":"Zabbix server","name":"Zabbix server"},"groups":["Zabbix servers"],"item_tags":[{"tag":"component","value":"cpu"}],"itemid":42264,"name":"Linux: CPU idle time","clock":1723531764,"ns":665879785,"value":99.566269,"type":0}
```

・Trend（1時間あたりの統計値。不要かもしれません。）
```
{"host":{"host":"Zabbix server","name":"Zabbix server"},"groups":["Zabbix servers"],"item_tags":[{"tag":"component","value":"cpu"}],"itemid":42264,"name":"Linux: CPU idle time","clock":1723528800,"count":1,"min":99.566269,"avg":99.566269,"max":99.566269,"type":0}
```

素直なJSON形式なので単にUFで出力データを取り込めばよいでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/eb856091-c350-c52e-454d-7f14db3999ac.png)




### DBクエリで詳細データを取得

Problem、Item、TrendデータをDBから直接クエリすることができます。
リアルタイムエクスポートが使えない場合の代替策や詳細データを取りたい場合に使用できます。

Splunkでは[Splunk DB Connect](https://splunkbase.splunk.com/app/2686)を使いクエリすればOKです。
※DB ConnectではInput typeをRisingにし、`WHERE time > ?`みたいに入れれば差分が取れます。

#### クエリ例
以下はクエリ例です。適宜変更してください。
Databaseは`zabbix`です。
テーブルスキーマとリレーションに関する公式情報はないので気合いとGoogle先生でなんとかするしかありません。

・Event
```sql: クエリ例
SELECT FROM_UNIXTIME(events.clock) AS time, hosts.host, events.eventid, events.name, events.severity, triggers.triggerid, events.acknowledged 
FROM events 
JOIN triggers ON events.objectid = triggers.triggerid 
JOIN functions ON functions.triggerid = triggers.triggerid 
JOIN items ON items.itemid = functions.itemid 
JOIN hosts ON items.hostid = hosts.hostid 
ORDER BY time;
```

```text: アウトプット例
+---------------------+---------------+---------+-------------------------------------+----------+-----------+--------------+
| time                | host          | eventid | name                                | severity | triggerid | acknowledged |
+---------------------+---------------+---------+-------------------------------------+----------+-----------+--------------+
| 2024-08-13 06:50:04 | Zabbix server |       2 | Memory pressure                     |        3 |     23708 |            0 |
| 2024-08-13 07:33:50 | Zabbix server |       4 | CPU Alerts                          |        4 |     23707 |            0 |
| 2024-08-13 07:36:50 | Zabbix server |       5 | CPU Alerts                          |        0 |     23707 |            0 |

```

・Item
```sql: クエリ例
SELECT FROM_UNIXTIME(history.clock) AS time, hosts.host, items.name, history.value 
FROM history 
JOIN items ON history.itemid = items.itemid 
JOIN hosts ON items.hostid = hosts.hostid 
ORDER BY time;
```

```text: アウトプット例
+---------------------+---------------+---------------------------------------------------------------------------+---------------------+
| time                | host          | name                                                                      | value               |
+---------------------+---------------+---------------------------------------------------------------------------+---------------------+
| 2024-08-13 06:17:43 | Zabbix server | Zabbix server: Utilization of ODBC poller data collector processes, in %  |                   0 |
| 2024-08-13 06:17:44 | Zabbix server | Zabbix server: Utilization of poller data collector processes, in %       | 0.19933554817275748 |
| 2024-08-13 06:17:45 | Zabbix server | Zabbix server: Utilization of proxy poller data collector processes, in % |                   0 |
```

・Trend
```sql: クエリ例
SELECT FROM_UNIXTIME(trends.clock) AS time, hosts.host, items.name, trends.value_min, trends.value_avg, trends.value_max 
FROM trends 
JOIN items ON trends.itemid = items.itemid 
JOIN hosts ON items.hostid = hosts.hostid 
ORDER BY time;
```

```text: アウトプット例
+---------------------+---------------+----------------------------------------------------------------------+--------------------+-----------------------+---------------------+
| time                | host          | name                                                                 | value_min          | value_avg             | value_max           |
+---------------------+---------------+----------------------------------------------------------------------+--------------------+-----------------------+---------------------+
| 2024-08-13 06:00:00 | Zabbix server | Zabbix server: Number of processed numeric (float) values per second | 0.8655759063842902 |     1.040058365006189 |  1.0665503618891081 |
| 2024-08-13 06:00:00 | Zabbix server | Zabbix server: Number of processed character values per second       |                  0 | 0.0031214101236932907 | 0.03329858264057206 |
| 2024-08-13 06:00:00 | Zabbix server | Zabbix server: Number of processed log values per second             |                  0 |                     0 |                   0 |
```


### アラート連携（Media Type設定）

Splunk HEC用のMedia typeを作成すればアラートをSplunkに送信することができます。
※アイディア、スクリプトはとある方から頂いたものです。ありがとうございます！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/fd814c70-f5e7-bc1b-b817-9b0ed48f2aa9.png)

#### 設定手順

1. SplunkでHECを作成してください

2. Zabbixで以下のようにMedia Typeを作成してください

| Name | Value | 
|:-----------|:------------|
| URL       | https://\<Splunk HEC URL※\>/services/collector/event        |
| HTTPProxy     |    （PROXYを通す場合はプロキシサーバーを設定）   |
| To       | {ALERT.SENDTO}        |
| SUBJECT       | {ALERT.SUBJECT}       |
| MESSAGE       | {ALERT.MESSAGE}       |
| TOKEN       | \<HECトークン\>      |
| ALERT_SEVERITY       | {EVENT.SEVERITY}       |
| HOST       | {HOST.NAME}       |
| ID       | {EVENT.ID}       |
| OPDATA       | {EVENT.OPDATA}       |
| EVENT_DATE       | {EVENT.DATE}       |
| EVENT_TIME       | {EVENT.TIME}       |
| TRIGGER_URL       | {TRIGGER.URL}       |

※HEC URLについてはこちら
https://docs.splunk.com/Documentation/Splunk/latest/Data/UsetheHTTPEventCollector#Send_data_to_HTTP_Event_Collector


Scriptは以下のようにします。

```sh:Script
try {
    var params = JSON.parse(value);

    // Validate required parameters
    if (!params.URL || !params.TOKEN) {
        throw 'Missing required parameters URL or TOKEN';
    }

    // Create the HTTP request
    var req = new HttpRequest();
    req.addHeader('Authorization: Splunk ' + params.TOKEN);
    req.addHeader('Content-Type: application/json');
    
    // Add Proxy
    if(params.HTTPProxy){
      req.setProxy(params.HTTPProxy);
    }

    // Create the data to send to Splunk HEC
    var data = JSON.stringify({
        event: {
            _time: params.EVENT_DATE + ' ' + params.EVENT_TIME,
            subject: params.SUBJECT,
            severity: params.ALERT_SEVERITY,
            hostname: params.HOST,
            event_id: params.ID,
            opdata: params.OPDATA,
            url: params.TRIGGER_URL
        },
        sourcetype: 'zabbix:alert'
    });

    // Send the HTTP POST request
    var response = req.post(params.URL, data);

    if (req.getStatus() != 200) {
        throw 'Failed to send data to Splunk HEC: ' + req.getStatus();
    }

    return 'OK';
} catch (error) {
    Zabbix.log(4, '[ Splunk HEC Webhook ] Error: ' + error);
    throw 'Splunk HEC Webhook failed: ' + error;
}
```

<details><summary>5.4以前はこちら（CurlHttpRequestを使用）</summary>



```sh:Script 5.4以前
try {
    var params = JSON.parse(value);

    // Validate required parameters
    if (!params.URL || !params.TOKEN) {
        throw 'Missing required parameters URL or TOKEN';
    }

    // Create the HTTP request
    var req = new CurlHttpRequest();
    req.AddHeader('Authorization: Splunk ' + params.TOKEN);
    req.AddHeader('Content-Type: application/json');
    
    // Add Proxy
    if(params.HTTPProxy){
      req.SetProxy(params.HTTPProxy);
    }

    // Create the data to send to Splunk HEC
    var data = JSON.stringify({
        event: {
            _time: params.EVENT_DATE + ' ' + params.EVENT_TIME,
            subject: params.SUBJECT,
            severity: params.ALERT_SEVERITY,
            hostname: params.HOST,
            event_id: params.ID,
            opdata: params.OPDATA,
            url: params.TRIGGER_URL
        },
        sourcetype: 'zabbix:alert'
    });

    // Send the HTTP POST request
    var response = req.Post(params.URL, data);

    if (req.Status() != 200) {
        throw 'Failed to send data to Splunk HEC: ' + req.Status();
    }

    return 'OK';
} catch (error) {
    Zabbix.log(4, '[ Splunk HEC Webhook ] Error: ' + error);
    throw 'Splunk HEC Webhook failed: ' + error;
}
```
</details>


Message templateの追加も忘れずに。
デフォルトでよいでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/af8da1e2-d524-04ce-e884-1c15babe91bf.png)

そしてMediaを紐づけたユーザーにTrigger Actionを設定すれば完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/7efc8a84-54ee-5792-6218-a912989919b8.png)


#### 結果例
Splunkで受信できます。
タイムスタンプのフォーマットがなかなかユニークでうまく抽出できてない感があるので、パース用の設定はした方が良さそうですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1218403/61d1684b-6529-f242-4030-b09bc40ab9a7.png)



後は同じアラートIDやホストでアラート集約したり、グラフ化したり、他のメトリクスと相関させたり、好きなようにしてください。


## おわりに

Zabbixからのデータ取得方法についていくつかパターンをまとめてみました。
Zabbixのデータを活用したいけどどうやって出せばいいんだーとお悩みの方の参考になれば幸いです。