🐔

Ansible の Playbook をインベントリファイル無しで実行する

2024/03/28に公開

インベントリファイルって必須なの?

Ansible Playbook を実行する際、普通は予めインベントリファイルを作成してターゲットノードを指定しておく必要があるらしい。例えば、ドキュメントのHow to build your inventory にはこんな例が載っている。

/etc/ansible/hosts
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 するだけの以下のようなものを使った。

playbook.yaml
- 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 には allungrouped か信条に従って好きな方を使えばよいんじゃないかな。

もしかしたら、デフォルトのインベントリファイル /etc/ansible/hosts がある環境で実行することがあるような Playbook の場合、誤って -i を指定し忘れて実行してしまった場合の被害は all より ungrouped の方が多少は小さいかもしれない。(そうか?

Discussion