CUBE SUGAR CONTAINER

技術系のこと書きます。

Ansible: Dynamic Inventory を使ってみる

通常の Inventory は ini 形式を拡張したテキストファイルで、中には Ansible で管理したいホストと変数の情報が書き込まれている。 しかし、これだとホストの台数や名前などが動的に変化するシチュエーションでメンテナンスが難しい。 あとは、ホストの情報が別の場所で管理されているパターンも二重管理になる可能性がある。 そうした場合には Dynamic Inventory という機能を使って動的に Inventory を生成するやり方があるようだ。

Ansible をインストールする

検証用の環境には CentOS7 を使った。

$ cat /etc/redhat-release 
CentOS Linux release 7.1.1503 (Core) 
$ uname -r
3.10.0-229.11.1.el7.x86_64

EPEL 経由で Ansible をインストールする。

$ sudo yum -y install epel-release
$ sudo yum -y install ansible

Inventory

まずは通常の Inventory を使うパターンから。

まずは動作確認用の Playbook を用意する。 といっても、実行されることさえ確認できれば良いだけなのでシンプルに。

$ cat << EOF > site.yml
---
- hosts:
  - all
  tasks:
    - name: sample
      debug: msg="{{ message }}"
EOF

次に Inventory を用意する。 sample グループの中に localhost ホストを作った。 localhost なのでコネクションには ssh を使わず local で実行する。

$ cat << EOF > hosts 
[sample]
localhost ansible_connection=local

[sample:vars]
message=Hello, World!
EOF

動作確認

通常の手順通り実行すれば上手くいく。

$ ansible-playbook -i hosts site.yml -v

PLAY [all] ******************************************************************** 

GATHERING FACTS *************************************************************** 
ok: [localhost]

TASK: [sample] **************************************************************** 
ok: [localhost] => {
    "msg": "Hello, World!"
}

PLAY RECAP ******************************************************************** 
localhost                  : ok=2    changed=0    unreachable=0    failed=0   

Dynamic Inventory

通常の Inventory の動作が確認できたので、次は上記を Dynamic Inventory 化してみる。

Dynamic Inventory は実行可能ファイルとして作成する。 その際、言語は特に問わない。 ただ、API としての取り決めが幾つかある。 まず、入力に関してはホストの名前一覧を取得する '--list' を引数にした実行と、更にそこで得たホスト名を元にホスト毎の変数を取得する '--host {hostname}' を引数にした実行が行われる。 そして、出力に関しては所定のフォーマットに沿った JSON 文字列になっている必要がある。

それでは、先ほど作った Inventory を Dynamic Inventory にしてみる。

$ cat << EOF > hosts.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import absolute_import
from __future__ import print_function

import sys
import json


hosts = {}
hosts_vars = {}

hosts['sample'] = {
    'hosts': [
        'localhost',
    ]
}

hosts_vars['localhost'] = {
    'ansible_connection': 'local',
    'message': 'Hello, World!'
}

if sys.argv[1] == '--list':
    hosts_json = json.dumps(hosts)
    print(hosts_json)
else:
    hostname = sys.argv[2]
    hosts_vars_json = json.dumps(hosts_vars[hostname])
    print(hosts_vars_json)
EOF

Dynamic Inventory には実行権限を付与する。

$ chmod +x hosts.py

動作確認

まずは、出力される JSON 文字列を確認しておく。 前述した通り、引数には '--list' が付くパターンと '--host {hostname}' が付くパターンがある。 '--list' が付くパターンでは各グループに所属しているホスト名を 'hosts' 以下に羅列する。 '--host {hostname}' のパターンではホストで使用する変数を返す。 ホスト単位ということは Inventory を使うよりも細かい単位で変数を制御できるみたい。

$ ./hosts.py --list
{"sample": {"hosts": ["localhost"]}}
$ ./hosts.py --host localhost
{"message": "Hello, World!", "ansible_connection": "local"}

動作確認

実行する際に変わるところは -i で指定する Inventory を先ほど作成した hosts.py にするところだけ。

$ ansible-playbook -i hosts.py site.yml -v

PLAY [all] ******************************************************************** 

GATHERING FACTS *************************************************************** 
ok: [localhost]

TASK: [sample] **************************************************************** 
ok: [localhost] => {
    "msg": "Hello, World!"
}

PLAY RECAP ******************************************************************** 
localhost                  : ok=2    changed=0    unreachable=0    failed=0   

ばっちり動いた。

まとめ

今回は最もシンプルな形で Dynamic Inventory を試してみた。 実際に Dynamic Inventory が必要になるのは、ホストの情報が別の場所で集中管理されているパターンになるはず。 それは例えば IaaS を使っていたり、ホストの台帳が別の場所で管理されていたり、あるいは監視システムに登録済みのホストを取ってきたい場合とかとか。 むしろ、そういったパターン以外で使うと無用に複雑化してイマイチかな。