設定テンプレートを生成して検証する

AnsibleBeginner
オンラインで実践に進む

はじめに

テンプレートによる自動化では、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 分岐を使って、ハンドラーを通じて検証済み設定を適用しました。入力の変更は、ソースを書き換えずに異なる稼働出力を生成しました。無効な候補は置き換え前に拒否され、復元した有効なテンプレートは、追加の変更なしに収束しました。