はじめに
テンプレートによる自動化では、1 つのソースを保持しながら、各ホストの入力から設定を生成します。変数とファクトからファイルを生成し、組み込みの Jinja フィルターでヘッダーを変換して、条件分岐を選択し、ハンドラーで検証済みの nginx 設定を読み込みます。実際の HTTP リクエストと無効な候補の拒否により、ソーステキスト、管理対象ファイル、稼働サービスの動作の違いを確認します。
ホスト変数とファクトを管理対象内容に反映する
このステップでは、1 つのページテンプレートを作成し、同じソースから両対象に異なる実ファイルを生成します。
準備済みノードには、前提として nginx がインストールされています。ここでの作業は、パッケージの再インストールではなく、設定の生成と適用です。テンプレートは、各対象の変数コンテキストでコントローラー上でレンダリングされ、その後、管理対象のコピー先に転送されます。
コントローラーのプロジェクトに移動し、templates ディレクトリを作成します。mkdir -p は、必要ならディレクトリを作成し、すでに存在していても失敗しません。
cd /home/labex/project
mkdir -p templates
型を持つホスト入力を定義します。環境と待ち受けポートは異なり、引用符なしの maintenance_mode 値は YAML のブール値です。
cat > host_vars/web1.yml <<'YAML'
environment_label: development
service_port: 8081
maintenance_mode: true
YAML
cat > host_vars/web2.yml <<'YAML'
environment_label: production
service_port: 8082
maintenance_mode: false
YAML
二重波括弧内の Jinja 式は、テキストに値を挿入します。.j2 拡張子はテンプレートソースの便利な慣例であり、管理対象のコピー先ファイル名に必須ではありません。現在のインベントリ名、環境を表す変数、検出したディストリビューションのファクトを使うソースを 1 つ作成します。
cat > templates/page.txt.j2 <<'JINJA'
host={{ inventory_hostname }}
environment={{ environment_label }}
distribution={{ ansible_facts.distribution }}
JINJA
レンダリングには template モジュールを使用します。Copy は通常、これらの Jinja 式を解釈せずに、ソースのバイト列を転送します。ディストリビューション値を利用する前にファクトを収集し、管理対象の所有者と権限を明示的に宣言してください。
cat > templates.yml <<'YAML'
- name: Render host-specific website content
hosts: all
gather_facts: true
become: true
tasks:
- name: Prepare the rendered site directory
ansible.builtin.file:
path: /var/www/template-site
state: directory
owner: root
group: root
mode: "0755"
- name: Render each host's website content
ansible.builtin.template:
src: templates/page.txt.j2
dest: /var/www/template-site/index.html
owner: root
group: root
mode: "0644"
YAML
両方の管理対象ページを生成します。
ansible-playbook templates.yml --syntax-check
ansible-playbook templates.yml
実際の対象ファイルを読みます。
ansible all -m ansible.builtin.command -a 'cat /var/www/template-site/index.html'
各ページは、自身のホスト、対応する development/production 環境、distribution=Ubuntu を示すはずです。コントローラーには再利用可能なソースが 1 つのまま残り、2 つの管理対象出力は異なります。
組み込みフィルターと条件付きの設定分岐を追加する
このステップでは、各ホストの入力に応じて HTTP 動作を変える、有効な nginx 設定を生成します。
Jinja フィルターは、パイプ記号で値を変換します。upper はテキストを大文字に変換する組み込みフィルターであり、カスタムプラグインではありません。Jinja の制御文は、パーセントと波括弧の区切り文字を使います。以下の if/else/endif 分岐は、ブール入力に基づいて 1 つの maintenance ヘッダーを選択します。
完全な nginx 設定テンプレートを作成します。ポート、環境ヘッダー、保守ヘッダーは、すべてホスト別データから得られます。root と index の設定は、すでに生成したページを配信します。
cat > templates/nginx.conf.j2 <<'JINJA'
events {}
http {
server_tokens off;
server {
listen {{ service_port }};
root /var/www/template-site;
index index.html;
add_header X-Environment "{{ environment_label | upper }}" always;
{% if maintenance_mode %}
add_header X-Maintenance "enabled" always;
{% else %}
add_header X-Maintenance "disabled" always;
{% endif %}
}
}
JINJA
既存の Playbook に、設定とサービスのタスク、および名前付き再読み込みハンドラーを追記します。template タスクの validate コマンドは、管理対象設定を置き換える前に、一時的な生成済み候補(%s)をテストします。変更された有効な設定はハンドラーに通知し、通常の service タスクは nginx を稼働状態に保ちます。
cat >> templates.yml <<'YAML'
- name: Render and validate the nginx candidate before replacement
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
validate: /usr/sbin/nginx -t -c %s
notify: Reload nginx
- name: Keep the rendered site service running
ansible.builtin.service:
name: nginx
state: started
handlers:
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
YAML
完全な Playbook を適用します。
ansible-playbook templates.yml
設定タスクは両ホストを変更し、再読み込みハンドラーを起動するはずです。web1 はポート 8081、web2 はポート 8082 で調べます。curl -i は、本文とともに応答ヘッダーを含めます。
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
ansible web2 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8082/'
web1 は X-Environment: DEVELOPMENT と X-Maintenance: enabled を返し、web2 は X-Environment: PRODUCTION と X-Maintenance: disabled を返すはずです。両方の本文は、正しいホスト別ページを保ちます。ヘッダーの日付は変わるため、データに基づく値と成功ステータスを比較してください。
入力を変え、無効な候補を拒否する
このステップでは、テンプレートを編集せずに永続的な入力を更新し、実際の稼働結果を観察して、検証が動作中の設定を保護することを証明します。
既存のポートを維持しながら、web1 の保存された環境を staging、maintenance mode を false に変更します。
cat > host_vars/web1.yml <<'YAML'
environment_label: staging
service_port: 8081
maintenance_mode: false
YAML
同じソーステンプレートを適用します。
ansible-playbook templates.yml
web1 のページと設定が変更され、その再読み込みハンドラーに通知するはずです。web2 は変更されないはずです。web1 をもう一度調べます。
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
本文は environment=staging を示し、X-Environment: STAGING と X-Maintenance: disabled があるはずです。入力が変わっても、テンプレートは再利用可能なままでした。
次に、候補の拒否をテストします。cp は、復元できるように有効なコントローラーのソースを保存します。nginx テンプレートだけを、意図的に無効なテキストで置き換えます。
cp templates/nginx.conf.j2 templates/nginx.conf.j2.good
printf 'invalid nginx configuration;\n' > templates/nginx.conf.j2
次の Playbook 実行は、候補検証で失敗することが想定されています。シェルの || は終了コードがゼロ以外の場合だけメッセージを実行し、この意図的な失敗の練習を継続できるようにします。
ansible-playbook templates.yml || printf 'Expected candidate validation rejection\n'
タスクの失敗内容を読んでください。nginx は一時的な候補を拒否するはずです。管理対象の動作中の設定は残る必要があるため、稼働中の両サービスは引き続き正しく応答するはずです。
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
ansible web2 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8082/'
mv は、保存した有効なソースを元のファイル名に戻します。完全な Playbook をもう一度実行します。
mv templates/nginx.conf.j2.good templates/nginx.conf.j2
ansible-playbook templates.yml
両ホストが changed=0、failed=0、unreachable=0 を報告し、再読み込みハンドラーは実行予定に入らないはずです。検証は、無効な候補のコピーを望ましい最終状態にすることなく、稼働中の設定を保護しました。
まとめ
template モジュールでホスト変数とファクトを反映し、組み込みの upper フィルターとブール型の Jinja 分岐を使って、ハンドラーを通じて検証済み設定を適用しました。入力の変更は、ソースを書き換えずに異なる稼働出力を生成しました。無効な候補は置き換え前に拒否され、復元した有効なテンプレートは、追加の変更なしに収束しました。


