Zabbixの通知をホストグループと深刻度によって別々のSlackチャンネルに送信するようにAnsibleで構成する方法の調査

Zabbix の障害通知を Slack に送ることができて喜んだのもつかの間で、冷静になって考えたらこんなことに気付いてしまいました。

「ホストの種類によって緊急対応の担当者が変わるかもしれない」
「重大な障害通知が警告通知に埋もれて見逃されたら大変だ」
「ホストの種類と深刻度によって通知する時間帯を変えられた方がいい」

これらを解決するために、ホストグループとトリガーの深刻度によって別々の Slack チャンネルに通知する方法を調べて Ansible で実装しました。
![zabbix-slacks.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/f5348814-0ab4-e2d5-dae7-d3b9e4dc38b1.png)
この調査結果が思った以上に複雑だったので記事にしました。

# 今回の要件

1. Ansible で構成すること
2. ホストの種類と深刻度によって、以下のように時間帯と通知先チャンネルを別々に設定できること

\#|ホストの種類|深刻度|有効な時間帯|通知先 Slack チャンネル名
:--:|:--:|:--:|:--:|:--:
1|サーバー|重度以上|24h 7d|server_emergency
2|サーバー|軽度、警告|9:00-24:00 月-金|server_warning
3|サーバー|情報以下|通知しない|
4|クライアント|重度以上|24h 7d|client_emergency
5|クライアント|軽度、警告|9:00-17:00 月-金|client_warning
6|クライアント|情報以下|通知しない|

## 環境
（バージョンが古くてすみません！）

* Zabbix 4.0.7
* CentOS 7.6
* Ansible 2.9.2

※ Zabbix 4.4 から外部スクリプトなしで Slack に送信できるようになりましたが、今回は外部スクリプトを呼び出す方式です。

# 調査

これらの要件を実現するために情報を収集します。

## ハンズオンの体験

まずは先人の知恵を拝借して、Slack に通知を送るための基本的な設定と動作を理解します。
別々の Slack チャンネルに送信する方法は検索しても見つからなかったので、1 つのチャンネルに送信するシンプルな構成を体験しました。

ハンズオンは以下の動画を参考にさせていただきました。
**Call Slack Hooks With Zabbix Triggers - YouTube**
https://www.youtube.com/watch?v=zZSXEvjpfbY
![zabbix-call-slack-youtube.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/c3d4707d-c2db-5b4c-ab3e-91061fcc12bd.png)

英語ですが画面を操作しながら説明してくれるのと YouTube が字幕を表示してくれるので、英語のリスニングができなくても問題ありません。動作確認の手順まで優しく教えてくれます！

## ブログから学ぶ

ブログは以下を参考にさせていただきました。特に送信用スクリプトはほとんどそのまま使わせてもらいました。
**ZABBIXのアラートをSlackに飛ばす - 株式会社CoLabMix**
https://colabmix.co.jp/tech-blog/install-zabbix-slack/

## 全体的なフローの把握

次に通知先を振り分ける方法ですが、[公式マニュアル](https://www.zabbix.com/documentation/4.0/manual/config/notifications)で調べても要素が多くて関係が複雑なため、なかなか理解できませんでした。
こんな時は複雑な要素と関係をクラス図にしてしまえば、クラスと属性、関連（矢印線）の種類、向き、多重度（1 や *）で表現できるので、視覚的に理解しやすくなります。
![zabbix-alert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/cc6a66c6-f5a4-e759-fbaf-df1ac5ed0f2f.png)

以下のようなフローになっていました。

1. アイテムがホストから値を定期的に取得する
2. トリガーが取得値と条件式で判定し、障害などのイベントを生成する
3. アクションが実行条件に合致するイベントに反応して、指定されたユーザーにメッセージを送信する
4. ユーザーは自身が持つメディアを使って、指定された送信先（今回は Slack のチャンネル名）にメッセージを送信する
5. メディアタイプがスクリプトの場合、外部スクリプトを実行する

## 各部の詳細

以上の情報から、要件を実現する方法を検討します。

### 深刻度によって時間帯を制限して別のチャンネルに送信する

最下流のスクリプトから遡って、チャンネルを分岐できる場所を探します。ユーザーとメディアの関係に注目してください。
![zabbix-alert-user-media.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/c2bae34f-11a7-e980-cb91-075cda2ed848.png)

* 1 つの「ユーザー」は複数の「メディア」を持つことができる
* 1 つの「メディア」は 1 つの「送信先チャンネル」を持つことができる
* 1 つの「メディア」は 1 つの「有効な時間帯」を持つことができる
* 1 つの「メディア」は複数の「指定した深刻度のときに使用」を条件にすることができる

ゆえに、ユーザーに複数のメディアを設定すれば、深刻度によって別のチャンネルに送信することができ、時間帯を個別に設定することもできます。
Web の設定画面では下図のようになります。「有効な時間帯」の`1-5`は月〜金の意味です。
![zabbix-webif-user-media.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/d839b7ac-4f90-a786-56ff-503f9d86e7e9.png)

これは以下の要件に相当するサーバー用のユーザーの設定です。

\#|ホストの種類|深刻度|有効な時間帯|通知先チャンネル名
:--:|:--:|:--:|:--:|:--:
1|サーバー|重度以上|24h 7d|server_emergency
2|サーバー|軽度、警告|9:00-24:00 月-金|server_warning

### ホストの種類によって別のチャンネルに送信する

ホストの種類は、ホストが属する「ホストグループ」で区別することにします。
ここでは「アクション」と「ホストグループ」と「ユーザー」の関係に注目します。
![zabbix-alert-action.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/3129a536-97cd-17bb-16fe-43edf3db616f.png)

* 「アクション」は実行条件として「ホストグループ」を使用することができる
* 「アクション」は実行内容に「送信先ユーザー」を指定することができる

ゆえに、ホストグループごとに反応するアクションを作成して、それぞれが別のユーザーにメッセージを送信すればよいことが分かります。
さらに各ユーザーは深刻度によって送信先を分岐できるので、下図の構成で 2 ユーザー × 2 メディア = 計 4 つのチャンネルに通知を振り分けることができます。
![zabbix-alert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/44ae1fa7-be1a-841c-0073-245eff06508f.png)

# 作業内容

アイテムとトリガーを除いて、イベントを拾って Slack に送信するまでの作業内容の例です。

\#|項目|方法|備考
:--:|:--|:--|:--
1|Zabbix のスクリプトパス取得|ANsible shell モジュール、grep、awk|Ansible 実行時に zabbix_server.conf から取得する場合
2|送信スクリプト作成|シェルスクリプト|参考: [ZABBIXのアラートをSlackに飛ばす - 株式会社CoLabMix](https://colabmix.co.jp/tech-blog/install-zabbix-slack/)
3|送信スクリプト配置|Ansible template モジュール|#2 のスクリプトを #1 で取得したパスに配置する
4|メディアタイプ作成|Ansible zabbix_mediatype モジュール|
5|送信用ユーザーとユーザーメディア作成コマンド作成|シェルスクリプト、Python、Zabbix API|（Ansible の zabbix_user モジュールはまだ開発中）
6|ユーザー作成|Ansible shell モジュール|#5 のコマンドを実行する
7|アクション作成|Ansible zabbix_action モジュール|

問題は、ユーザーとユーザーメディアを設定する #5 の方法が Ansible モジュールではなく自作コマンドであることです…

下記のように、現在の Ansible では zabbix_user モジュールがまだ開発中となっているため、今回はシェルスクリプトと Python で Zabbix API を呼び出すコマンドを自作しました。

これはこれで結構大変だったので、少し待って zabbix_user モジュールを使うことをお勧めします。

最新の Zabbix 用モジュールの一覧
https://docs.ansible.com/ansible/latest/modules/list_of_monitoring_modules.html#zabbix

![zabbbix-minitoring-modules-latest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/020bdb73-c78a-1ba8-d66a-c63625ebd887.png)

開発中の Zabbix 用モジュールの一覧
https://docs.ansible.com/ansible/devel/modules/list_of_monitoring_modules.html#zabbix
![zabbbix-monitoring-modules-devel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/30a32ef8-d78f-05aa-d886-206211893262.png)

以上、実現方法の調査報告までになります。

# 参考

* Call Slack Hooks With Zabbix Triggers - YouTube
  * https://www.youtube.com/watch?v=zZSXEvjpfbY
* ZABBIXのアラートをSlackに飛ばす - 株式会社CoLabMix
  * https://colabmix.co.jp/tech-blog/install-zabbix-slack/
* Zabbix Documentation 4.0 - 9 Notifications upon events
  * https://www.zabbix.com/documentation/4.0/manual/config/notifications
* Zabbix Documentation 4.0 - 11 Users and user groups
  * https://www.zabbix.com/documentation/4.0/manual/config/users_and_usergroups
* Zabbix APIを操作するコマンドをPython3の標準ライブラリで作る方法
  * https://qiita.com/3244/items/f7d0fd32d4f3fcd673e9