Ansible の Playbook をインベントリファイル無しで実行する
インベントリファイルって必須なの?
Ansible Playbook を実行する際、普通は予めインベントリファイルを作成してターゲットノードを指定しておく必要があるらしい。例えば、ドキュメントのHow to build your inventory にはこんな例が載っている。
mail.example.com
[webservers]
foo.example.com
bar.example.com
[dbservers]
one.example.com
two.example.com
three.example.com
なるほどわかった。
わかったんだが、場合によってはやんごとなき理由によって既存のインベントリファイルを編集したくないとか、あるいは、何ならインベントリファイル自体を作成したくない時もあるよね。(ない?
と言う訳で、Playbook をインベントリファイル無しで実行する方法を調べてみた。
ヘルプ見るとインベントリファイル無しでも実行できそう?
ansible-playbook --help
を実行すると、インベントリファイルを指定する -i
オプションの記載は以下のようになっている。
...
-i INVENTORY, --inventory INVENTORY, --inventory-file INVENTORY
specify inventory host path or comma separated host list.
--inventory-file is deprecated. This argument may be specified
multiple times.
...
specify inventory host path or comma separated host list
って書いてあるんだからできるんじゃね?と思って target-host
というホストを指定して実行できるか試してみた。
$ ansible-playbook -i target-host playbook.yaml
[WARNING]: Unable to parse /home/mitsuru/target-host as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the
implicit localhost does not match 'all'
PLAY [all] ******************************************************************************
skipping: no hosts matched
PLAY RECAP ******************************************************************************
ダメじゃん。target-host
をファイル名だと思ってるじゃん。
ちなみに、Playbook は touch tmp.txt
するだけの以下のようなものを使った。
- hosts: all
gather_facts: false
tasks:
- name: touch tmp.txt
ansible.builtin.file:
path: tmp.txt
state: touch
カンマが重要だった…
実は comma separated host list
って書いてある通り、どうもカンマ区切りじゃなきゃホストリストとみなしてくれないらしい…
と言う訳で target-host,target-host
と同じホストを 2 回指定してみた。
$ ansible-playbook -i target-host,target-host playbook.yaml
PLAY [all] ******************************************************************************
TASK [touch tmp.txt] ********************************************************************
changed: [target-host]
PLAY RECAP ******************************************************************************
target-host : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
出来た!しかし同じホストを 2 回指定するのもキモいしめんどい。
と思ったらそんな必要は無かった。
$ ansible-playbook -i target-host, playbook.yaml
PLAY [all] ******************************************************************************
TASK [touch tmp.txt] ********************************************************************
changed: [target-host]
PLAY RECAP ******************************************************************************
target-host : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ダメだった時との違いは target-host,
と最後にカンマが付いてることだけ。なんだそりゃ!
これもキモいが(2 回指定するよりは)めんどうではない。
ちなみに、,target-host
と最初にカンマを付けても大丈夫だったので、信条に従って好きな方を使えばよいんじゃないかな。最初につけた方がカンマを見逃さなくてよいかもしれない。(そうか?
もちろん最初から対象が 2 つ以上あればこんな無茶な指定はしなくても普通に実行できた。
$ ansible-playbook -i target-host1,target-host2 playbook.yaml
PLAY [all] ******************************************************************************
TASK [touch tmp.txt] ********************************************************************
changed: [target-host2]
changed: [target-host1]
PLAY RECAP ******************************************************************************
target-host1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
target-host2 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ちゃんとドキュメントに書いてあった…
-i
の引数にホストリストを渡せるのは ansible.builtin.host_list
というインベントリプラグインのおかげらしい。
そしてドキュメントの Synopsis にちゃんと書いてあった。
This plugin only applies to inventory strings that are not paths and contain a comma.
適当訳:このプラグインはインベントリ文字列がパスじゃなくてカンマが含まれている時だけ使われるよ。
しかも、以下のような例も載ってた。
...
# just use localhost
# ansible-playbook -i 'localhost,' play.yml -c local
localhost
1 個だけ指定の時に最後にカンマが付いてる!
ドキュメントって重要やな…
パスじゃなくてとは…
ふとドキュメントの「パスじゃなくて」が気になったので ansible.builtin.host_list
インベントリプラグインのソースを調べてみた。
def verify_file(self, host_list):
valid = False
b_path = to_bytes(host_list, errors='surrogate_or_strict')
if not os.path.exists(b_path) and ',' in host_list:
valid = True
return valid
ターゲットホストとして指定した文字列がファイル名に一致してたらダメそうじゃん!
と言う訳で試してみた。
$ touch target-host,
$ ls -l target-host,
-rw-r--r-- 1 mitsuru mitsuru 0 Mar 28 01:16 target-host,
$ ansible-playbook -i target-host, playbook.yaml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the
implicit localhost does not match 'all'
PLAY [all] ******************************************************************************
skipping: no hosts matched
PLAY RECAP ******************************************************************************
やっぱりダメだった!
とは言え、カンマが必須なので target-host,
とか ,target-host
とか target-host1,target-host2
とか言うファイルがカレントディレクトリにある確率は非常に低い気がするしこの事象に遭遇することはあんまりなさそうかな。
Playbook の hosts は?
ところで、Playbook にはターゲットホストを指定する hosts
が必須なので、ここに何か指定しなきゃならないが、今回のように -i
で直接指定することを意図した Playbook の場合はどうすればよいのだろうか?
まぁ答えは簡単で、上の例のように all
を指定しておけば大丈夫だった。と言うのも、all
は全てのターゲットホストを含む暗黙のグループ(指定なしで勝手に作られるグループ)だから。
で、暗黙のグループにはもう 1 つ ungrouped
と言うのもあるらしい。コイツは他のどのグループにも属さないターゲットホストを含む暗黙のグループらしいので、これでもいいんじゃないの?と思って試してみたらやっぱり大丈夫だった。
と言う訳で、hosts
には all
か ungrouped
か信条に従って好きな方を使えばよいんじゃないかな。
もしかしたら、デフォルトのインベントリファイル /etc/ansible/hosts
がある環境で実行することがあるような Playbook の場合、誤って -i
を指定し忘れて実行してしまった場合の被害は all
より ungrouped
の方が多少は小さいかもしれない。(そうか?
Discussion