Zabbixの設定をZabbicookで設定ファイル化＆gitで管理する

# この記事の概要

Zabbixの設定もgitで管理したい・・・
その想いから [Zabbicook](https://github.com/rerorero/zabbicook/blob/master/README.ja.md) という **Zabbixの設定をファイルで記述するためのツール** を作りました。
単独での利用例と、[Ansibleと一緒に利用する例](https://github.com/rerorero/zabbicook-ansible-example)を紹介しています。
ユーザーの設定のみ、とか１テンプレートのみに使う、といったこともできるのですぐに導入できます！

# Zabbix As Code
皆さんはZabbixの設定をどのように管理していますか？
必要になったらGUIでぽちぽち設定するというのが普通だと思いますが、いくつか運用で困ることもあります。

- 設定の変更履歴が分からない（誰がいつ変えたのか）
- 設定の変更をレビューする方法
- 開発環境ごとにzabbixのインスタンスを立てていると、環境ごとに似たような設定をしないといけない

Zabbixの設定もファイル化してgitで管理したい、設定ファイルで一括設定したい、そう思ったことはないでしょうか。

Zabbixには [Export / Import](https://www.zabbix.com/documentation/3.2/manual/xml_export_import) の機能が用意されているので、エクスポートしたXMLやJSONで管理することができます。
しかしこの方法は少し難があります。例えばテンプレートアイテムにログファイルを１つ設定した場合、管理したいのは設定したアイテムのkeyや、intervalなど、いくつかの設定だけなのに、エクスポートされたファイルのアイテムの項目には [アイテムのすべてのプロパティ](https://www.zabbix.com/documentation/3.2/manual/api/reference/item/object) の情報が含まれており、関心のない情報まで扱わなければなりません。また、ユーザーやアクションの設定は今の所まだ対応していないようです。
エクスポート・インポート用のファイルは、一括で設定を移行するのにはとても便利なものですが、レビューや履歴管理には今ひとつむかなそうです。

そこで、もう少しスマートな Zabbix as Code を目指して、 [Zabbicook](https://github.com/rerorero/zabbicook/blob/master/README.ja.md) というツールを作りました。

# Zabbicookとは
[rerorero/zabbicook](https://github.com/rerorero/zabbicook/blob/master/README.ja.md)

Zabbixの設定をコード化（設定ファイル化）して管理するためのツールです。設定ファイルの内容に従って Zabbix API を使い、Zabbixの設定変更を行います。以下のような特徴があります。

- Scala製のコマンドラインツール
- HOCONフォーマットによる設定（HOCONフォーマットについては後述します。）
- ある程度の冪等性
- ユーザーやアクションの設定も可能
- 一部の設定の管理だけでも利用可能

以下の条件で利用できます。

1. 実行環境に **[Java 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html) 以上**が必要です
2. **Zabbix 3.0 以上**に対応しています。〜2.x では動作確認していません。

# Zabbicookを試してみる
## インストール
[Githubの最新リリースのzip](https://github.com/rerorero/zabbicook/releases/latest) をダウンロードし、好きなところに解凍すれば、準備は完了です。
解凍されたファイル内にある `zabbicook`(Mac/Linux) または `zabbicook.cmd`(Windows) をコマンドラインから叩いて実行します。

```bash:バージョン表示
zabbicook -v
```

## Zabbixサーバーを用意する
Zabbicookを試すにはZabbixサーバーが必要です。
バージョン3.0以上の適当なZabbixサーバーを用意する必要があります。（すでに用意できている場合はこの手順は飛ばしてください。）
[Dockerized Zabbix] (https://www.zabbix.org/wiki/Dockerized_Zabbix) と称してdockerイメージが提供されており、簡単にdockerインスタンスが立てられるので、今回はこれを利用してみます。

```bash
docker run \
    -d \
    --name zabbix-db \
    --env="MARIADB_USER=zabbix" \
    --env="MARIADB_PASS=my_password" \
    monitoringartist/zabbix-db-mariadb

docker run \
    -d \
    --name zabbix \
    -p 8080:80 \
    -p 10051:10051 \
    -v /etc/localtime:/etc/localtime:ro \
    --link zabbix-db:zabbix.db \
    --env="ZS_DBHost=zabbix.db" \
    --env="ZS_DBUser=zabbix" \
    --env="ZS_DBPassword=my_password" \
    --env="XXL_zapix=true" \
    --env="XXL_grapher=true" \
    monitoringartist/zabbix-xxl:latest
```

ここでは 8080 ポートで動かしています。http://localhost:8080/ をブラウザで開くとZabbixが起動しているのが確認できます。

## ユーザーを追加してみる
最初にユーザーを追加する設定を書いてみます。
エディターで以下のような内容で、`zabbicook.conf`という設定ファイルを作成します。ファイル名は特に決まりはありません。
場所もどこでも良いのですが、ここではZabbicookと同じディレクトリに作成することにします。

```:zabbicook.conf
users: [
  {
    user {
      alias: Alice
      type: admin
    }
    groups: [ "Zabbix administrators" ]
    password: pass1234
    // 'password' を暫定的なパスワードとして利用するかどうか
    // initialPassword=false の場合は、この設定ファイルで設定を行うたびにAliceのパスワードを'pass1234'に設定しようとします
    initialPassword: false
  }

  {
    user {
      alias:　Bob
      type: user
    }
    groups: [ Guests ]
    password: bob
    // 'password' を暫定的なパスワードとして利用するかどうか
    // initialPassword=true の場合は、Bobユーザーが新たに作成される場合にだけパスワードを設定します
    // 初期パスワードを設定しておいて、Bob自身にWebからログインしてパスワードを変更してもらう、といった運用での利用を想定しています。
    initialPassword: true
  }
]
```

```bash:実行
$ zabbicook -f ./zabbicook.conf -i http://localhost:8080/ -u Admin -p zabbix
presenting 2 users ...
User entities modified(created=2)
Succeed: Total changes = 2
```

http://localhost:8080/users.php をブラウザで開くと、`Alice`と`Bob`がユーザーに追加されていることがわかります。
[`users`](https://rerorero.github.io/zabbicook/#m-users) という名前の Object Array を定義すると、それに従いユーザーを作成します。`user` Object には以下のようなプロパティがあります

- `user`: 名前を示す`alias`や、ユーザータイプを指定する`type`などのユーザー属性を含むObjectです。全てのプロパティは [こちら](https://rerorero.github.io/zabbicook/#m-users-user)を参照してください。
- `groups`: ユーザーグループ名を指定します。独自のユーザーグループを作成するには、別途 [`userGroups`](https://rerorero.github.io/zabbicook/#m-userGroups)を定義してください。
- `password`: ユーザーのパスワードです。
- `initialPassword`: `password`を初期パスワードとするかどうかのフラグです。

Zabbicookではこのように、各設定を決められたスキーマのObjectとして、設定ファイルに記述していき、`zabbicook`コマンドに渡すことで設定の適用を行います。
`zabbicook`コマンドに渡す各オプションの説明は以下の通りです。

- `-f`: 設定ファイルのパスを指定します。
- `-i`: zabbix apiのエンドポイントURLを指定します。
- `-u`: zabbix設定を行うZabbixの管理者ユーザーを指定します。
- `-p`: `-u` で指定したユーザーのパスワードを指定します。`Admin:zabbix`はZabbixの初期状態で存在する管理者ユーザー名とパスワードです。


## テンプレートを追加してみる
次にテンプレートを追加してみます。
テンプレートの各プロパティについては [こちら](https://rerorero.github.io/zabbicook/#m-templates) を参照してください。

```:templates.conf
templates: [
  {
    template: { name: "My Template"}
    groups: [ Templates ]
    items: [
      {
        name: "Check sum of /var/log/maillog"
        key: "vfs.file.cksum[/var/log/maillog]"
        type: ZabbixAgent
        valueType: unsigned
        interval: 60
        history: 30
      }
    ]
  }
]
```
```
$ zabbicook -f ./templates.conf -i http://localhost:8080/ -u Admin -p zabbix
presenting 1 templates ...
Template entities modified(created=1)
Item entities modified(created=1)
Succeed: Total changes = 2
```

ブラウザで http://localhost:8080/templates.php にアクセスすると、`My Template` という名前で、アイテムが1つだけ登録されているテンプレートが作成されているかと思います。


# 冪等性に関する注意点
たった今実行したテンプレート設定を、もう一度同じコマンドで実行してみます。

```bash:実行
$ zabbicook -f ./templates.conf -i http://localhost:8080/ -u Admin -p zabbix
presenting 1 templates ...

Succeed: Total changes = 0
```
`changes = 0`となり、今度は何も変更を行わなかったことを示しています。
Zabbicookは設定ファイルに基づいて、ある程度の冪等性を保ちます。
ただし**削除される条件については注意が必要**です。

Zabbicookでは、テンプレートに属するアイテムやトリガーの削除については冪等性がありますが、テンプレートそのものの削除には冪等性がありません。
例えば、テンプレートに属するアイテムをZabbicookの設定ファイルから1つ消して実行した場合、そのアイテムは消えます。
しかし、１つのテンプレートを丸々設定ファイルから消した場合、Zabbicookはそのテンプレートを消すことはしません（何も変更しません）。
テンプレート以外でも、`users`、`userGroups`、`actions` 等にも同じことが言えます。

また、今の所**Zabbicookはホストに直接アイテムやトリガーなどを設定することはできません。**
アイテム、トリガー、グラフなどはテンプレートごとに冪等性を持っており、ホストはそれらのテンプレートをリンクして利用することを前提としています。

# HOCONって何？
[HOCON (Human-Optimized Config Object Notation)] (https://github.com/typesafehub/config/blob/master/HOCON.md)

Zabbicookでは設定ファイルをHOCONというフォーマットで記述します。HOCONはあまりメジャーではなさそうなので簡単に触れておきます。
「人類が編集しやすい（？）」をモットーに、JSONのスーパーセットとして作られた設定ファイル用のフォーマットです。
「新しいフォーマットなんて覚えられない！」という方は、**JSONをそのまま使うこともできます**。
HOCONを採用した理由としては、読みやすいことやZabbicook自体の実装言語（Scala)で利用しやすかった、ということもありますが、それ以外に以下の二点が大きな理由として挙げられます。

##### 1. 変数のように定義した値を利用できる
同じようなアイテムをたくさん設定したい場合に便利です。たとえば特定のSNMPコミュニティから複数の値を登録したい場合、共通の設定を一つの`Object`で定義しておき、使い回すことができます。

```
// SNMP共通の設定
SNMPItemBase {
  type: SNMPv1agent
  valueType: float
  port: 8161
  SNMPCommunity: test_community
  interval: 30
  history: 30
  units: ""
}

templates: [
  {
     template: { name: "Test template" }
     groups: [ "Templates" ]
     items: [
       ${SNMPItemBase} {
         name: SNMPItem1
         key: item1
         OID: "1.3.6.1.4.1.99999.1"
       }
       ${SNMPItemBase} {
         name: SNMPItem2
         key: item2
         OID: "1.3.6.1.4.1.99999.2"
       }
       // 〜 以下略 〜
     ]
  }
]
```

##### 2. `include`で簡単にファイルを分けられる
設定項目が増えてくると１つのファイルで管理するのは苦しくなりますが、`include "templates.conf"` と書くと別のHOCONファイルをインクルードできるため、簡単にファイルを分割して管理することができます。

Zabbcookの設定を書いているとすぐに設定ファイルが肥大化したり、冗長な記述が多くなりがちなので、HOCONのこういった機能は非常に助かります。
HOCONの詳細な仕様は [冒頭のリンク](https://github.com/typesafehub/config/blob/master/HOCON.md) で確認するとよいでしょう。

# 設定できる項目
Zabbicookで設定できる全ての項目は[こちらを参照](https://rerorero.github.io/zabbicook/)してください。
また、コマンドラインから`--doc`オプションで同じ情報を表示することもできます。

```bash:設定ファイルの説明をツリーで表示
$ zabbicook --doc
```

そのままだと出力が長すぎるので、`-r`でパスを、`-L`でツリーの深さを指定することができます。

```bash:templates.itemプロパティの説明を深さ1で表示する
$ zabbicook --doc -r templates.items -L 1
```

現在Zabbicookで設定できる項目はわずかです。開発者が私しかいないため、自分が必要なものを随時追加していく感じで進めていますが、[Issue](https://github.com/rerorero/zabbicook/issues)を立てれば対応するかもしれません:rolling_eyes:

# Ansibleで利用する
Zabbicookは独立したコマンドラインツールであり、30億のデバイスで走るJavaさえあれば動かすことができるので、AnsibleやChefなどから利用したり、docker buildの際に走らせるといったこともできます。
私は主にAnsibleから利用しているので、ここではAnsibleで利用する例を紹介します。

### サンプルPlaybook
サンプルは以下に置いています。
https://github.com/rerorero/zabbicook-ansible-example

```
# ansible　インストール
pip install ansible

git clone git@github.com:rerorero/zabbicook-ansible-example.git
cd zabbicook-ansible-example

# 今までの手順で localhost:8080 でzabbixサーバーが動いていればそのままプレイブックを流せます。
# もし動いていなければ https://github.com/rerorero/zabbicook-ansible-example/blob/master/README.md を参考にしてください。
ansible-playbook playbook.yml
```

以下簡単に補足します。

### 管理者ユーザーのパスワード

Ansibleから利用する際に若干面倒なのが、Zabbicookを実行する管理者ユーザーのパスワードの管理です。
ユーザーのパスワードは、Zabbicook設定ファイルでは [`users`](https://rerorero.github.io/zabbicook/#m-users) で設定できますが、Zabbicookを実行するためのAdministratorユーザーは、設定ファイルに記述できません。
初期設定のユーザーとパスワード(Admin/zabbix)を使うか、あらかじめZabbix GUIで設定しておく必要があります。しかし商用環境でデフォルトのパスワードを利用し続けるわけにはいきませんし、GUIで設定するのも、せっかくansibleを利用しているのに手動で作業しないといけないというのは残念な感じです。
そこでZabbicookには、コマンドラインからAdminユーザーのパスワードを変更する機能を用意しています。

```bash:ユーザーのパスワード変更
zabbicook --change-pass -i http://localhost:8080/ -u Admin -p zabbix --new-pass rDLz=a7w
```

- `-u`: パスワードを変更したいユーザー名を指定します。
- `-p`: 変更前のパスワードを指定します。
- `--new-pass`: 変更後のパスワードを指定します

パスワードがすでに変更済みの場合も正常終了します。
サンプルPlaybookでは [このあたり](https://github.com/rerorero/zabbicook-ansible-example/blob/master/roles/zabbicook/tasks/execute.yml#L6-L9) が該当部分です。
パスワードは vault などで管理するのがよいでしょう。

### Zabbicookの結果を取得する
もうひとつ連携で面倒なのが、結果の出力です。
標準出力をawkでいじったりするのは面倒なので、Zabbicookでは`--json`オプションで結果をJSONフォーマットで出力できるようにしました。

```bash:--jsonオプション成功時の出力例
$ zabbicook -f ./zabbicook.conf -i http://localhost:8080/ -u Admin -p zabbix --json
{
  "result" : "success",
  "report" : {
    "created" : 3,
    "updated" : 0,
    "deleted" : 0,
    "total" : 3,
    "messages" : [ "Template entities modified(created=1)", "Item entities modified(created=2)" ]
  }
}
```

```bash:--jsonオプション失敗時の出力例
$ zabbicook -f ./zabbicook.conf -i http://localhost:8080/ -u Admin -p zabbix --json
{
  "result" : "fail",
  "error" : "./zabbicook.conf: 10: : unbalanced close brace '}' with no open brace (if you intended '}' to be part of a key or string value, try enclosing the key or value in double quotes)"
}
```

Ansible(jinja2)にはJSONフィルター(`from_json`)が用意されているので、 [このような感じで](https://github.com/rerorero/zabbicook-ansible-example/blob/master/roles/zabbicook/tasks/execute.yml#L16) 構造化して扱うことができます。

### テンプレートエンジンとの組み合わせ
あえて言うことでも無いですが・・・テンプレートエンジン（Ansibleの場合はjinja2）とHOCONとを組み合わせることで、さらに記述の表現が増すのでオススメです。
ホストをinventoryで管理していれば、[`hosts`](https://rerorero.github.io/zabbicook/#m-hosts)の設定もinventoryを元に設定できます。
サンプルコードでは [ユーザーの設定 を `group_vars` などで管理できるような形にしています。](https://github.com/rerorero/zabbicook-ansible-example/blob/master/roles/zabbicook_config/templates/users.conf.j2#L5)

# 最後に
Zabbicookは全ての設定に利用せずとも、`hosts`や`users`の管理だけに利用するといったことができますので、もし興味があれば使ってみてください。
まだ開発を始めたばかりで、機能があまり豊富ではなかったり、要素の削除についてはイマイチだったりしますが、今後もマイペースに更新は続けていこうと思います。