Ansibleのインベントリとvarsの依存関係を可視化する

ansible-inventory-grapherというツールを使って、Ansibleのもつインベントリとvarsの依存関係を可視化していきます。

可視化することで、例えば以下のようなイメージで、インベントリとvarsの依存関係を理解できるようになります。

![test-prod-web-server-28a.png](https://qiita-image-store.s3.amazonaws.com/0/16458/d7a0d8df-f3b2-b3d2-bd00-1274483d5353.png)

# 可視化の重要性

みなさん、Ansibleを活用していますか？

Ansibleはインフラ構成管理にも有用なツールである反面、「お手軽に使う」ところから「本気で使う」ところでは、様々な見直しを迫られると思います。これまでは1つのPlaybookに直書きしていたものが、roleを分割して管理したり、インベントリも親子関係をもたせたり…などです。すると、これまでは簡単に把握できていたものが、いつの間にかカオスになっていき、依存関係を把握できなくなっていきます。

依存関係を簡単に把握するにはどうすればよいか？そう、可視化です。可視化することで、いちいち説明しなくても、図を見れば一発でわかるようになります。自分ひとりが開発しているならともかく、他の開発者に共通の理解を促すためにも、可視化は非常に重要です。

これから行う前提として、既にAnsibleはインストールされているものとします。

# ansible-inventory-grapher 

インベントリの可視化にはansible-inventory-grapherというツールを使います。

https://github.com/willthames/ansible-inventory-grapher

ansible-lintの作者でもある方ですね。

インベントリの可視化、といってもぴんと来ないかもしれませんが、公式の作者のツールの説明がわかりやすいでしょう。
例えばインベントリファイルにこう書かれているとします:

https://github.com/willthames/ansible-ec2-example/blob/master/inventory/hosts

```bash:hostsファイル
[production:children]
production-az-a
#production-az-b
production-web
#production-other-app

[web:children]
production-web
#preprod-web

[production-web:children]
production-web-a
#production-web-b

[production-az-a:children]
production-web-a
#production-other-app-a

[production-web-a]
prod-web-server-[1:99]a
```
インベントリに、本番や開発などの環境や、Web/DBなどのサーバ種別をグループとして持たせると、このようにchildren地獄になります。
単なるインベントリの依存関係なら上記ファイルを見れば済むのですが、得てしてインベントリを分割しているのは、「その単位でvarsを分割定義しているから」ではないでしょうか？

例えば、全共通で定義するvars、本番環境(production)だけで定義するvars、webサーバだけで定義するvars...と言った具合です。一体、どこでこのvarsが定義されているのでしょうか？
それ自体は仕方のないことですが、これをどうにかして分かりやすくできないか、というためにansible-inventory-grapherがあります。

# ansible-inventory-grapherのインストール

公式ページにも書かれているので、そのとおりにpipでインストールしても構いませんが、CentOS7であれば、epel-releaseにパッケージが含まれているので、そちらを使ってもいいでしょう。

```bash
$ sudo yum -y install ansible-inventory-grapher
```

なお、CentOS6系(python2.6)ではうまく動きませんでした。

# ansible-inventory-grapherで可視化

基本的なフォーマットは以下です。

```bash
$ ansible-inventory-grapher -i <インベントリファイル> <パターン> --format "<出力ファイル>"
```

`<パターン>` は、インベントリの中からホスト名を記載します。複数並べても構いません。
`--format` は、出力するファイル名を指定します。 `{}` には、パターンとなるホスト名が入ります。(※ 公式では`{hostname}`と入れていますが、`{}`が正しいようです)

例えば、`test-<ホスト名>.dot`というファイルで、prod-web-server-78aとprod-web-server-28aの2つのインベントリのグループの依存関係を出力するには、以下の通りにします。

```bash
$ ansible-inventory-grapher -i inventory/hosts prod-web-server-78a prod-web-server-28a --format "test-{}.dot"
```

これにより、直下に以下の2つのファイルが出力されていることがわかるかと思います。

* `test-prod-web-server-28a.dot`
* `test-prod-web-server-78a.dot`

# png形式に変換する

上記のファイルはgraphviz形式なので、graphvizをインストールすれば参照可能ですが、そのままだと参照するためには見たい人は必ずgraphvizをインストールする必要が生じてしまいます。それでは配布するにはやや面倒なため、png形式に変換できるようにします。

http://www.graphviz.org/

そのために、graphvizをインストールします。

```
$ sudo yum -y install graphviz
```

graphvizによってpngに変換するためには、以下のようなコマンドを実行します。

```
$ dot -Tpng -o <出力ファイル(png)> <入力ファイル(dot)>
```

例えば、以下のようなコマンドです。

```
$ dot -Tpng -o test-prod-web-server-28a.png test-prod-web-server-28a.dot
```

こうして出来上がったものは、以下のようなイメージになります。(フォントをArialに変更しています)
インベントリだけではなく、そのグループでどのvarsを定義しているかまで可視化できています！

![test-prod-web-server-28a.png](https://qiita-image-store.s3.amazonaws.com/0/16458/d7a0d8df-f3b2-b3d2-bd00-1274483d5353.png)

# ansible-inventory-grapherの惜しいところ

## フォントがTimes New Roman, Bold固定

https://github.com/willthames/ansible-inventory-grapher/blob/master/bin/ansible-inventory-grapher#L51

ここにあるように、フォントがTimes New Romanに固定されています。
これのせいで、ansible-inventory-grapher実行時にgraphvizのオプションを渡すことができるのに、これが効かずにフォント指定が無視されてしまいます。
ですので、もしフォントを変えたい場合は、以下のいずれかを実施する必要があります。いずれの方法でも、書き換えた後にdotコマンドでpngファイルに変換すれば、(フォントがインストールされていれば)ちゃんと好きなフォントで画像を作成することができます。

* ansible-inventory-grapherのソースを書き換える(好きなフォントを指定)

一番単純です。上記のパスでフォントを直接好きなフォントに変更します。4行あるので全部変えましょう。

* ansible-inventory-grapherのソースを書き換えてコマンドラインで指定できるようにする

上記の4行の`face="Times New Roman, Bold"` を消します。すると、`ansible-inventory-grapher`実行時に、`-a`オプションでのフォント指定が効くようになります。

```bash
$ ansible-inventory-grapher -i inventory/hosts prod-web-server-78a prod-web-server-28a --format "test-{}.dot" -a 'node [fontname="Arial"];'
```

上記のように、 `-a 'node [fontname="Arial"];'` という風にattributeを追記すれば、効くようになります。

* dotファイルで出力されたあとにsedなどで書き換える

どうしてもソースをいじりたくない場合はこちら。

## inventoryディレクトリの配下にgroup_varsが存在する必要がある

なぜか、inventoryディレクトリの配下にgroup_varsディレクトリがないと、varsを正しく読んでくれません。
`ansible-inventory-grapher`自体は、単にansibleのVariableManagerを呼んでいるだけなので、詳しくはAnsible側の問題(?)なのですが、呼び方としては以下とほとんど変わらないので、なぜうまく効かないのかはansible側のソースをちゃんと見ないと分かりません…。

http://docs.ansible.com/ansible/dev_guide/developing_api.html

[AnsibleのBest Practices](http://docs.ansible.com/ansible/playbooks_best_practices.html#directory-layout)でも、group_varsとinventoryは並列で置かれていることですし、そもそもansibleの公式で効かないわけがないのですが、なんでだろう…？

もしベストプラクティスのような構成にしている場合は、一旦以下のようにしてgroup_varsを移動させることで、もちろん動くことは動きます。

```
# ダメな例(varsを読んでくれない)
ansible-playbook
  - group_vars
    - varsファイル
  - inventory
    - インベントリファイル

# 大丈夫な例(varsを読んでくれる)
ansible-playbook
  - inventory
    - インベントリファイル
    - group_vars
        - varsファイル
```

## インベントリホストをIPアドレスのままにするとdotによるpng変換時にエラーになる

```bash
$ dot -Tpng -o aig-192.168.33.11.png aig-192.168.33.11.dot
Warning: syntax ambiguity - badly delimited number '192_' in line 1 of aig-192.168.33.11.dot splits into two tokens
Warning: aig-192.168.33.11.dot: syntax error in line 1 near '_168_33_11'
Warning: syntax ambiguity - badly delimited number '192_' in line 4 of aig-192.168.33.11.dot splits into two tokens
Warning: syntax ambiguity - badly delimited number '192_' in line 49 of aig-192.168.33.11.dot splits into two tokens
Warning: syntax ambiguity - badly delimited number '192_' in line 50 of aig-192.168.33.11.dot splits into two tokens
```

graphvizの仕様を知りませんが、先頭を数字にするとダメなようです。
適当なホスト名を与えれば、動くことは動きます。

# ansible-inventory-grapherをどう使いこなすか

1つのplaybookが巨大になりすぎ、一体どんな依存関係になるのか分からないときに、このツールを使えば一発で依存関係がわかるようになります。

![aig-test1.png](https://qiita-image-store.s3.amazonaws.com/0/16458/324fd624-e118-84b1-3e9e-0d19730eefc1.png)

playbookは使っていけば行くほど、varsは膨大になり、インベントリの依存関係も複雑になっていき、いつしか誰も把握できない状態になります。
この画像は最初に見ると圧倒的に理解度が高まるので、たとえばplaybookリポジトリのREADMEに常に最新のイメージを埋め込むようにすれば、非常にわかりやすいplaybookリポジトリになるでしょう。
となると、playbookの更新時にGitのServiceHookなどで自動でこのpngが出力されるようにするとよさそうです。