Ansibleの使い方(ループ処理編)
1 はじめに
loopは、タスクを繰り返すために使用するキーワードです。従来はwith_list,with_items等を使用していましたが、Ansible version 2.5でloopキーワードが追加され、今後はloopキーワードの使用が推奨されるようです。Loops — Ansible Community Documentation
2 検証環境
動作検証をするための環境は以下のとおりです。
192.168.122.0/24
control ------------------------------------- node1
.220 .87
| ホスト名 | 役割 |
|---|---|
| control | コントロールノードとして動作します。Ansibleをインストールします。 |
| node1 | ターゲットノードとして動作します。モジュールが実行されるホストです |
コントロールノード、ターゲットノードのAlmaLinux版数は以下のとおりです。
[root@server ~]# cat /etc/redhat-release AlmaLinux release 9.2 (Turquoise Kodkod)
コントロールノード、ターゲットノードのカーネル版数は以下のとおりです。
[root@server ~]# uname -r 5.14.0-284.11.1.el9_2.x86_64
ansibleの版数は 2.14.9 です。
[root@control ~]# ansible --version ansible [core 2.14.9] config file = /etc/ansible/ansible.cfg configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python3.9/site-packages/ansible ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections executable location = /usr/bin/ansible python version = 3.9.16 (main, Dec 8 2022, 00:00:00) [GCC 11.3.1 20221121 (Red Hat 11.3.1-4)] (/usr/bin/python3) jinja version = 3.1.2 libyaml = True
3 事前準備
ansibleのインストールや公開鍵認証の設定方法などは、Ansibleの使い方(モジュール編) - hana_shinのLinux技術ブログを参照してください。
4 動作確認
4.1 単純なループ処理
playbookを作成します。このPlaybookでは、loopキーワードを使用して、リスト内のファイル名に基づいて3つのファイルを作成します。具体的には、fileモジュールを使用して、/tmp/ディレクトリ直下にfile1.txt、 file2.txt、およびfile3.txtを作成します。各ループでは、item変数にファイル名が自動的に設定されます。なお、itemはAnsibleで予約された変数で、以下の例では、各ループでファイル名が自動的に設定されます。なお、モジュールについては、Ansibleの使い方(モジュール編) - hana_shinのLinux技術ブログを参照してください。
[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
hosts: node1
tasks:
- name: Create file
ansible.builtin.file:
path: "/tmp/{{ item }}"
state: touch
loop:
- file1.txt
- file2.txt
- file3.txtplaybookを実行します。
[root@control ~]# ansible-playbook -i hosts.ini test.yml PLAY [Sample Playbook] ************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************** ok: [192.168.122.220] TASK [Create file] ****************************************************************************************************** changed: [192.168.122.220] => (item=file1.txt) changed: [192.168.122.220] => (item=file2.txt) changed: [192.168.122.220] => (item=file3.txt) PLAY RECAP ************************************************************************************************************** 192.168.122.220 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [root@control ~]#
playbookを実行すると、/tmp直下にfile1.txt、file2.txt、file3.txtが作成されていることがわかります。
[root@node1 ~]# ls -l /tmp/file* -rw-r--r--. 1 root root 0 3月 1 22:27 /tmp/file1.txt -rw-r--r--. 1 root root 0 3月 1 22:27 /tmp/file2.txt -rw-r--r--. 1 root root 0 3月 1 22:27 /tmp/file3.txt
4.2 辞書を使ったループ処理
playbookを作成します。このPlaybookでは、ファイル名とファイルの実行権を指定してファイルを作成します。
[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
hosts: node1
tasks:
- name: Create files with owners using loop
ansible.builtin.file:
path: "/tmp/{{ item.key }}.txt"
mode: "{{ item.value }}"
state: touch
loop:
- { key: 'file1', value: '0777' }
- { key: 'file2', value: '0666' }
- { key: 'file3', value: '0444' }playbookを実行します。
[root@control ~]# ansible-playbook -i hosts.ini test.yml
PLAY [Sample Playbook] **************************************************************************************************
TASK [Create files with owners using loop] ******************************************************************************
changed: [192.168.122.220] => (item={'key': 'file1', 'value': '0777'})
changed: [192.168.122.220] => (item={'key': 'file2', 'value': '0666'})
changed: [192.168.122.220] => (item={'key': 'file3', 'value': '0444'})
PLAY RECAP **************************************************************************************************************
192.168.122.220 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@control ~]#playbookを実行すると、/tmp直下にfile1.txt、file2.txt、file3.txtが作成され、ファイルの実行権が777, 666, 444であることがわかります。
[root@node1 ~]# ls -l /tmp/file* -rwxrwxrwx. 1 root root 0 3月 1 22:28 /tmp/file1.txt -rw-rw-rw-. 1 root root 0 3月 1 22:28 /tmp/file2.txt -r--r--r--. 1 root root 0 3月 1 22:28 /tmp/file3.txt
あと始末をします。
[root@node1 ~]# rm -f /tmp/file*
4.3 ループ変数を変更する方法(loop_var)
デフォルトのループ変数は item です。 loop_varを使用するとループ変数を item 以外のものに変更できます。ここでは、ループ変数をxxxに変更してみます。
[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
hosts: node1
tasks:
- name: Create file
ansible.builtin.file:
path: /tmp/{{ xxx }}
state: touch
loop:
- file1.txt
- file2.txt
- file3.txt
loop_control:
loop_var: xxxplaybookを実行します。
[root@control ~]# ansible-playbook -i hosts.ini test.yml PLAY [Sample Playbook] ************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************** ok: [192.168.122.220] TASK [Create file] ****************************************************************************************************** changed: [192.168.122.220] => (item=file1.txt) changed: [192.168.122.220] => (item=file2.txt) changed: [192.168.122.220] => (item=file3.txt) PLAY RECAP ************************************************************************************************************** 192.168.122.220 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [root@control ~]#
playbookを実行すると、/tmp直下にfile1.txt、file2.txt、file3.txtが作成されていることがわかります。
[root@node1 ~]# ls -l /tmp/file* -rw-r--r--. 1 root root 0 3月 1 22:32 /tmp/file1.txt -rw-r--r--. 1 root root 0 3月 1 22:32 /tmp/file2.txt -rw-r--r--. 1 root root 0 3月 1 22:32 /tmp/file3.txt
あと始末をします。
[root@node1 ~]# rm -f /tmp/file*
4.4 ループの進捗状況を表示する方法(index_var)
各ループでループカウンタとファイル名を表示するplaybookを作成します。なお、リモートホストのファクトは取得の必要がないのでfalseとしています。
[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
hosts: node1
gather_facts: false
tasks:
- name: Counting
loop:
- file1.txt
- file2.txt
- file3.txt
loop_control:
index_var: loop_counter
ansible.builtin.debug:
msg: "{{ loop_counter }} -> {{ item }}"playbookを実行すると、ループカウンタ(0,1,2)とファイル名が表示されていることがわかります。
[root@control ~]# ansible-playbook -i hosts.ini test.yml
PLAY [Sample Playbook] **************************************************************************************************
TASK [Counting] *********************************************************************************************************
ok: [192.168.122.220] => (item=file1.txt) => {
"msg": "0 -> file1.txt"
}
ok: [192.168.122.220] => (item=file2.txt) => {
"msg": "1 -> file2.txt"
}
ok: [192.168.122.220] => (item=file3.txt) => {
"msg": "2 -> file3.txt"
}
PLAY RECAP **************************************************************************************************************
192.168.122.220 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@control ~]#
4.5 ループを一定時間停止する方法(pause)
3秒間隔でファイル名を表示するplaybookを作成します。
[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
hosts: node1
gather_facts: false
tasks:
- name: Counting
loop:
- file1.txt
- file2.txt
- file3.txt
loop_control:
index_var: loop_counter
pause: 3
ansible.builtin.debug:
msg: "{{ loop_counter }} -> {{ item }}"playbookを実行します。3秒間隔で実行されていることがわかります。
[root@control ~]# ansible-playbook -i hosts.ini test.yml
PLAY [Sample Playbook] **************************************************************************************************
TASK [Counting] *********************************************************************************************************
ok: [192.168.122.220] => (item=file1.txt) => {
"msg": "0 -> file1.txt"
}
ok: [192.168.122.220] => (item=file2.txt) => {
"msg": "1 -> file2.txt"
}
ok: [192.168.122.220] => (item=file3.txt) => {
"msg": "2 -> file3.txt"
}
PLAY RECAP **************************************************************************************************************
192.168.122.220 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4.6 指定した条件にマッチするまでタスクを繰り返し実行する方法(until)
playbookを作成します。lsコマンドの実行結果はregisterキーワードによって、変数command_result.rcに格納されます。そして、untilキーワードでcommand_result.rcが0になるまで、lsコマンドを3秒間隔(delay)で5回実行(retries)します。
[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
hosts: node1
tasks:
- name: Wait until a condition is met
command: ls /tmp/test.txt
register: command_result
until: command_result.rc == 0
delay: 3
retries: 5playbook中のcommand_result.rcは、以下のようになります。debugモジュールを使ってcommand_resultの中身を確認することができます。debugモジュールの使い方は、Ansibleの使い方(デバッグ編) - hana_shinのLinux技術ブログを参照してください。
"msg": {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"attempts": 1,
"changed": true,
"cmd": [
"ls",
"/tmp/test.txt"
],
"delta": "0:00:00.011117",
"end": "2024-03-02 21:11:50.231665",
"failed": false,
"msg": "",
"rc": 0,
"start": "2024-03-02 21:11:50.220548",
"stderr": "",
"stderr_lines": [],
"stdout": "/tmp/test.txt",
"stdout_lines": [
"/tmp/test.txt"
]
}
}事前準備として、/tmp/test.txtが存在しないことを確認します。
[root@node1 ~]# rm /tmp/test.txt
playbookを実行します。/tmp/test.txtがないため、lsコマンドが失敗するため、5回実行されていることがわかります。
[root@control ~]# ansible-playbook -i hosts.ini test.yml
PLAY [Sample Playbook] **************************************************************************************************
TASK [Wait until a condition is met] ************************************************************************************
FAILED - RETRYING: [192.168.122.220]: Wait until a condition is met (5 retries left).
FAILED - RETRYING: [192.168.122.220]: Wait until a condition is met (4 retries left).
FAILED - RETRYING: [192.168.122.220]: Wait until a condition is met (3 retries left).
FAILED - RETRYING: [192.168.122.220]: Wait until a condition is met (2 retries left).
FAILED - RETRYING: [192.168.122.220]: Wait until a condition is met (1 retries left).
fatal: [192.168.122.220]: FAILED! => {"attempts": 5, "changed": true, "cmd": ["ls", "/tmp/test.txt"], "delta": "0:00:00.008907", "end": "2024-03-01 22:43:38.125986", "msg": "non-zero return code", "rc": 2, "start": "2024-03-01 22:43:38.117079", "stderr": "ls: '/tmp/test.txt' にアクセスできません: そのようなファイルやディレクトリはありません", "stderr_lines": ["ls: '/tmp/test.txt' にアクセスできません: そのようなファイルやディレクトリはありません"], "stdout": "", "stdout_lines": []}
PLAY RECAP **************************************************************************************************************
192.168.122.220 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
[root@control ~]#次はplatbook実行中に、ターゲットノードで/tmp/test.txtを作成してみます。
playbookを実行します。
[root@control ~]# ansible-playbook -i hosts.ini test.yml
ターゲットノードで/tmp/test.txtを作成します。
[root@node1 ~]# touch /tmp/test.txt
/tmp/test.txtが作成されたので、3回目でlsコマンドが正常終了しているとがわかります。
[root@control ~]# ansible-playbook -i hosts.ini test.yml PLAY [Sample Playbook] ************************************************************************************************** TASK [Wait until a condition is met] ************************************************************************************ FAILED - RETRYING: [192.168.122.220]: Wait until a condition is met (5 retries left). FAILED - RETRYING: [192.168.122.220]: Wait until a condition is met (4 retries left). changed: [192.168.122.220] PLAY RECAP ************************************************************************************************************** 192.168.122.220 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [root@control ~]#
4.7 フィルタを使った繰り返し
playbookを作成します。loop は反復処理を指示するためのキーワードであり、同じタスクを複数回繰り返す際に使用します。range は整数の範囲を生成するための関数です。以下の例では、rangeは1から3までの整数を出力します。
[root@control ~]# vi test.yml
[root@control ~]# cat test.yml
- name: Sample Playbook
hosts: node1
gather_facts: false
tasks:
- name: Create files with loop and filter
ansible.builtin.file:
path: "/tmp/file{{ item }}.txt"
state: touch
loop: "{{ range(1, 4) }}"playbookを実行します。
[root@control ~]# ansible-playbook -i hosts.ini test.yml PLAY [Sample Playbook] ************************************************************************************************** TASK [Create files with loop and filter] ******************************************************************************** changed: [192.168.122.220] => (item=1) changed: [192.168.122.220] => (item=2) changed: [192.168.122.220] => (item=3) PLAY RECAP ************************************************************************************************************** 192.168.122.220 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [root@control ~]#
playbookを実行すると、/tmp直下にfile1.txt、file2.txt、file3.txtが作成されていることがわかります。
[root@node1 ~]# ls -l /tmp/file* -rw-r--r--. 1 root root 0 3月 1 22:45 /tmp/file1.txt -rw-r--r--. 1 root root 0 3月 1 22:45 /tmp/file2.txt -rw-r--r--. 1 root root 0 3月 1 22:45 /tmp/file3.txt
Y 参考図書
本記事の作成に以下の図書を参考にしました。

Ansible実践ガイド 第4版[基礎編] impress top gearシリーズ

Ansibleクックブック impress top gearシリーズ
Ansibleクックブック (impress top gear) 単行本
Z 参考情報
私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ
Ansible の使い方

