Ansible: Playbookの繰り返し制御の色々 (6)
今回もAnsible
のPlaybook
における繰り返し制御について、学んでいきます。
引き続き題材は本家のドキュメントから。
シリーズはこちら。
目次
Looping over the inventory
インベントリを使ったループ、ということでこれを実現する方法は複数あるそうです。
一つは、 with_items
に play_hosts
または groups
変数を利用した方法、
もう一つは with_inventory_hostnames
を使った方法、と紹介されています。
実際に動かしてみるとわかりやすいので、やってみましょう。
まずは、インベントリファイルとして下記を用意します。
[localnet] localhost [www] www1 www2
これを利用するようにして、Playbookを実行してみます。
まずは、一つ目の with_items
を使う方法です。
- hosts: localnet connection: local tasks: # show all the hosts in the inventory - debug: msg: "{{ item }}" with_items: - "{{ groups['all'] }}" # show all the hosts in the current play - debug: msg: "{{ item }}" with_items: - "{{ play_hosts }}"
tasksの中は、例文通りです。
こちらを実行すると、下記のようになります。
PLAY [localnet] ****************************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************************** ok: [localhost] TASK [debug] ********************************************************************************************************************* ok: [localhost] => (item=localhost) => { "item": "localhost", "msg": "localhost" } ok: [localhost] => (item=www1) => { "item": "www1", "msg": "www1" } ok: [localhost] => (item=www2) => { "item": "www2", "msg": "www2" } TASK [debug] ********************************************************************************************************************* ok: [localhost] => (item=localhost) => { "item": "localhost", "msg": "localhost" } PLAY RECAP *********************************************************************************************************************** localhost : ok=3 changed=0 unreachable=0 failed=0
一つ目のタスクで「全てのグループのホスト」、二つ目のタスクで「Playbookで指定しているホスト(グループ)」が表示されました。
今度は、二つ目の with_inventory_hostnames
を使う方法です。
- hosts: localnet connection: local tasks: # show all the hosts in the inventory - debug: msg: "{{ item }}" with_inventory_hostnames: - all # show all the hosts matching the pattern, ie all but the group www - debug: msg: "{{ item }}" with_inventory_hostnames: - all:!www
これも、tasksの中は例文通りですね。
こちらを実行すると、下記のようになります。
PLAY [localnet] ****************************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************************** ok: [localhost] TASK [debug] ********************************************************************************************************************* ok: [localhost] => (item=www1) => { "item": "www1", "msg": "www1" } ok: [localhost] => (item=localhost) => { "item": "localhost", "msg": "localhost" } ok: [localhost] => (item=www2) => { "item": "www2", "msg": "www2" } TASK [debug] ********************************************************************************************************************* ok: [localhost] => (item=localhost) => { "item": "localhost", "msg": "localhost" } PLAY RECAP *********************************************************************************************************************** localhost : ok=3 changed=0 unreachable=0 failed=0
結果は同じ形(実行順がやや違いますが)となっています。
指定した意味としては、一つ目のタスクで「全てのホスト」、二つ目のタスクで「wwwグループ以外で絞り込んだホスト」、ということになります。
このように、自分の必要に応じた形でホストを使ったループ処理が実現できます。
ちなみに、 with_inventory_hostnames
で利用している絞り込みのパターンは、こちらの Patterns(本家) に詳細があります。
Loop Control
ここでは、ループの制御のバージョンによる進化が書かれています。
バージョン2.0では、with_loops
とタスクのインクルードが使えました。
これにより、一連のタスクを一度にループするできます。
そして Ansible では標準で、ループに item
という名前の変数を使います。そして、このために「外側」のループの item
の値は上書きされてしまいます。
バージョン2.1では、 loop_control
オプションにより、ループに使用する変数名を指定できます。
下記の例を見てみます。
# main.yml - include: inner.yml with_items: - 1 - 2 - 3 loop_control: loop_var: outer_item # inner.yml - debug: msg: "outer item={{ outer_item }} inner item={{ item }}" with_items: - a - b - c
inner.yml
以下が内側のループに相当するところですね。
ここで、外側である main.yml
の loop_control
で設定した outer_item
変数が利用されています。
また、注意書きとして記載されていますが、 Ansible がすでに定義されている変数名をループ変数として利用しようとしているのを検知したら、エラーとしてタスク失敗としてしまう、という動作となるようです。
次に、バージョン2.2です。
複雑なデータ構造でループを行なった場合、実行時の表示結果が少しうるさい(busy)ものとなります。
これにラベル(label
)ディレクティブをつけることで抑制できます。
- name: create servers digital_ocean: name: "{{ item.name }}" state: present with_items: - name: server1 disks: 3gb ram: 15Gb network: nic01: 100Gb nic02: 10Gb ... loop_control: label: "{{item.name}}"
上記の例だと、サーバ名を表示させることになるので、何を操作しているかもわかりますし、すっきりとした表示になります。
他のループ制御オプションとして、pause
があります。
こちらは、繰り返し実行ごとに指定した秒を待たせる、というものになります。
# main.yml - name: create servers, pause 3s before creating next digital_ocean: name: "{{ item }}" state: present with_items: - server1 - server2 loop_control: pause: 3
こちらは name
にある説明の通り、次のサーバを作る前に3秒待つ、という動作になります。
Loops and Includes in 2.0
Ansible のバージョン2.0では loop_control
を使えません。
そのため、先ほど紹介した「外側」のループの変数名を変更したい場合には、 set_fact
を利用して実現することができます。
# main.yml - include: inner.yml with_items: - 1 - 2 - 3 # inner.yml - set_fact: outer_item: "{{ item }}" - debug: msg: "outer item={{ outer_item }} inner item={{ item }}" with_items: - a - b - c
古いバージョンしか使えない環境であれば、このような回避方法があるようです。
Writing Your Own Iterators
最後に、自分でループの記述をしたい場合、 Developing Plugins (本家) にプラグイン開発開始のための情報があります。
今まで見てきたように多くの機能が存在しているので、参照する実装は豊富に存在しています。
まとめ
ということで、ループについて一通り確認してきました。
やり始めてからだいぶ時間かけてしまいましたが、何とかやりきれてよかったです。
もう少し参照しやすい形に整理もしたいですね。