背景
最近、UbuntuとCentOSが混在する約75台のノードを管理している際に、少し厄介な問題に直面しました。デプロイ時間を短縮するため(ファクトの収集には1ホストあたり3〜5秒かかることがあります)、プレイブックの冒頭で明示的に gather_facts: no を設定していました。大規模なインベントリでは、この設定ひとつで、SSHハンドシェイクやシステム情報のスキャンにかかる時間を数分短縮できます。
プレイブックは順調に動いていましたが、OSの種類を判別する必要があるタスクに差し掛かったところで止まってしまいました。UbuntuとCentOSで異なる設定を適用するために when 条件式を使用していたのですが、その瞬間に真っ赤なエラーメッセージとともに実行が停止しました。
fatal: [server-01]: FAILED! => {
"msg": "The conditional check failed. The error was: 'ansible_distribution' is undefined"
}
デバッグのプロセス
このエラーは非常に文字通りな内容です。Ansibleは ansible_distribution という名前の変数の記録がないと伝えています。Ansibleの世界では、ansible_ で始まる変数は通常「ファクト(facts)」と呼ばれます。これらは、プレイの開始時に setup モジュールが自動的に収集するIPアドレスやOSバージョンなどのメタデータです。
プレイブックのヘッダーをさっと確認したところ、原因(動かぬ証拠)が見つかりました。
---
- name: Configure Web Servers
hosts: web_servers
gather_facts: no # <-- これが原因です
tasks:
- name: Install Apache on Ubuntu
apt:
name: apache2
state: present
when: ansible_distribution == 'Ubuntu'
gather_facts: no を設定すると、リモートホストに対する内部調査をスキップするようにAnsibleに指示したことになります。その結果、ローカルのファクト辞書は空のままになります。apt タスクがホストのディストリビューションを評価しようとした際、値が null であるため、プレイ全体が失敗してしまいます。
解決策
私は通常、環境の規模やパフォーマンスをどこまで重視するかによって、以下の3つの解決策のいずれかを採用します。
1. クイックフィックス:グローバルなファクト収集を再有効化する
数台のサーバーを管理しているだけなら、スイッチを yes に戻すだけです。Ansibleはデフォルトでファクトを収集するため、この行を削除するだけでも構いません。多少のオーバーヘッドは増えますが、すべてのシステム変数が使用可能な状態になります。
- name: Configure Web Servers
hosts: web_servers
gather_facts: yes
# ... タスクが続く
2. ピンポイントなアプローチ:Setupモジュールを使用する
速度が優先される場合は、グローバルではファクトを無効にしたまま、条件付きタスクの直前で必要なものだけを取得できます。プレイブックを軽量かつ明示的に保てるため、これが私の推奨する方法です。
- name: Configure Web Servers
hosts: web_servers
gather_facts: no
tasks:
- name: ディストリビューションのファクトのみを取得
setup:
filter: ansible_distribution
- name: OS固有のタスク
debug:
msg: "{{ ansible_distribution }} で実行中"
when: ansible_distribution is defined
filter パラメータを使用することで、他の何百ものハードウェアやネットワークのファクトを無視するようAnsibleに指示できるため、この特定の呼び出しは非常に高速になります。
3. ファクトキャッシュの利用
何百ものノードがある本番環境では、ファクトキャッシュの設定を強くお勧めします。RedisやローカルのJSONファイルを使用することで、プレイブックでは gather_facts: no を維持しつつ、前回の実行時のキャッシュからデータを取り出すことができます。ansible.cfg を以下のように更新します。
[defaults]
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400 # ファクトは24時間有効
検証手順
プレイブック全体を実行せずに修正が正しく機能したか確認するには、ターミナルから以下のアドホックコマンドを試してください。
ansible <your_host> -m setup -a "filter=ansible_distribution"
成功すると、次のようなきれいなJSONブロックが返ってくるはずです。
server-01 | SUCCESS => {
"ansible_facts": {
"ansible_distribution": "Ubuntu"
},
"changed": false
}
学んだ教訓
- トレードオフに注意: ファクトの無効化は強力な最適化ですが、プレイブックのロジックに「死角」を生みます。無効にする前に、必ず
whenステートメントを監査してください。 - 優雅に失敗させる: プロダクションレベルのコードを書くときは、
when: ansible_distribution is defined and ansible_distribution == 'Ubuntu'を使用しましょう。これにより、ファクトが欠落していても実行全体がクラッシュするのを防げます。 - ファクトをフィルタリングする: システムプロファイル全体が必要になることは稀です。特定のフィルタを指定して
setupモジュールを使用すれば、10秒ものパフォーマンス低下を招くことなく必要なデータを取得できます。

