Ansibleでloopを使った動作について

Ansible

検証環境はこちらを利用しています。
CentOS Linux release 7.9.2009 (Core)
python version 2.7.5
ansible 2.9.23

こちらのドキュメントを参考にしています。
https://docs.ansible.com/ansible/2.9_ja/user_guide/playbooks_loops.html

まずは標準的なループから試してみます。localhostに対してdebugモジュールでメッセージを表示する処理を書きました。出力するメッセージの内容をloopに並べて2つ指定し、msgで変数itemを出力するように書きます。

---
- hosts: localhost
  gather_facts: no

  tasks:
    - name: loop test
      debug:
        msg: "{{ item }}"
      loop:
        - "test message1"
        - "test message2"
[root@ansible-ctl ~]# ansible-playbook -i /etc/ansible/hosts loop-test.yml

PLAY [localhost] *****************************************************************************************************************************************

TASK [loop test] *****************************************************************************************************************************************
ok: [localhost] => (item=test message1) => {
    "msg": "test message1"
}
ok: [localhost] => (item=test message2) => {
    "msg": "test message2"
}

PLAY RECAP ***********************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@ansible-ctl ~]#

2つのメッセージが出力されました。ループ処理されていることがわかります。

次はハッシュリストを使用したループを試してみます。ここでもdebugモジュールを使用し、loopで指定した内容をmsgで出力します。ハッシュリストの場合、変数の指定が item.<key> になります。

---
- hosts: localhost
  gather_facts: no

  tasks:
    - name: loop test
      debug:
        msg: "name: {{ item.name }}, message: {{ item.message }}"
      loop:
        - { name: 'test1', message: 'test message1' }
        - { name: 'test2', message: 'test message2' }
[root@ansible-ctl ~]# ansible-playbook -i /etc/ansible/hosts loop-test2.yml

PLAY [localhost] *****************************************************************************************************************************************

TASK [loop test] *****************************************************************************************************************************************
ok: [localhost] => (item={u'message': u'test message1', u'name': u'test1'}) => {
    "msg": "name: test1, message: test message1"
}
ok: [localhost] => (item={u'message': u'test message2', u'name': u'test2'}) => {
    "msg": "name: test2, message: test message2"
}

PLAY RECAP ***********************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@ansible-ctl ~]#

item.nameitem.messageが繰り返し出力されました。
OSユーザを作成する場合などに各ユーザに指定する値を変えて繰り返せるので使えるかもしれません。

次はディクショナリーのループです

---
- hosts: localhost
  gather_facts: no

  tasks:
    - name: loop test
      debug:
        msg: "key: {{ item.key }}, value: {{ item.value }}"
      loop: "{{ dic_test|dict2items }}"
      vars:
        dic_test:
          name: test1
          color: red
[root@ansible-ctl ~]# ansible-playbook -i /etc/ansible/hosts loop-test3.yml

PLAY [localhost] *****************************************************************************************************************************************

TASK [loop test] *****************************************************************************************************************************************
ok: [localhost] => (item={u'key': u'color', u'value': u'red'}) => {
    "msg": "key: color, value: red"
}
ok: [localhost] => (item={u'key': u'name', u'value': u'test1'}) => {
    "msg": "key: name, value: test1"
}

PLAY RECAP ***********************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@ansible-ctl ~]#

変数でディクショナリーを使った場合のループ処理のやり方のようですが、いまいち使えるシチュエーションが思いついていないです。

次は、ループした内容を変数に登録した場合の動作です。loopで2つ値を指定し、shellモジュールを実行した結果を変数echoに登録しています。そして変数echoの内容を出力しているものと、変数echoから特定の値だけ出力する場合の指定方法をみています。

---
- hosts: localhost
  gather_facts: no

  tasks:
    - name: loop test
      shell: "echo {{ item }}"
      loop:
        - "one"
        - "two"
      register: echo

    - name: show var
      debug:
        msg: "{{ echo }}"

    - name: show var echo
      debug:
        msg: "{{ echo.results[0].item }}"
[root@ansible-ctl ~]# ansible-playbook -i /etc/ansible/hosts loop-test4.yml

PLAY [localhost] *****************************************************************************************************************************************

TASK [loop test] *****************************************************************************************************************************************
changed: [localhost] => (item=one)
changed: [localhost] => (item=two)

TASK [show var] ******************************************************************************************************************************************
ok: [localhost] => {
    "msg": {
        "changed": true,
        "msg": "All items completed",
        "results": [
            {
                "ansible_loop_var": "item",
                "changed": true,
                "cmd": "echo one",
                "delta": "0:00:00.003615",
                "end": "2021-08-06 20:50:42.578516",
                "failed": false,
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo one",
                        "_uses_shell": true,
                        "argv": null,
                        "chdir": null,
                        "creates": null,
                        "executable": null,
                        "removes": null,
                        "stdin": null,
                        "stdin_add_newline": true,
                        "strip_empty_ends": true,
                        "warn": true
                    }
                },
                "item": "one",
                "rc": 0,
                "start": "2021-08-06 20:50:42.574901",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "one",
                "stdout_lines": [
                    "one"
                ]
            },
            {
                "ansible_loop_var": "item",
                "changed": true,
                "cmd": "echo two",
                "delta": "0:00:00.003554",
                "end": "2021-08-06 20:50:42.719830",
                "failed": false,
                "invocation": {
                    "module_args": {
                        "_raw_params": "echo two",
                        "_uses_shell": true,
                        "argv": null,
                        "chdir": null,
                        "creates": null,
                        "executable": null,
                        "removes": null,
                        "stdin": null,
                        "stdin_add_newline": true,
                        "strip_empty_ends": true,
                        "warn": true
                    }
                },
                "item": "two",
                "rc": 0,
                "start": "2021-08-06 20:50:42.716276",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "two",
                "stdout_lines": [
                    "two"
                ]
            }
        ]
    }
}

TASK [show var echo] *************************************************************************************************************************************
ok: [localhost] => {
    "msg": "one"
}

PLAY RECAP ***********************************************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@ansible-ctl ~]#

変数echoの中のresultsの中に、ループを実行した内容が登録されているのがわかります。

次は入れ子のリストでのループです。少しこの指定の仕方はよく理解できていないのですが、サンプルを参考に以下のように書きました。

---
- hosts: localhost
  gather_facts: no

  tasks:
    - name: loop test
      shell: "echo {{ item[0] }} {{ item[1] }}"
      loop: "{{ ['alice','bob'] |product([ 'clientdb', 'employeedb', 'providerdb' ])|list }}"

    - name: show var
      debug:
        msg: "{{ ['alice','bob'] |product([ 'clientdb', 'employeedb', 'providerdb' ])|list }}"
[root@ansible-ctl ~]# ansible-playbook -i /etc/ansible/hosts loop-test5.yml

PLAY [localhost] *****************************************************************************************************************************************

TASK [loop test] *****************************************************************************************************************************************
changed: [localhost] => (item=[u'alice', u'clientdb'])
changed: [localhost] => (item=[u'alice', u'employeedb'])
changed: [localhost] => (item=[u'alice', u'providerdb'])
changed: [localhost] => (item=[u'bob', u'clientdb'])
changed: [localhost] => (item=[u'bob', u'employeedb'])
changed: [localhost] => (item=[u'bob', u'providerdb'])

TASK [show var] ******************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        [
            "alice",
            "clientdb"
        ],
        [
            "alice",
            "employeedb"
        ],
        [
            "alice",
            "providerdb"
        ],
        [
            "bob",
            "clientdb"
        ],
        [
            "bob",
            "employeedb"
        ],
        [
            "bob",
            "providerdb"
        ]
    ]
}

PLAY RECAP ***********************************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@ansible-ctl ~]#

動きとしては、リストの各要素に対してもう一つのリストの全要素分、ループが回ったという感じでしょうか。こちらも中々上手く使うにはどうしたらいいのか考え中です。

次は条件を指定してループを回すやり方です。ここではloopではなくuntilを使用します。
ループの条件は実行結果を登録した変数resultstdoutに文字列testが見つかるまで。リトライは5回、間隔は3秒を指定しています。
shellモジュールで文字列tstを出力するだけなので、条件が満たされることはありません。

---
- hosts: localhost
  gather_facts: no

  tasks:
    - name: loop test
      shell: "echo tst"
      register: result
      until: result.stdout.find("test") != -1
      retries: 5
      delay: 3
[root@ansible-ctl ~]# ansible-playbook -i /etc/ansible/hosts loop-test6.yml

PLAY [localhost] *****************************************************************************************************************************************

TASK [loop test] *****************************************************************************************************************************************
FAILED - RETRYING: loop test (5 retries left).
FAILED - RETRYING: loop test (4 retries left).
FAILED - RETRYING: loop test (3 retries left).
FAILED - RETRYING: loop test (2 retries left).
FAILED - RETRYING: loop test (1 retries left).
fatal: [localhost]: FAILED! => {"attempts": 5, "changed": true, "cmd": "echo tst", "delta": "0:00:00.003339", "end": "2021-08-06 21:11:07.081726", "rc": 0, "start": "2021-08-06 21:11:07.078387", "stderr": "", "stderr_lines": [], "stdout": "tst", "stdout_lines": ["tst"]}

PLAY RECAP ***********************************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

[root@ansible-ctl ~]#

5回リトライして、最終的にFAILEDで終了しました。何かしらの処理待ちを行いたい場合に使えそうです。

Ansibleではloopで指定して繰り返す、変数の作り方によって繰り返すといった書き方ができるようです。構築の自動化をする場合、同じ処理だけどサーバによってパラメータを変えたい時などにloopは使えそうです。

コメント

タイトルとURLをコピーしました