【連載 第1回】脱・初心者!Ansibleを変数(Variables)とFactsで使いこなす〜ハードコーディングからの卒業〜

「そのIPアドレス、直接書いていませんか?」それが苦労の始まりです。

こんにちは!「LINUX工房」管理人の「リナックス先生」です。
過去記事「Ansible応用講座 第1回」などの学習を通して、Ansibleの基本的な操作やクラウド連携の便利さにはもう触れましたか?
ansible-playbook コマンドを叩いてサーバーが自動で構築される様子は、何度見ても気持ちが良いものですよね。

さて、入門を卒業したあなたが次に直面する壁。それは「修正の手間」です。

  • 「テスト環境と本番環境で、インストールするバージョンを変えたい」
  • 「サーバーごとに設定ファイルのIPアドレスだけ書き換えたい」
  • 「OSがCentOSかUbuntuかによって、使うコマンド(yum/apt)を自動で切り替えたい」

もし、これらを解決するために「Playbookをコピーして別ファイルを作る」なんてことをしていたら、それはAnsibleの力を10%も引き出せていません。
今回からの新連載では、「一度書けばどこでも動く(Write Once, Run Anywhere)」スマートな自動化を実現するためのテクニックを学びます。

コウ君

先生!待ってました!
実は現場で「PlaybookのIPアドレスを書き換えるのを忘れて、開発環境の設定を本番に反映しちゃった」という事故を起こしかけまして…。
ファイルの中身を書き換えなくても、環境を切り替えられるようになりたいです!

リナックス先生

コウ君、それはヒヤリハットね!でも良い経験よ。
「値(パラメータ)」と「処理(ロジック)」を分離することこそが、Infrastructure as Code (IaC) の真髄なの。
今回はその第一歩として、「変数(Variables)」と、Ansibleが自動で集める情報「Facts」をマスターしましょう。
これが分かれば、Playbookの行数は半分以下に減らせるわよ!

本連載では、実務で即戦力となるAnsibleの中級テクニックを、全8回にわたって徹底解説します。

🚀 【続・Ansible講座】本連載のカリキュラム

  • 【第1回】変数(Variables)とFactsの活用(今回):環境差異を吸収する
  • 【第2回】ループと条件分岐 (Loop & When):複雑なロジックを組む
  • 【第3回】ハンドラーと通知 (Handlers):無駄な再起動を防ぐ
  • 【第4回】テンプレート (Jinja2):設定ファイルを動的に生成する
  • 【第5回】ロール (Roles) の設計:Playbookを部品化して再利用する
  • 【第6回】Ansible Vault:パスワードなどの機密情報を暗号化する
  • 【第7回】エラーハンドリングとデバッグ:止まらない自動化のために
  • 【第8回】実践プロジェクト:LAMP環境の完全自動構築と総まとめ

1. なぜ「変数」が必要なのか? ハードコーディングの罪

プログラミングにおいて、具体的な値(IPアドレス、パスワード、ファイルパスなど)をソースコードの中に直接書いてしまうことを「ハードコーディング」と呼びます。
Ansibleにおいて、ハードコーディングは以下のような問題を引き起こします。

ハードコーディングのデメリット

  1. 再利用性の低下:
    「WebサーバーA」用に書いたPlaybookを「WebサーバーB」に使おうとしたとき、ファイル内のIPアドレスやホスト名をすべて書き直す必要があります。
  2. 修正漏れのリスク:
    「Apacheのバージョンを 2.4.6 から 2.4.57 に上げたい」と思ったとき、Playbook内に散らばる “2.4.6” という記述を10箇所修正するのは大変です。1箇所でも漏れればエラーになります。
  3. セキュリティリスク:
    パスワードなどの機密情報をPlaybookに直接書くと、Gitなどで共有した際に漏洩する危険があります。

これらの問題を解決するのが「変数(Variables)」です。
「値」を変数として外に出し、Playbookの中では {{ variable_name }} のように参照する形にします。


2. 変数の定義と使い方(基本編)

Ansibleでは、変数の定義する方法がいくつかありますが、まずは最も基本的なPlaybook内での定義方法を見てみましょう。

2-1. varsセクションでの定義

Playbookの vars: セクションで変数を定義し、タスク内で {{ }} で囲んで使用します。

💡 文法チェック:Jinja2テンプレート
Ansibleでは変数の展開にPython製のテンプレートエンジン「Jinja2」を使用します。
{{ my_variable }} のように二重中括弧で囲むのがルールです。
注意:YAMLの仕様上、{{ }} で始まる値は辞書(マップ)と誤認されやすいため、必ず全体をクォート "{{ var }}" で囲む必要があります。

実践:Webサーバー構築(変数版)

以下のPlaybookを作成してみましょう。
インストールするパッケージ名と、ドキュメントルートを変数化しています。

ファイル名:vars_test.yml

---
- name: 変数のテスト
  hosts: all
  become: true
  
  # ここで変数を定義します
  vars:
    web_package: httpd
    doc_root: /var/www/html
    page_content: "Hello, Ansible Variables!"

  tasks:
    - name: Webサーバーをインストール
      ansible.builtin.dnf:
        name: "{{ web_package }}"
        state: present

    - name: Webサーバーを起動
      ansible.builtin.service:
        name: "{{ web_package }}"
        state: started
        enabled: true

    - name: トップページを作成
      ansible.builtin.copy:
        content: "{{ page_content }}\n"
        dest: "{{ doc_root }}/index.html"
        mode: '0644'

このPlaybookの優れた点は、「Ubuntu用に使いたいなら web_package: apache2 に書き換えるだけ」で済む点です。
タスクの中身(ロジック)を触る必要はありません。

2-2. 実行時に変数を渡す (Extra Vars)

変数は、Playbookの中に書くだけでなく、コマンド実行時に外から注入することもできます。
-e オプションを使います。

ansible-playbook vars_test.yml -e "page_content='Overwritten by Command!'"

こうすると、Playbook内の定義よりもコマンドライン引数が優先され、Webページの中身が書き換わります。
CI/CDパイプラインなどで、ビルド番号などを動的に渡す際によく使われます。


3. 【重要】変数をどこに書くべきか?(ベストプラクティス)

vars: セクションに変数を書く」のは簡単ですが、サーバーの台数が増えるとPlaybookが肥大化して管理しきれなくなります。
実務では、変数を「インベントリ(Inventory)」に関連付けて管理するのが一般的です。

3-1. host_vars と group_vars

Ansibleには、「特定のホスト」や「特定のグループ」専用の変数を格納するディレクトリ構造の決まりがあります。

  • group_vars/ : サーバーグループごとの変数を置くディレクトリ
    • 例:group_vars/webservers.yml (Webサーバーグループ共通の設定)
    • 例:group_vars/dbservers.yml (DBサーバーグループ共通の設定)
  • host_vars/ : 特定の1台だけの変数を置くディレクトリ
    • 例:host_vars/192.168.1.10.yml (そのサーバー固有のIPやIDなど)

ディレクトリ構成の例

.
├── inventory.ini
├── site.yml (Playbook)
├── group_vars/
│   ├── all.yml        # 全サーバー共通 (NTP設定など)
│   └── webservers.yml # Webサーバー用の変数 (Apache設定など)
└── host_vars/
    └── db-master.yml  # DBマスター機固有の設定

このようにファイルを分割することで、Playbook本体(site.yml)はすっきりとロジックだけになり、環境ごとのパラメータは vars ディレクトリで管理できるようになります。
これが「IaCにおける整理整頓」の第一歩です。


4. Ansible Facts(自動収集変数)の活用

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

4-1. Factsとは?

AnsibleがPlaybookを実行する際、最初に Gathering Facts というタスクが自動で走っているのに気づきましたか?
ここでAnsibleは対象サーバーに接続し、OSの種類、IPアドレス、メモリ容量、ディスク空き容量などの情報を収集し、変数として格納しています。

4-2. どんな情報が取れるのか確認する

アドホックコマンドを使って、実際に取れる情報を見てみましょう。

ansible localhost -m setup

膨大なJSONデータが返ってきます。
よく使うFacts変数は以下の通りです。

変数名 格納されている値
ansible_distribution OSの種類(CentOS, Ubuntu, Rockyなど)
ansible_distribution_major_version OSのメジャーバージョン(8, 9, 22など)
ansible_default_ipv4.address そのサーバーのIPアドレス
ansible_processor_vcpus CPUのコア数
ansible_memtotal_mb メモリ総量(MB)

4-3. Factsを使った条件分岐(導入編)

Factsを使えば、「OSによって処理を変える」といったことが可能になります。
(詳しくは第2回の「条件分岐」で解説しますが、ここでは雰囲気だけ掴んでください)

    - name: RedHat系の場合の処理
      debug:
        msg: "これはRHEL/CentOS/AlmaLinuxです。yum/dnfを使います。"
      when: ansible_distribution == "AlmaLinux"

このように、Factsは「サーバーの状態に応じた動的な構成管理」を行うための強力な武器になります。


5. 実行結果を変数に入れる「register」

「コマンドを実行して、その結果によって次の処理を変えたい」という場面は多々あります。
例えば、「ファイルが存在するか確認し、存在しなければ作成する」といったケースです。
この時に使うのが register です。

registerの使用例

Linuxの date コマンドを実行し、その結果を変数 current_time に保存して表示するPlaybookです。

---
- name: registerのテスト
  hosts: localhost
  connection: local
  
  tasks:
    - name: 現在時刻を取得
      ansible.builtin.command: date "+%Y-%m-%d %H:%M:%S"
      register: current_time_result  # ここで結果を変数に入れる

    - name: 結果の中身を確認
      ansible.builtin.debug:
        var: current_time_result

    - name: 標準出力だけを表示
      ansible.builtin.debug:
        msg: "現在は {{ current_time_result.stdout }} です。"

実行すると、current_time_result という変数には、コマンドの終了コード(rc)、標準出力(stdout)、標準エラー出力(stderr)などがオブジェクトとして格納されていることが分かります。
欲しい情報(今回は stdout)を取り出して使うのがポイントです。

⚠️ プロの注意点:commandモジュールの濫用
commandshell モジュールで何でもやろうとするのは「Ansibleアンチパターン」です。
register は便利ですが、「既存のモジュール(file, serviceなど)で実現できないか?」を常に考える癖をつけましょう。モジュールを使ったほうが、冪等性(何度実行しても同じ結果になる性質)が担保されやすいためです。


6. 【プロのノウハウ】変数の優先順位と命名規則

最後に、実務でトラブルになりやすい「変数の優先順位」と「名前の付け方」について解説します。

6-1. 変数の優先順位(Precedence)

Ansibleでは、同じ名前の変数を複数の場所(group_vars, Playbook内, コマンド引数など)で定義できます。
もし重複した場合、どちらが勝つのでしょうか?

ざっくりとした「強さ」の順序は以下の通りです(下が強い)。

  1. group_vars/all.yml (一番弱い:デフォルト値として使う)
  2. group_vars/webservers.yml (グループ固有)
  3. host_vars/server1.yml (ホスト固有)
  4. Playbook内の vars:
  5. コマンドライン引数 -e (最強:一時的な上書き用)

基本方針として、「広い範囲(all)でデフォルト値を定義し、狭い範囲(host_vars)で必要に応じて上書きする」という設計にすると、管理がしやすくなります。

6-2. 命名規則(Naming Convention)

変数が何千個にもなると、「これ何の変数だっけ?」と混乱します。
プロの現場では、変数の役割がひと目で分かるようにプレフィックス(接頭辞)をつけることが多いです。

  • apache_port (アプリ名_役割)
  • mysql_root_password
  • common_ntp_server

また、すべてスネークケース(小文字とアンダースコア)で書くのがAnsibleの標準的な作法です。
ApachePortapachePort ではなく、apache_port と書きましょう。


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

お疲れ様でした!
第1回では、脱・初心者のための第一歩として「変数」と「Facts」について学びました。

今回の要点まとめ:

  • ハードコーディングは敵。 値は変数として外に出す。
  • 変数は {{ variable }} で呼び出し、全体をクォートする。
  • group_varshost_vars を使って、環境ごとの設定を整理する。
  • Ansible Facts を使えば、サーバーの情報を自動で取得できる。
  • register を使えば、コマンドの結果に応じた処理ができる。
コウ君

なるほど!
「値」を外に出すだけで、Playbookがこんなにスッキリして使い回せるようになるんですね。
これなら本番環境と開発環境の切り替えも怖くないです!

次回は、プログラミング的な要素である「ループ(繰り返し)」「条件分岐(if文)」について学びます。
これを使えば、「ユーザーを100人一気に作成する」とか「RedHat系とDebian系で処理を分ける」といった、さらに高度な自動化が可能になります。

次回、【第2回】ループと条件分岐 (Loop & When) でお会いしましょう!

▼ Ansibleの実験場を作ろう ▼

複数台構成の実験に最適
「おすすめVPS」

VPSランキングを見る

自動化スキルを活かす
「ITエンジニア転職」

転職エージェントを見る

コメント