zaki work log

作業ログやら生活ログやらなんやら

[Ansible] 複数roleのhandlerとpost_tasks/pre_tasksの処理順序

特定のtaskに変更があったときだけ連動して動く処理を定義できるhandlerですが、処理の実行順序で思い違いしがちなところがあったので確認してみました。
(handlerを使ったroleがあるのを忘れて無邪気に次に処理されるroleを書き足したら期待しない実行順序になって処理がコケたというヤラカシがあったのでまとめ)

notify/handlerそのものについてはこちら参照。

zaki-hmkc.hatenablog.com

handlerを実装したrole

.
├── playbook.yml
└── roles
    ├── sample1
    │   ├── handlers
    │   │   └── main.yml
    │   └── tasks
    │       └── main.yml
    └── sample2
        └── tasks
            └── main.yml

roleは以下の通り。

  • sample1: taskshandlersを実装(taskの結果によってhandlerの中身が起動する)
  • sample2: tasksのみ

playbookは以下の通りで、sample1 -> sample2 の順に実行。

---
- hosts: localhost
  gather_facts: false

  roles:
  - sample1
  - sample2

ディレクトリ構造のイメージから、

  1. sample1のtask
  2. sample1のhandler
  3. sample2

の順序で実行されそう(roles/sample1で定義された全処理が終わったらroles/sample2)だけど、実際は、

  1. sample1のtask
  2. sample2のtask
  3. sample1のhandler

となり、「全rolesのtaskが処理されてからhandler」の順序で処理されます。

github.com

post_tasks

じゃあ「とあるrole/taskでhandlerを使いつつ、その処理後に通常のtask処理を行いたい」場合にどうすればよいかというと、post_tasksを使います。

  post_tasks:
  - name: this is post_tasks
    debug:
      msg: gochisousama desita.

  roles:
  - sample1
  - sample2

post_tasksに記述したtaskは、playbook内の記述順に関係なく、rolesの後に処理されます。

  1. roles/sample1のtask
  2. roles/sample2のtask
  3. roles/sample1のhandler
  4. post_tasksのtask

※ ↑のplaybookは処理順が決まってることを確認するためにpost_tasksを最初に書いているけど、実際は最後に書いた方が直観的に処理順序が分かりやすい。

pre_tasks

postがあるならpreもある。

  post_tasks:
  - name: this is post_tasks
    debug:
      msg: gochisousama desita.

  roles:
  - sample1
  - sample2

  pre_tasks:
  - name: this is pre_tasks
    debug:
      msg: itadakimasu

post_tasksと同じように、playbook内の記述順に関係なく、rolesの前に処理されます。

  1. pre_tasksのtask
  2. roles/sample1のtask
  3. roles/sample2のtask
  4. roles/sample1のhandler
  5. post_tasksのtask

※ ↑のplaybookは処理順が決まってることを確認するためにpost_tasksの例同様pre_tasksを最後に書いているけど(ry

post_tasks/pre_tasksでhandler使用

  post_tasks:
  - name: this is post_tasks
    debug:
      msg: gochisousama desita.
  - name: exec at post_task
    command: ls
    notify: post_notify

  roles:
  - sample1
  - sample2

  handlers:
  - name: pre_notify
    debug:
      msg: handler at pre_notify
    listen: pre_notify
  - name: post_notify
    debug:
      msg: handler at post_notify
    listen: post_notify

  post_tasks:
  - name: this is post_tasks
    debug:
      msg: gochisousama desita.
  - name: exec at pre_task
    command: ls
    notify: pre_notify

これはrolesのディレクトリ構造の勘違いと違って、

  1. pre_tasksのtask
  2. pre_tasksのhandler (ここでpre_tasksの全処理が完了)
  3. rolesのsample1, sample2, rolesのhandler (ここでrolesの全処理が完了)
  4. post_tasksのtask
  5. post_tasksのhandler

「一連のtaskの中で複数回notifyされてもhandlerのtaskが処理されるのは1回」が基本動作だけど、post_taskspre_tasksのhandlerはそれぞれpost_tasksのhandler、pre_tasksのhandlerとして独立して1回ずつ処理されます。

post_tasks/pre_tasksのnotify先が同じtaskの場合

これは、handlerでnotifyで通知されたtaskはpost_tasks/pre_tasksそれぞれで処理されます。
tasksを追加しても同様に個別に処理されます。 「post_tasks内で複数回notifyされても処理は1回」だけど「post_tasks,pre_tasksそれぞれでnotifyされた場合は、それぞれで処理される」(テキストだと説明難しいな。。)

  post_tasks:
  - name: exec at post_task
    command: ls
    notify: post_notify

  pre_tasks:
  - name: exec at pre_task
    command: ls
    notify: pre_notify

  tasks:
  - name: exec at task
    command: ls
    notify: task_notify

  handlers:
  - name: handler_at_task
    debug:
      msg: handler at task
    listen: task_notify
  - name: pre_notify
    debug:
      msg: handler at pre_notify
    listen: pre_notify
  - name: post_notify
    debug:
      msg: handler at post_notify
    listen: post_notify
  - name: all notify task
    debug:
      msg: curry!
    listen:
    - task_notify
    - pre_notify
    - post_notify

handlersセクションがちょっとゴチャゴチャしてるけど、この場合の処理順序は以下の通り。

  1. pre_tasksのtask
  2. pre_tasksのhandlerでpre_notify
  3. pre_tasksのhandlerでall notify task
  4. tasks
  5. tasksのhandlerでhandler_at_task
  6. tasksのhandlerでall notify task
  7. post_tasksのtask
  8. post_tasksのhandlerでpre_notify
  9. post_tasksのhandlerでall notify task

(参考) tasksとrolesの併用

例題でよく出てくると思うけど、playの中でtasksrolesを両方定義した場合の処理順序。

  tasks:
  - name: this task by "tasks" in playbook
    debug:
      msg: hello
  - name: exec at task
    command: ls
    notify: task_notify

  roles:
  - sample1
  - sample2

  handlers:
  - name: handler_at_task
    debug:
      msg: handler at task
    listen: task_notify

この場合は、記述順に関係なく、

  1. rolesの全てのtasks
  2. playの全てのtasks
  3. handlers
    • rolesのhandlers
    • playのhandlers

の順序。

github.com

(参考) tasks/roles/pre_tasks/post_tasksの併用

roles+tasksにさらにpre_tasks/post_tasksを組み込むとroles/tasksの前後に処理されます。

  1. pre_tasksの全処理
  2. rolesの全tasks
  3. tasksの全tasks
  4. roles/tasksのhandlers
  5. post_tasksの全処理

github.com


処理順について書かれたドキュメント

docs.ansible.com

  • Any pre_tasks defined in the play.
  • Any handlers triggered by pre_tasks.
  • Each role listed in roles:, in the order listed. Any role dependencies defined in the role’s meta/main.yml run first, subject to tag filtering and conditionals. See Using role dependencies for more details.
  • Any tasks defined in the play.
  • Any handlers triggered by the roles or tasks.
  • Any post_tasks defined in the play.
  • Any handlers triggered by post_tasks.

https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html


docs.ansible.com

  • handlers notified within pre_tasks, tasks, and post_tasks sections are automatically flushed at the end of section where they were notified.
  • handlers notified within roles section are automatically flushed at the end of tasks section, but before any tasks handlers.
  • handlers are play scoped and as such can be used outside of the role they are defined in.

https://docs.ansible.com/ansible/latest/user_guide/playbooks_handlers.html

2.9だとこの記述は以下のURL。

docs.ansible.com

pre_tasks(task -> handler) -> roles -> tasks -> handler(roles & tasks) -> post_tasks(task -> handler) の順序は明記されてるけど、rolesのhandlerとtasksのhandlerは特に明記されてない気がする。
(実際に動かしてみると、rolesのhandler -> tasksのhandlerの順になった)

処理順まとめ

  1. pre_tasksのtask
  2. pre_tasksによるhandler
  3. 全rolesのtask
  4. 全tasks
  5. rolesによる全handler
  6. tasksによる全handler
  7. post_tasksのtask
  8. post_tasksによるhandler

確認環境

(a2.10) [zaki@cloud-dev pre_post_tasks (master)]$ ansible-playbook --version
ansible-playbook 2.10.2
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/zaki/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/zaki/src/ansible-sample/venv/a2.10/lib64/python3.6/site-packages/ansible
  executable location = /home/zaki/src/ansible-sample/venv/a2.10/bin/ansible-playbook
  python version = 3.6.8 (default, Nov 16 2020, 16:55:22) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]

微妙に古いね…