「Ansibleで設定ファイルを配布した後、サービスのreloadを毎回手動でやっている」
こんな悩みは、Ansibleを使い始めたエンジニアが必ずぶつかる壁だ。copyモジュールで静的ファイルを配布するだけでは、環境ごとの差異(ホスト名・ポート番号・SSL設定の有無など)を吸収できない。設定変更のたびに手動でサービスを再起動するのは自動化の恩恵を半分しか受けていない状態だ。
この記事では、Ansibleの templateモジュール(Jinja2テンプレートで設定ファイルを動的生成)と handler/notify(設定変更を検知してサービスを自動再起動)の仕組みを、httpd.confを題材に解説する。動作確認環境: RHEL 10 / Rocky Linux 9・Ansible 2.17。概念から実際のPlaybook設計まで順を追って説明する。
この記事のポイント
・templateモジュールはJinja2で変数・条件分岐を埋め込める設定ファイル配布モジュール
・copyとtemplateの違いはJinja2レンダリングの有無だけ
・handlerはnotify時のみPlay終了後に1回だけ実行される通知型タスク
・template + handlerで設定配布→変更検知→再起動を完全自動化できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
copyモジュールではなくtemplateモジュールを使うべき理由
Ansibleで設定ファイルを配布するとき、最初はcopyモジュールを使いがちだ。copyモジュールはローカルのファイルをそのままリモートサーバーへ転送する。シンプルで理解しやすいが、「環境ごとに内容を変えたい」という要件が出た途端に限界が来る。たとえば、次のような状況を考えてほしい。
・開発環境のhttpd.conf:
ServerName dev01.example.com・本番環境のhttpd.conf:
ServerName www.example.comこの差異をcopyモジュールで管理しようとすると、ファイルを環境の数だけ用意してwhen条件で分岐させるか、変数ファイルで制御するか、いずれにせよ構成が複雑になっていく。そこで登場するのがtemplateモジュールだ。
copyとtemplateの違いは1点だけ
両モジュールの機能はほぼ同じ。決定的な違いは Jinja2レンダリングの有無だ。| モジュール | 転送方法 | Jinja2変数埋め込み | 用途 |
|---|---|---|---|
| copy | バイナリそのまま転送 | 不可 | 画像・バイナリ・固定テキスト |
| template | レンダリング後に転送 | 可能(.j2ファイル) | 環境依存の設定ファイル |
.j2 のJinja2テンプレートファイルを用意する。Ansibleは実行時にそのファイルをレンダリング(変数展開・条件分岐の解決)してからリモートサーバーへ転送する。テンプレートファイルはAnsibleコントロールノード(自分のPC)側に置き、生成済みのファイルをリモートへ送る、という流れだ。この仕組みにより「1つのテンプレートファイルから複数環境の設定を生成する」ことが可能になる。
Jinja2テンプレートの基本構文|変数・条件分岐・フィルターを使いこなす
Jinja2はPython製のテンプレートエンジンで、AnsibleはこれをPlaybook変数と統合している。設定ファイル(.j2)に以下の構文を埋め込むことで動的な内容を生成できる。1. 変数の埋め込み({{ }})
最も基本の構文だ。二重波括弧の中にPlaybook変数名を書く。# httpd.conf.j2(変数展開の例) ServerName {{ ansible_hostname }} ServerAdmin {{ server_admin }} Listen {{ http_port }}
vars や group_vars に定義した変数が展開される。ansible_hostname はAnsibleが自動収集するマジック変数(インベントリのホスト名)だ。実行時にはサーバーごとのホスト名に自動で置き換わる。2. 条件分岐({% if %})
環境によって設定内容を切り替えたいときに使う。# SSL設定を本番環境のみ有効化する例 {% if enable_ssl | default(false) %} SSLEngine on SSLCertificateFile {{ ssl_cert_path }} SSLCertificateKeyFile {{ ssl_key_path }} {% endif %}
enable_ssl が true の場合のみSSL設定ブロックが生成される。| default(false) はフィルター(次節参照)でデフォルト値を設定している。開発環境ではSSLを無効のまま、本番環境ではgroup_varsで enable_ssl: true を設定する、という使い方が典型的だ。3. フィルター(|)
変数をパイプ(|)でつないで加工できる。よく使うフィルターを示す。・
{{ http_port | default(80) }}: 変数が未定義なら80を使う・
{{ server_name | upper }}: 大文字に変換・
{{ items | join(',') }}: リストをカンマ区切りの文字列に変換・
{{ path | basename }}: パスからファイル名を取り出す特に
| default() は必須のフィルターだ。変数が未定義のままテンプレートに記述すると実行時にエラーになる(AnsibleUndefinedVariable)。デフォルト値を設定しておけばグループ全体で安全に使いまわせる。templateモジュールでhttpd.confを動的生成する実践例
実際のディレクトリ構成から、Playbook・テンプレートの記述、実行結果まで順を追って説明する。1. ディレクトリ構成
site.yml inventory/ prod/ ← 本番環境インベントリ dev/ ← 開発環境インベントリ group_vars/ webservers.yml ← 共通変数 prod.yml ← 本番環境変数(enable_ssl: true等) roles/ apache/ tasks/ main.yml templates/ httpd.conf.j2 ← Jinja2テンプレート handlers/ main.yml
2. Jinja2テンプレート(roles/apache/templates/httpd.conf.j2)
# httpd.conf.j2 — Jinja2変数・条件分岐は実行時に展開される ServerName {{ ansible_hostname }} ServerAdmin {{ server_admin | default('webmaster@localhost') }} Listen {{ http_port | default(80) }} DocumentRoot {{ document_root | default('/var/www/html') }}
Options Indexes FollowSymLinks AllowOverride {{ allow_override | default('None') }} Require all granted {% if enable_ssl | default(false) %} Listen 443ServerName {{ ansible_fqdn }} SSLEngine on SSLCertificateFile {{ ssl_cert_path }} SSLCertificateKeyFile {{ ssl_key_path }} {% endif %}
3. タスクファイル(roles/apache/tasks/main.yml)
--- - name: httpd.confをテンプレートから配布する ansible.builtin.template: src: httpd.conf.j2 dest: /etc/httpd/conf/httpd.conf owner: root group: root mode: '0644' validate: '/usr/sbin/httpd -t -f %s' notify: Reload Apache
validate パラメーターに注目してほしい。httpd -t -f は設定ファイルの構文チェックを行うコマンドで、これが失敗するとAnsibleはファイルを転送しない。誤った設定を本番サーバーに配布するリスクを防ぐ重要なオプションだ。notify: Reload Apache は次章のhandlerを呼び出す。Apacheの設定項目の詳細(タイムアウト値やKeepAliveの調整など)については、Apache タイムアウト設定の詳細も参考にしてほしい。
4. 実行結果(RHEL 10 / Rocky Linux 9 での出力例)
本番サーバー(web01.example.com)で初回適用した際の出力を示す。# ansible-playbook site.yml -i inventory/prod 実行時の出力 TASK [apache : httpd.confをテンプレートから配布する] ** changed: [web01.example.com] RUNNING HANDLERS ************************************ TASK [apache : Reload Apache] *********************** changed: [web01.example.com] PLAY RECAP ****************************************** web01.example.com : ok=4 changed=2 unreachable=0 failed=0
changed: [web01.example.com] が ok: [web01.example.com] に変わり、handlerは呼ばれない。これがAnsibleの冪等性(べき等性)だ。同じPlaybookを何度実行しても、変更がなければサービスへの影響はゼロになる。templateとhandlerを組み合わせたPlaybook設計を実機で体験したい方は、>> Ansibleハンズオン講座の詳細を見る をご覧ください。
handlerとnotifyの仕組み|設定変更時だけサービスを再起動する
handlerはAnsibleにおける「通知受信型のタスク」だ。通常のタスクとの違いを理解することが、Playbook設計の品質を大きく左右する。1. handlerが解決する問題
設定ファイルを変更した後は必ずサービスをreloadまたはrestartしなければならない。しかし通常のタスクとしてサービス再起動を毎回書くと、2つの問題が発生する。・不必要なサービス再起動: 設定ファイルに変更がなくてもサービスが再起動される。本番環境では瞬断リスクがある
・重複再起動: httpd.confとssl.confを別タスクで配布した場合、サービスが2回再起動される
handlerはこの両方を解決する。「changedになった時だけ」「Play終了後に1回だけ」実行される点が通常のタスクとの決定的な違いだ。
2. handlerの書き方(roles/apache/handlers/main.yml)
--- - name: Reload Apache ansible.builtin.service: name: httpd state: reloaded - name: Restart Apache ansible.builtin.service: name: httpd state: restarted
handlers/main.yml(Role構成の場合)またはPlaybook内の handlers: セクションに定義する。name がhandlerの識別子で、タスク側の notify: と完全一致させる必要がある(大文字・小文字・スペースを含む)。3. 複数タスクから同じhandlerをnotifyする
--- # httpd.confとssl.confを両方配布するタスク - name: httpd.confを配布する ansible.builtin.template: src: httpd.conf.j2 dest: /etc/httpd/conf/httpd.conf validate: '/usr/sbin/httpd -t -f %s' notify: Reload Apache - name: ssl.confを配布する ansible.builtin.template: src: ssl.conf.j2 dest: /etc/httpd/conf.d/ssl.conf notify: Reload Apache
changed になっても、Reload Apache handlerはPlay終了時に1回だけ実行される。Ansibleが重複をキューで自動排除するためだ。4. handlerが実行されるタイミング(実行フロー)
Ansibleのhandlerは通常、Playの全タスク終了後(Play recapの直前)に実行される。実行フローを整理すると次のようになる。・タスク1: httpd.conf配布 → changed → 「Reload Apache」をキューに追加
・タスク2: ssl.conf配布 → changed → 「Reload Apache」をキューに追加(重複は自動排除)
・タスク3: ファイアウォール設定 → ok(変更なし、notifyは発火しない)
・(全タスク終了)
・RUNNING HANDLERS: 「Reload Apache」を1回だけ実行
Play途中でhandlerを強制実行したい場合は、タスクに
ansible.builtin.meta: flush_handlers を挿入する。Apache再起動後にヘルスチェックタスクを実行するような依存関係のある処理では有効なテクニックだ。Postfix(Postfix の基本設定の解説)のmain.cfなど、設定ファイルを持つ他のサービスでも全く同じtemplateとhandlerのパターンが使える。
よくある失敗パターンとトラブルシュート
1. handlerが実行されない
症状: タスクがchanged になっているのに RUNNING HANDLERS が出ない。【重要】原因①: notifyとhandler名が一致していない
タスク側の
notify とhandler側の name は文字列の完全一致が必要だ。大文字・小文字・スペースの違いも区別される。この不一致がhandlerが動かない最多原因なので、名前のコピーペーストミスに注意してほしい。# NG: notify名とhandler名が不一致(大文字・小文字が異なる) notify: reload apache handlers: - name: Reload Apache # OK: 完全一致 notify: Reload Apache handlers: - name: Reload Apache
--check(ドライラン)モードでは実際の変更が発生しないため、handlerも実行されない。動作確認は通常実行で行うこと。原因③: Playbookがエラーで中断した
タスクがエラーで中断すると、その後のhandlerは実行されない。
--force-handlers オプションを使うと、エラー発生時でもhandlerを実行できる(クリーンアップ処理などに活用する)。2. テンプレートで「undefined variable」エラーが出る
症状:fatal: [web01.example.com]: FAILED! => { "msg": "AnsibleUndefinedVariable: 'ssl_cert_path' is undefined" }
# テンプレート内でデフォルト値を指定する SSLCertificateFile {{ ssl_cert_path | default('/etc/pki/tls/certs/localhost.crt') }}
# 変数が定義されている場合のみSSL設定を出力する {% if ssl_cert_path is defined %} SSLCertificateFile {{ ssl_cert_path }} {% endif %}
そもそも変数を定義し忘れているケースが多い。
group_vars/webservers.yml や host_vars/web01.yml に必要な変数を明示的に定義することが根本的な解決策だ。変数の優先順位はhost_vars > group_vars > defaults(Role)の順になる。3. validateエラーで設定ファイルが配布されない
症状: templateモジュールにvalidate を設定した際に構文エラーが検出され、ファイル転送がブロックされる。これは 正常な動作だ。
validate は誤った設定を本番サーバーに送らないための安全装置として機能している。エラーメッセージに従って .j2 テンプレートを修正し、正しい設定ファイルが生成されることを確認してから再実行すること。本記事のまとめ
| やりたいこと | 使う機能 | ポイント |
|---|---|---|
| 環境別設定ファイルを動的生成 | templateモジュール + .j2ファイル | Jinja2で変数・条件を埋め込む |
| 変数が未定義の場合にデフォルト値を使う | {{ 変数 | default(値) }} フィルター |
AnsibleUndefinedVariableを防ぐ |
| 設定変更時だけサービスを再起動 | notify + handler | Play終了後に1回だけ実行 |
| 設定ファイルの構文を転送前にチェック | templateモジュールのvalidate |
httpd -t -f でApache構文検証 |
| 複数タスクから同じhandlerを1回だけ呼ぶ | 同名のnotifyを複数設定 |
Ansibleがキューで重複を自動排除 |
「template + handler」は構成管理の設計パターンとして覚えておく価値が高い。どのサービス(Apache・Postfix・Nginxなど)でも同じ構造で応用できる汎用パターンだからだ。
Ansible実践ハンズオンの詳細を見る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 次のページへ:Ansible動的インベントリ設計|aws_ec2プラグインでEC2ホストを自動取得する仕組みと設定パターン
- 前のページへ:Ansible Vaultでシークレットを安全に管理する方法|パスワードファイルや暗号化変数の実践例
- この記事の属するカテゴリ:Ansibleへ戻る

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