【無料ツール】画像をPDFに変換するPhotoPDF Appを公開しました!

【Ansible講座 第4回】変数を使いこなせ。Jinja2テンプレートによる設定ファイルの動的生成完全ガイド

「開発環境」と「本番環境」、Playbookを2つ作っていませんか?

こんにちは!「LINUX工房」管理人の「リナックス先生」です。
前回は、基本的なPlaybookの書き方と、主要モジュールを使ったWebサーバー構築を行いました。

しかし、前回のPlaybookには大きな欠点がありました。
設定ファイル(index.html)の内容が「固定」だったことです。
実際の現場では、こんな要望が必ず出てきます。
「開発環境はログレベルをDEBUGにしたいけど、本番環境はWARNにしたい」
「WebサーバーAには “Server-A”、WebサーバーBには “Server-B” と表示させたい」

コウ君

先生、まさにそれで困ってました!
開発用と本番用で httpd.conf の設定を変えなきゃいけなくて、httpd_dev.confhttpd_prod.conf を用意して、Playbookも2つ作って切り替えてます。
でもこれ、修正するとき両方直さなきゃいけないから、絶対いつかミスりますよね…。

リナックス先生

そのやり方は「アンチパターン」の代表格ね。
Ansibleには「変数(Variables)」「テンプレート(Template)」という強力な武器があるの。
これを使えば、たった1つのPlaybookとテンプレートファイルで、環境に合わせた設定ファイルを自動生成できるわ。
今回は、Ansibleを「魔法の杖」に変える、動的生成テクニックをマスターしましょう!

本記事では、AlmaLinux 9 をベースに、変数の定義方法、システム情報の自動取得(Facts)、そしてPythonベースの強力なテンプレートエンジン「Jinja2」を使った設定ファイルの書き換え術について徹底解説します。

🚀 Ansible完全攻略講座(バックナンバー)

現在地:【第4回】変数を使いこなせ。Jinja2テンプレートによる設定ファイルの動的生成


第1章:変数(Variables)の基礎知識

プログラミングと同様に、Ansibleでも「値」に「名前」をつけて管理することができます。
変数を使うことで、Playbookの中から具体的な値(IPアドレスやポート番号、ユーザー名など)を追い出し、再利用性を高めることができます。

変数の書き方と参照方法

変数は主にYAML形式で定義し、Playbook内では {{ variable_name }} という形式(二重波括弧)で参照します。

定義例(vars):

vars:
  http_port: 80
  server_name: "my-web-server"

参照例(tasks):

tasks:
  - name: Print server name
    ansible.builtin.debug:
      msg: "This server is {{ server_name }} running on port {{ http_port }}"

変数を定義する3つの場所

Ansibleでは変数を定義できる場所が20箇所以上ありますが、初心者が覚えるべきは以下の3つです。

  1. インベントリ変数 (Host/Group Vars):
    第2回で紹介した group_vars/web.ymlhost_vars/web01.yml です。
    「Webサーバーグループはポート80」「DBサーバーグループはポート3306」といった、ホスト固有の値を定義するのに最適です。
  2. Playbook変数 (Playbook Vars):
    Playbookファイルの vars: セクションに直接書きます。
    そのPlaybookだけで使う一時的な定数などに使います。
  3. コマンドライン変数 (Extra Vars):
    実行時に -e オプションで渡します。
    ansible-playbook site.yml -e "http_port=8080"
    最も優先順位が高く、強制的に値を上書きしたい場合に使います。

変数の優先順位(Precedence)

同じ名前の変数が複数の場所で定義されていた場合、以下の順で優先されます(下に行くほど強い)。

  1. インベントリ変数(group_vars < host_vars)
  2. Playbook変数(vars)
  3. コマンドライン変数(-e) ← 最強

第2章:システム情報を自動取得する「Facts変数」

自分で定義する変数の他に、Ansibleが自動的に収集してくれる変数があります。
それが「Facts(ファクト)」です。

Gathering Facts とは?

Playbookを実行した時、最初に TASK [Gathering Facts] という表示が出ますよね?
あそこでAnsibleはターゲットサーバーに潜り込み、OSの種類、IPアドレス、CPUコア数、メモリ容量などの情報を収集し、変数に格納しています。

主要なFacts変数

ansible_ で始まる変数は、Facts変数である可能性が高いです。

変数名 格納されている値の例
ansible_distribution OS名 (例: “AlmaLinux”, “Ubuntu”)
ansible_distribution_major_version メジャーバージョン (例: “9”, “22”)
ansible_default_ipv4.address IPアドレス (例: “192.168.1.50”)
ansible_hostname ホスト名 (例: “web01”)
ansible_processor_vcpus CPUコア数 (例: 4)
inventory_hostname インベントリ上の名前 (マジック変数)

Factsの中身を確認する方法

setup モジュールを使うと、そのサーバーの全Factsを表示できます。

ansible web01 -m setup

大量のJSONが表示されますが、これら全てを変数として利用できるのです。
「OSがRedHat系ならdnf、Debian系ならaptを使う」といった条件分岐(when)などで大活躍します。


第3章:Jinja2(ジンジャーツー)テンプレートの魔術

変数がわかったところで、いよいよ本丸「Jinja2」の登場です。
Jinja2はPython製のテンプレートエンジンで、テキストファイルの中に変数を埋め込んだり、if文などのロジックを書いたりすることができます。

なぜ copy モジュールではダメなのか?

前回使った copy モジュールは、手元のファイルを「そのまま」サーバーに置くだけでした。
しかし、設定ファイル(httpd.confnginx.conf)の中身を、サーバーごとに変えたい場合(IPアドレスを埋め込むなど)は、copy では対応できません。

そこで登場するのが template モジュールです。
拡張子 .j2 のテンプレートファイルを処理し、変数を展開した上で、完成形のファイルをサーバーに配置します。

Jinja2の基本構文

主に使うのは以下の3つだけです。

1. 変数の埋め込み

ServerName {{ ansible_hostname }}
Listen {{ http_port }}

二重波括弧 {{ }} で囲むと、その部分が変数の値に置き換わります。

2. 条件分岐 (if)

{% if env == 'production' %}
  LogLevel WARN
{% else %}
  LogLevel DEBUG
{% endif %}

{% %} で囲むと、制御構文が書けます。
変数 env が ‘production’ なら WARN、それ以外なら DEBUG になります。

3. ループ (for)

{% for ip in allowed_ips %}
  Allow from {{ ip }}
{% endfor %}

リスト(配列)の中身を繰り返し出力できます。


第4章:【実践】動的なWebページを作ってみよう

それでは、実際に手を動かしてみましょう。
「各サーバーのホスト名とIPアドレスを表示する index.html」を自動生成して配布します。

1. テンプレートファイルの作成

作業ディレクトリに templates ディレクトリを作り、その中に index.html.j2 を作成します。

mkdir templates
nano templates/index.html.j2

index.html.j2 の内容:

<html>
<head><title>Server Info</title></head>
<body>
  <h1>Welcome to {{ ansible_hostname }}</h1>
  <p>This server's IP address is: {{ ansible_default_ipv4.address }}</p>
  <p>Environment: {{ env_name }}</p>
</body>
</html>

{{ env_name }} は、こちらで定義する変数です。

2. 変数の定義

Playbook内で変数を定義します。

nano web_setup.yml

web_setup.yml の内容:

---
- name: Setup Dynamic Web Page
  hosts: webservers
  become: true
  vars:
    env_name: "Development"  # ここで変数を定義

  tasks:
    - name: Install Apache
      ansible.builtin.dnf:
        name: httpd
        state: present

    - name: Deploy index.html from template
      ansible.builtin.template:    # copyではなくtemplateを使う!
        src: templates/index.html.j2
        dest: /var/www/html/index.html
        mode: '0644'

    - name: Start Apache
      ansible.builtin.systemd:
        name: httpd
        state: started
        enabled: true

3. 実行と確認

ansible-playbook web_setup.yml

実行後、ブラウザや curl で各サーバーにアクセスしてみてください。

  • web01 にアクセス → “Welcome to web01” … “192.168.1.50”
  • web02 にアクセス → “Welcome to web02” … “192.168.1.51”

同じPlaybook、同じテンプレートを使ったのに、サーバーごとに異なる内容のHTMLファイルが生成されました。
これがテンプレートの威力です。


第5章:【応用】設定ファイルの条件分岐(if文)

次はもう少し実用的な、httpd.conf の設定切り替えに挑戦しましょう。

1. シナリオ

  • 変数 http_port で待受ポートを変更できるようにする。
  • 変数 is_productiontrue ならログレベルを warn に、false なら debug にする。

2. テンプレートの作成

既存の httpd.conf を元にするのが安全ですが、ここでは簡易的な例を作ります。

nano templates/httpd.conf.j2

httpd.conf.j2 の内容(抜粋):

# Dynamic Port Configuration
Listen {{ http_port }}

# Log Level Configuration
{% if is_production %}
LogLevel warn
{% else %}
LogLevel debug
{% endif %}

DocumentRoot "/var/www/html"

3. Playbookの修正

---
- name: Setup Apache Config
  hosts: webservers
  become: true
  vars:
    http_port: 8080
    is_production: false

  tasks:
    - name: Deploy httpd.conf
      ansible.builtin.template:
        src: templates/httpd.conf.j2
        dest: /etc/httpd/conf/httpd.conf
        validate: 'httpd -t -f %s'   # ← ここがプロの技!
      notify: Restart Apache

  handlers:
    - name: Restart Apache
      ansible.builtin.systemd:
        name: httpd
        state: restarted

🔥 プロの技:validate オプション
設定ファイルを書き換えて再起動した瞬間、「構文エラーでApacheが起動しない!」という事故は絶対に避けなければなりません。
validate オプションを使うと、Ansibleは「ファイルを配置する前に、構文チェックコマンドを実行」してくれます。
%s の部分に一時ファイルのパスが入ります。
もしチェックコマンド(httpd -t ...)が失敗したら、ファイルの配置はキャンセルされ、元の設定ファイルが守られます。これは必須テクニックです。


第6章:変数をファイルに切り出す

Playbookの中に vars: を書くと、ファイルが長くなってしまいます。
第2回で紹介したディレクトリ構成に従い、変数を外出ししましょう。

ディレクトリ構成

.
├── inventory.yml
├── group_vars/
│   ├── all.yml          # 全サーバー共通 (http_port: 80)
│   └── development.yml  # 開発環境用 (http_port: 8080)
├── site.yml
└── templates/
    └── httpd.conf.j2

group_vars/all.yml:

http_port: 80
is_production: true

inventory.yml:

all:
  children:
    development:
      hosts:
        web-dev.example.com:
      vars:
        http_port: 8080
        is_production: false
    production:
      hosts:
        web-prod.example.com:

このように構成すれば、web-dev にはポート8080とデバッグログ設定が、web-prod にはポート80と警告ログ設定が、1つのPlaybookを実行するだけで自動的に適用されます。


第7章:Jinja2の便利なフィルター機能

最後に、少し便利なJinja2の「フィルター」を紹介します。
パイプ | を使って値を加工できます。

1. default フィルター

変数が定義されていない場合の「デフォルト値」を設定します。

Listen {{ http_port | default(80) }}

これなら、http_port を定義し忘れてもエラーにならず、80番が使われます。

2. upper / lower フィルター

大文字・小文字に変換します。

Environment: {{ env_name | upper }}

出力: Environment: DEVELOPMENT

3. to_nice_json

変数の内容(リストや辞書)をきれいなJSON形式で出力します。デバッグや設定ファイルの生成に便利です。


まとめ:Playbookに「知性」を与えよう

お疲れ様でした!
変数とテンプレートを使いこなすことで、Ansibleは単なる「手順書の代わり」から、「環境に応じて振る舞いを変えるインテリジェントなツール」へと進化しました。

今回の重要ポイント:

  • 変数は {{ var }} で参照。-e オプションが最強。
  • Facts変数は、サーバーの情報を自動取得してくれる強い味方。
  • 設定ファイルの配布には copy ではなく template モジュールを使う。
  • Jinja2を使えば、if文やfor文で設定ファイルを動的に生成できる。
  • validate オプションで、再起動前の構文チェックを忘れずに。

さて、Playbookが便利になるにつれて、記述量が増えてファイルが長くなってきましたね。
「Webサーバーの設定」と「DBサーバーの設定」を1つのファイルに書くと、見通しが悪くなります。
もっと機能ごとにファイルを分割して、部品化できないでしょうか?

次回、第5回は「コードを整理整頓。Rolesディレクトリ構成と再利用性の最大化」です。
Ansibleにおける「整理整頓の最終形態」である Role(ロール) について学びます。
Roleを使えば、他人が作ったPlaybookを取り込んだり、自分のコードを部品化して使い回したりできるようになります。
本格的な運用の入り口です。お楽しみに!

▼ 変数とテンプレートを実験する ▼

動的構築を試す
「VPS」で自分専用環境

おすすめVPSを見る

Ansibleスキルを年収に
「ITエンジニア転職」

転職エージェントを見る

コメント