特定のtaskに変更があったときだけ連動して動く処理を定義できるhandlerですが、処理の実行順序で思い違いしがちなところがあったので確認してみました。
(handlerを使ったroleがあるのを忘れて無邪気に次に処理されるroleを書き足したら期待しない実行順序になって処理がコケたというヤラカシがあったのでまとめ)
notify/handlerそのものについてはこちら参照。
- handlerを実装したrole
- post_tasks
- pre_tasks
- post_tasks/pre_tasksでhandler使用
- post_tasks/pre_tasksのnotify先が同じtaskの場合
- (参考) tasksとrolesの併用
- (参考) tasks/roles/pre_tasks/post_tasksの併用
- 処理順について書かれたドキュメント
- 処理順まとめ
- 確認環境
handlerを実装したrole
.
├── playbook.yml
└── roles
├── sample1
│ ├── handlers
│ │ └── main.yml
│ └── tasks
│ └── main.yml
└── sample2
└── tasks
└── main.yml
roleは以下の通り。
- sample1:
tasksとhandlersを実装(taskの結果によってhandlerの中身が起動する) - sample2:
tasksのみ
playbookは以下の通りで、sample1 -> sample2 の順に実行。
--- - hosts: localhost gather_facts: false roles: - sample1 - sample2
ディレクトリ構造のイメージから、
- sample1のtask
- sample1のhandler
- sample2
の順序で実行されそう(roles/sample1で定義された全処理が終わったらroles/sample2)だけど、実際は、
- sample1のtask
- sample2のtask
- sample1のhandler
となり、「全rolesのtaskが処理されてからhandler」の順序で処理されます。
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の後に処理されます。
- roles/sample1のtask
- roles/sample2のtask
- roles/sample1のhandler
- 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の前に処理されます。
- pre_tasksのtask
- roles/sample1のtask
- roles/sample2のtask
- roles/sample1のhandler
- 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のディレクトリ構造の勘違いと違って、
- pre_tasksのtask
- pre_tasksのhandler (ここで
pre_tasksの全処理が完了) - rolesのsample1, sample2, rolesのhandler (ここで
rolesの全処理が完了) - post_tasksのtask
- post_tasksのhandler
「一連のtaskの中で複数回notifyされてもhandlerのtaskが処理されるのは1回」が基本動作だけど、post_tasksとpre_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セクションがちょっとゴチャゴチャしてるけど、この場合の処理順序は以下の通り。
- pre_tasksのtask
- pre_tasksのhandlerでpre_notify
- pre_tasksのhandlerでall notify task
- tasks
- tasksのhandlerでhandler_at_task
- tasksのhandlerでall notify task
- post_tasksのtask
- post_tasksのhandlerでpre_notify
- post_tasksのhandlerでall notify task
(参考) tasksとrolesの併用
例題でよく出てくると思うけど、playの中でtasksとrolesを両方定義した場合の処理順序。
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
この場合は、記述順に関係なく、
- rolesの全てのtasks
- playの全てのtasks
- handlers
- rolesのhandlers
- playのhandlers
の順序。
(参考) tasks/roles/pre_tasks/post_tasksの併用
roles+tasksにさらにpre_tasks/post_tasksを組み込むとroles/tasksの前後に処理されます。
- pre_tasksの全処理
- rolesの全tasks
- tasksの全tasks
- roles/tasksのhandlers
- post_tasksの全処理
処理順について書かれたドキュメント
- Any
pre_tasksdefined 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’smeta/main.ymlrun first, subject to tag filtering and conditionals. See Using role dependencies for more details.- Any
tasksdefined in the play.- Any handlers triggered by the roles or tasks.
- Any
post_tasksdefined in the play.- Any handlers triggered by post_tasks.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html
- handlers notified within
pre_tasks,tasks, andpost_taskssections are automatically flushed at the end of section where they were notified.- handlers notified within
rolessection are automatically flushed at the end oftaskssection, but before anytaskshandlers.- 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。
pre_tasks(task -> handler) -> roles -> tasks -> handler(roles & tasks) -> post_tasks(task -> handler) の順序は明記されてるけど、rolesのhandlerとtasksのhandlerは特に明記されてない気がする。
(実際に動かしてみると、rolesのhandler -> tasksのhandlerの順になった)
処理順まとめ
- pre_tasksのtask
- pre_tasksによるhandler
- 全rolesのtask
- 全tasks
- rolesによる全handler
- tasksによる全handler
- post_tasksのtask
- 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)]
微妙に古いね…