Ansible: 一連の処理をノードごとに順次実行させる2つの方法

# 課題: Ansibleにて一連の処理をノードごとに順次実行させたい

一連の処理を、あるノードにて実行、次に別のあるノードにて実行、さらに別のあるノードにて実行、といった順序で処理を行いたい。
通常のプログラミング言語ならば簡単だが、Ansibleでは、blockにループを適用できない為、回避する必要がある。

例えば以下はNG
```yaml
  - loop: "{{ play_hosts }}"
    when: item == inventory_hostname
    block:
    - debug: msg="{{ inventory_hostname }} step 0"
    - debug: msg="{{ inventory_hostname }} step 1"
```
ERROR! 'loop' is not a valid attribute for a Block にて失敗


# 方法１: play単位でserial: 1を使用

## serialとは

https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_strategies.html#setting-the-batch-size-with-serial


## Playbook例
```yaml:site0.yml
---
- hosts: all
  gather_facts: false
  serial: 1
  tasks:
  - debug: msg="{{ inventory_hostname }} step 0"
  - debug: msg="{{ inventory_hostname }} step 1"
```

## 実行例
```
$ ansible-playbook -i localhost0,localhost1 -c local site0.yml 

PLAY [all] *************************************************************************************************************

TASK [debug] ***********************************************************************************************************
ok: [localhost0] => {
    "msg": "localhost0 step 0"
}

TASK [debug] ***********************************************************************************************************
ok: [localhost0] => {
    "msg": "localhost0 step 1"
}

PLAY [all] *************************************************************************************************************

TASK [debug] ***********************************************************************************************************
ok: [localhost1] => {
    "msg": "localhost1 step 0"
}

TASK [debug] ***********************************************************************************************************
ok: [localhost1] => {
    "msg": "localhost1 step 1"
}

PLAY RECAP *************************************************************************************************************
localhost0                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
localhost1                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
```
ここで、localhost0, localhost1 は、検証用に割り当てたlocalhostの別名

非常にシンプルではあるが、play単位なので小回りは効かない。


taskやblockに対しては、throttle: 1 が利用可能。(ansible2.9 から)
しかし、blockにthrottle: 1をつけた場合、block全体に対する制御ではなく、block内の個別のtaskに対する制御となり、行いたい処理とはならない。

https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_strategies.html#restricting-execution-with-throttle


# 方法2: loop:, when:, include_tasks: を組み合わせて使用

## Playbook例

```yaml:site1.yml
---
- hosts: all
  gather_facts: false
  tasks:

  - loop: "{{ play_hosts }}"
    when: item == inventory_hostname
    include_tasks:
      file: task01.yml
```

```yaml:task01.yml
---
- debug: msg="{{ inventory_hostname }} step 0"
- debug: msg="{{ inventory_hostname }} step 1"
```

## 実行例
```
$ ansible-playbook -i localhost0,localhost1 -c local site1.yml 

PLAY [all] *************************************************************************************************************

TASK [include_tasks] ***************************************************************************************************
skipping: [localhost0] => (item=localhost1) 
skipping: [localhost1] => (item=localhost0) 
included: /home/hiroyukionodera/repo/ansible-throttle-include_tasks/task01.yml for localhost0 => (item=localhost0)
included: /home/hiroyukionodera/repo/ansible-throttle-include_tasks/task01.yml for localhost1 => (item=localhost1)

TASK [debug] ***********************************************************************************************************
ok: [localhost0] => {
    "msg": "localhost0 step 0"
}

TASK [debug] ***********************************************************************************************************
ok: [localhost0] => {
    "msg": "localhost0 step 1"
}

TASK [debug] ***********************************************************************************************************
ok: [localhost1] => {
    "msg": "localhost1 step 0"
}

TASK [debug] ***********************************************************************************************************
ok: [localhost1] => {
    "msg": "localhost1 step 1"
}

PLAY RECAP *************************************************************************************************************
localhost0                 : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
localhost1                 : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

意図通りの順序で実行されている。

なお、実際に使用する際には、バッティングのリスクを避ける為、loop変数をitemから変更しておくことをおすすめします。


# 参考:
- ワークアラウンド - gists · GitHub
https://gist.github.com/tbuchi888/ce5a6af9cdf9212e43bea5e3d3a0fa5d


# コメント
ansible serialでGoogle検索を行うと、この様な状況に対応するための書き込みがいくつかありました。
Task単位の場合には、現在は、問題のあるdelegate_to + run_onceではなく、throttle: 1が使えるというのがAnsibleの進歩でしょうか。