Ansibleには、roleやtasksとは別に実行タイミングを制御する仕組みがあります。pre_tasksとpost_tasksです。この2つを設計に組み込むと、「roleが動く前にホストの前提条件を確認する」「全タスクが終わった後に通知やヘルスチェックを実行する」という構造が作れます。
この記事では、RHEL 9系~10系/Ubuntu 24.04 LTSを想定環境として、pre_tasks・post_tasksの役割と実践的な設計パターンを説明します。handlerとの実行タイミングの関係も整理します。
この記事のポイント
・pre_tasksはroles・tasks実行前、post_tasksは実行後に動く
・前提条件チェックをpre_tasksに入れると障害を事前に検出できる
・handlerの実行タイミングを把握するとPre/Post設計の信頼性が上がる
・flush_handlersでhandlerをその場で実行する場面を覚えておく
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
Ansibleのタスク実行順序を整理する
Playbookを実行したとき、タスクが動く順序はAnsibleが厳密に定めています。1つのPlay(hosts:ブロック)の中での実行順序は、次のとおりです。
・pre_tasks
・rolesブロックに書いたrole(meta/main.ymlの依存roleも含む)
・tasksブロックに書いたタスク
・notifyで呼ばれたhandler(Play終了前にまとめて実行)
・post_tasks
・post_tasks内でnotifyされたhandler
この順序を把握していないと、「pre_tasksに書いたはずの確認が、roleより後に動いている」という見落としが起きます。
特に注意したいのは、roleとtasksの関係です。rolesブロックを書いた場合、tasksブロックより先にroleが動きます。「Playbookのtasksの前に動く処理」を書きたいときは、tasksに書くのではなくpre_tasksに書く必要があります。
pre_tasksで「始める前の確認」を設計する
pre_tasksの典型的な使い方は、roleやtasksを実行する前の前提条件チェックです。ここで問題を検出して処理を止めると、roleの途中で失敗するより被害が小さくて済みます。1. ディスク残量を事前確認する
パッケージ展開やログ出力を伴うroleを動かす前に、ディスク残量が十分かどうかを確認する例です。# pre_tasks でディスク残量を確認してから role を動かす例 - hosts: webservers pre_tasks: - name: ディスク残量が10GB以上あることを確認する ansible.builtin.assert: that: - ansible_mounts | selectattr('mount', '==', '/') | map(attribute='size_available') | first > 10737418240 fail_msg: "/ のディスク残量が不足しています。role の実行を中断します。" success_msg: "ディスク残量を確認しました。処理を続けます。" roles: - webserver_deploy
roleが展開の途中で止まるより、pre_tasksで止まった方が後片付けが楽です。
2. 対象ホストのOSバージョンを確認する
RHELとUbuntuが混在する環境では、roleを動かす前にホストのOS情報を確認しておくと安全です。# RHEL系のみに適用するroleを動かす前にOS確認 - hosts: all pre_tasks: - name: 対象OSがRHEL系であることを確認する ansible.builtin.assert: that: - ansible_os_family == "RedHat" fail_msg: "このPlaybookはRHEL系OSにのみ対応しています(検出: {{ ansible_os_family }})" roles: - rhel_hardening
3. サービスの起動状態を確認する
依存関係があるサービスが起動しているかどうかをpre_tasksで確認する例です。# NTPサービスの起動を事前確認 - hosts: appservers pre_tasks: - name: chronydが起動していることを確認する ansible.builtin.service_facts: - name: chronydが起動状態であることをassertする ansible.builtin.assert: that: - ansible_facts.services['chronyd.service']['state'] == 'running' fail_msg: "chronydが起動していません。時刻同期を先に確認してください。" roles: - app_deploy
post_tasksで「終わった後の処理」を設計する
post_tasksは、roleとtasksが終わった後に実行します。デプロイ後のヘルスチェック、通知送信、ログ記録など「後処理」に使います。1. デプロイ後のヘルスチェック
Webアプリケーションをデプロイした後、エンドポイントが応答するかどうかを確認する例です。# roleでデプロイ後にヘルスチェックを走らせる - hosts: webservers roles: - app_deploy post_tasks: - name: アプリケーションのヘルスチェックエンドポイントを確認する ansible.builtin.uri: url: "http://{{ inventory_hostname }}:8080/health" return_content: false status_code: 200 retries: 5 delay: 10 register: health_result until: health_result.status == 200
post_tasksで確認が取れない場合はPlaybookが失敗するので、デプロイ成功の判定として使えます。
2. 通知を後処理として入れる
Slackへの通知をpost_tasksに入れると、Playbook全体の成否に関わらず「実行が終わった」ことを知らせる設計ができます。# post_tasks で Slack 通知を送る post_tasks: - name: Slack にデプロイ完了を通知する community.general.slack: token: "{{ slack_token }}" msg: "{{ inventory_hostname }} へのデプロイが完了しました" channel: "#deploy-notify" delegate_to: localhost
Ansibleの構成管理・役割設計をより体系的に学びたい方には、AnsibleによるLinuxサーバー自動化講座で実務設計の全体像を整理できます。
handlerとpre/post_tasksの実行タイミングを理解する
handlerはnotifyで呼ばれたとき「その場ではなくPlaybook末尾にまとめて実行される」というルールがあります。このルールを把握していないと、pre_tasks・post_tasksとhandlerを組み合わせたときに予期しない順序で処理が動くことがあります。1. handlerの実行タイミング
標準の動作では、handlerはPlay全体のタスク(pre_tasks・roles・tasks・post_tasks)が終わってから一度だけ実行されます。次のような設計にしたとき、期待どおりに動かないケースがあります。
# 間違えやすい例: post_tasks の前に httpd を再起動したい tasks: - name: Apacheの設定ファイルを更新する ansible.builtin.template: src: httpd.conf.j2 dest: /etc/httpd/conf/httpd.conf notify: httpd を再起動する post_tasks: - name: Apacheが応答することを確認する # この時点ではhandlerがまだ動いていない ansible.builtin.uri: url: "http://localhost/health" status_code: 200 handlers: - name: httpd を再起動する ansible.builtin.service: name: httpd state: restarted
2. flush_handlersでhandlerをその場で実行する
flush_handlersを使うと、その時点でnotifyされているhandlerをまとめて実行できます。# flush_handlers で handler を先に実行してから post_tasks へ - hosts: webservers tasks: - name: Apacheの設定ファイルを更新する ansible.builtin.template: src: httpd.conf.j2 dest: /etc/httpd/conf/httpd.conf notify: httpd を再起動する - name: notifyされたhandlerをここで先に実行する ansible.builtin.meta: flush_handlers post_tasks: - name: Apacheが応答することを確認する # この時点でhandlerは完了済み ansible.builtin.uri: url: "http://localhost/health" status_code: 200 handlers: - name: httpd を再起動する ansible.builtin.service: name: httpd state: restarted
「設定変更→サービス再起動→後続タスク」という依存関係が必要な設計では、flush_handlersを明示的に入れることを検討してください。
3層構造のPlaybook設計パターン
pre_tasks・roles・post_tasksを使った「前処理→本処理→後処理」の3層構造は、Ansibleでよく使われる設計パターンです。# 前処理・本処理・後処理の3層構造 - hosts: appservers gather_facts: true pre_tasks: - name: ディスク残量を確認する(1GB以上) ansible.builtin.assert: that: - ansible_mounts | selectattr('mount', '==', '/') | map(attribute='size_available') | first > 1073741824 fail_msg: "ディスク残量不足のため処理を中断します" - name: 依存サービスの起動を確認する ansible.builtin.service_facts: - name: MariaDBが起動していることをassertする ansible.builtin.assert: that: - ansible_facts.services['mariadb.service']['state'] == 'running' fail_msg: "MariaDBが起動していません" roles: - app_deploy - nginx_config post_tasks: - name: notifyされたhandlerを先に実行する ansible.builtin.meta: flush_handlers - name: アプリケーションのヘルスチェック ansible.builtin.uri: url: "http://{{ inventory_hostname }}/health" status_code: 200 retries: 3 delay: 10 register: check_result until: check_result.status == 200 - name: デプロイ完了をSlackへ通知する community.general.slack: token: "{{ slack_token }}" msg: "{{ inventory_hostname }} のデプロイが完了しました(ヘルスチェック: OK)" channel: "#ops-notify" delegate_to: localhost
設計上の注意点
pre_tasks・post_tasksを使う上で、実務でよく引っかかるポイントをまとめます。gather_factsの確認
ansible_mountsやansible_os_familyなどのfacts変数を使うpre_tasksを書く場合、gather_facts: trueが前提です。デフォルトはtrueですが、実行速度のためにfalseに設定したPlaybookでは動きません。facts変数を使う前にgather_factsの設定を確認してください。
pre_tasksのfailed_when設計
assertモジュールはfail_msgを設定しておくと、どの条件で止まったかがエラーメッセージから分かります。チームで運用するときは、fail_msgを「何が不足しているか」を具体的に書く習慣を持つと、原因調査の時間が短くなります。
post_tasksの冪等性
post_tasksに書く処理も冪等性(何度実行しても同じ結果になる性質)を意識してください。ヘルスチェックのuriモジュールはGETリクエストなので問題ありません。しかし通知送信のように「実行するたびにSlackにメッセージが届く」処理は、べき等ではありません。重複通知を防ぐ設計(register変数で変更の有無を確認してから送信する等)が必要な場合があります。
block・rescue・alwaysとの組み合わせ
post_tasksをalwaysに似た感覚で使う人がいますが、動作が異なります。post_tasksはPlaybookが途中で失敗すると実行されません。「失敗しても必ず実行したい後処理」がある場合は、block・rescue・alwaysの組み合わせを検討してください。post_tasksは「正常終了後の処理」と理解しておくと設計上の混乱が減ります。
本記事のまとめ
pre_tasks・post_tasksとhandlerの実行タイミングを整理します。| タイミング | 使う場所 | 代表的な用途 |
|---|---|---|
| role・tasks実行前 | pre_tasks | 前提条件チェック(ディスク残量・OS確認・依存サービス確認) |
| role・tasks実行後 | post_tasks | ヘルスチェック・通知送信・ログ記録 |
| notifyされた時点でまとめて | handlers | 設定変更後のサービス再起動 |
| flush_handlersを書いた時点で即時 | tasks内のmeta | post_tasks実行前にhandlerを完了させたい場合 |
handlerのデフォルト動作(Play末尾でまとめて実行)を理解した上で、post_tasksの前にflush_handlersを入れる場面を判断できると、実行順序の設計が確実になります。
>> Ansible構成管理講座の詳細を見る
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:AnsibleのCallback Plugin設計入門|実行結果をJSON出力・Slack通知・ARAで可視化する方法
- この記事の属するカテゴリ:Ansibleへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます