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.txt
playbookを実行します。
[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: xxx
playbookを実行します。
[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: 5
playbook中の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 の使い方