Ansibleのfactsとgather_facts制御|setupモジュールでホスト情報を収集して条件分岐に使う方法

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Ansible > Ansibleのfactsとgather_facts制御|setupモジュールでホスト情報を収集して条件分岐に使う方法
「本番に5台のRHELと3台のUbuntuが混在している。同じPlaybookで両方に対応できるのか?」
Ansibleを使い始めて間もないエンジニアが必ずぶつかる壁だ。

この問題を解決するのが facts(ファクト) だ。AnsibleはPlaybookのタスクを実行する前に、対象ホストのOS情報・CPU・メモリ・ネットワーク設定を自動で収集する。収集した情報はPlaybook内で変数として参照できるため、OSの種類に応じて処理を切り替えたり、メモリ容量によってパラメータを変えたりといった「環境適応型のPlaybook」を実現できる。

この記事では、Ansible factsの仕組みsetupモジュールが収集する情報の種類gather_factsの制御方法(無効化・部分収集)when条件分岐へのfacts活用パターンカスタムfactsの定義方法を、実際の実行結果とともに解説する。

動作確認環境: RHEL 9.4 / Ubuntu 24.04 LTS、Ansible 2.16.x

この記事のポイント

・gather_facts: true(デフォルト)でsetupモジュールが自動的にホスト情報を収集する
・ansible_distributionでOS種別を判定してwhen条件分岐に使える
・gather_facts: falseに設定すると大規模環境でPlaybookを高速化できる
・/etc/ansible/facts.d/にINIファイルを置くとカスタムfactsを定義できる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
図解60P/登録10秒/解除も3秒 / 詳細はこちら

Ansible factsとは何か|Playbookが走る前に何が起きているか

ansible-playbookコマンドを実行したとき、タスクの前に必ず表示される行がある。

PLAY [webservers] ************************************************************** TASK [Gathering Facts] ********************************************************* ok: [webserver01] ok: [webserver02] ok: [db01]

この「Gathering Facts」こそが、factsを収集する処理だ。Ansibleが対象ホストに接続し、setupモジュールを自動実行して各種情報を取得している。

収集されたfactsはansible_factsというディクショナリに格納され、ansible_distributionansible_memtotal_mbといった変数名でPlaybook内から参照できる。Playbookに明示的な設定を書かなくても、デフォルトで収集が有効になっているのがポイントだ。

factsの仕組みを理解することで、「このホストはRHELなのかUbuntuなのか」「RAMは8GB以上あるか」をPlaybook実行時に動的に判断できるようになる。複数のOS・スペックのホストを同一Playbookで管理するための基盤がfactsだ。

setupモジュールが収集する情報の種類

1. OS・ディストリビューション情報

Playbookの条件分岐で最もよく使われる情報群だ。OSファミリーで大まかに分岐し、バージョンで細かく制御するパターンが現場の定番だ。

ansible_distribution:ディストリビューション名(例: "RedHat", "Ubuntu", "CentOS", "Rocky")
ansible_distribution_version:バージョン番号(例: "9.4", "24.04")
ansible_distribution_major_version:メジャーバージョンのみ(例: "9", "24")
ansible_os_family:OSファミリー("RedHat" または "Debian")
ansible_kernel:Linuxカーネルバージョン(例: "5.14.0-427.13.1.el9_4.x86_64")

2. CPU・メモリ情報

JVM ヒープサイズやDBのバッファプールサイズなど、リソース依存のパラメータ設定に使う情報群だ。

ansible_processor_count:CPUソケット数
ansible_processor_vcpus:仮想CPU(vCPU)数
ansible_memtotal_mb:総メモリ量(MB単位)
ansible_memfree_mb:空きメモリ量(MB単位)

3. ネットワーク情報

IPアドレスやホスト名の確認・条件分岐に使う情報群だ。

ansible_hostname:ホスト名(短縮形)
ansible_fqdn:完全修飾ドメイン名(FQDN)
ansible_default_ipv4.address:デフォルトルート側のIPv4アドレス
ansible_interfaces:ネットワークインターフェース名のリスト

4. setupモジュールを直接実行してfactsを確認する

Playbookを書く前に対象ホストのfactsを調べるには、アドホックコマンドでsetupモジュールを直接実行するのが手っ取り早い。

# 対象ホストの全factsを表示する ansible webserver01 -m setup # filterオプションで絞り込む(ディストリビューション情報のみ) ansible webserver01 -m setup -a "filter=ansible_distribution*"

RHEL 9.4のホストで実行すると次のような出力が得られる。

webserver01 | SUCCESS => { "ansible_facts": { "ansible_distribution": "RedHat", "ansible_distribution_major_version": "9", "ansible_distribution_release": "Plow", "ansible_distribution_version": "9.4" }, "changed": false }

Ubuntu 24.04のホストではansible_distributionが"Ubuntu"になり、ansible_distribution_versionが"24.04"に変わる。条件分岐のwhen句で使う変数の実値をここで確認しておくと、Playbook作成時のミスを防げる。

メモリ情報を確認したいときは次のようにfilterを変える。

# メモリ情報だけ確認する ansible db01 -m setup -a "filter=ansible_memory_mb"

db01 | SUCCESS => { "ansible_facts": { "ansible_memory_mb": { "nocache": { "free": 13412, "used": 2427 }, "real": { "free": 6893, "total": 15839, "used": 8946 }, "swap": { "cached": 0, "free": 2047, "total": 2047, "used": 0 } } }, "changed": false }

この出力からansible_memory_mb.real.totalが15839MB(約16GB)であることがわかる。このような実測値を把握しておくことで、メモリベースの条件分岐をPlaybook内で自信を持って設計できる。

Ansibleのfacts収集を実機でハンズオン体験したい方は、>> Ansibleハンズオン講座の詳細を見る をご覧ください。

gather_factsの制御|収集を無効化して高速化する

1. gather_facts: falseで収集を無効化する理由

factsの収集はPlaybookの冒頭で対象ホスト全台に対して実行される。ホスト数が多い環境では、この処理が実行時間のボトルネックになることがある。

100台のホストに対してPlaybookを実行した場合、gather_factsだけで30秒以上かかることも珍しくない(並列接続数がforks設定に縛られるためだ)。単純なファイル配布やコマンド実行のみでOS情報が不要なPlaybookでは、gather_facts: falseを指定して収集をスキップできる。

--- - name: ファイルを配布する(facts不要なため収集をスキップ) hosts: all gather_facts: false tasks: - name: 設定ファイルを転送する copy: src: files/app.conf dest: /etc/app/app.conf mode: '0644'

gather_facts: falseを設定すると、Playbookの出力から「TASK [Gathering Facts]」のステップが消え、最初のタスクから即座に実行が始まる。ただしansible_distribution等のfacts変数はすべてundefinedになるため、when条件でfacts変数を参照しているタスクがあると実行時エラーになる。この点は必ず確認すること。

2. gather_subsetで必要な情報だけ収集する

factsを完全に無効化せず、必要な情報だけを収集したい場合はgather_subsetを使う。全情報を収集するよりも処理時間を短縮しながら、必要な変数は使えるようにするバランス型の設定だ。

--- - name: ネットワーク情報だけ収集する hosts: all gather_facts: true gather_subset: - network - '!hardware' - '!virtual' tasks: - name: IPアドレスを表示する debug: msg: "{{ ansible_default_ipv4.address }}"

gather_subsetで指定できる主なサブセットは次の通りだ。

min:最小限の情報(OS・ホスト名・IPアドレス)
network:ネットワークインターフェース・IPアドレス情報
hardware:CPU・メモリ・ディスク情報
virtual:仮想化環境の情報(VMware・KVM・Docker判定など)
!hardware:先頭に「!」を付けると除外指定になる

まずallで一度全収集して必要な変数を把握し、本番運用時にgather_subsetで絞り込む、という手順が実用的だ。

収集したfactsをwhen条件分岐で使う設計パターン

1. OS別の処理分岐(RHELとUbuntuを同一Playbookで管理)

最も使用頻度の高いパターンだ。ansible_os_familyでRedHat系とDebian系を振り分ける。

--- - name: Webサーバーをインストールする(OS混在環境対応) hosts: webservers become: true tasks: - name: Apache をインストールする(RHEL系) dnf: name: httpd state: present when: ansible_os_family == "RedHat" - name: Apache をインストールする(Debian系) apt: name: apache2 state: present update_cache: true when: ansible_os_family == "Debian" - name: Apache を起動する(RHEL系:サービス名が httpd) service: name: httpd state: started enabled: true when: ansible_os_family == "RedHat" - name: Apache を起動する(Debian系:サービス名が apache2) service: name: apache2 state: started enabled: true when: ansible_os_family == "Debian"

RHEL 9.4のホストにこのPlaybookを実行すると、dnfタスクはok/changedになり、aptタスクはskippingになる。Ubuntu 24.04のホストでは逆になる。

TASK [Apache をインストールする(RHEL系)] ********************************** changed: [webserver01] TASK [Apache をインストールする(Debian系)] ********************************** skipping: [webserver01] TASK [Apache をインストールする(RHEL系)] ********************************** skipping: [ubuntu01] TASK [Apache をインストールする(Debian系)] ********************************** changed: [ubuntu01]

2. メモリ容量に基づく分岐

DBのバッファプールサイズやJVMヒープ設定など、メモリ量によってパラメータを変えたい場面に使う。

--- - name: MariaDB の innodb_buffer_pool_size を設定する hosts: dbservers become: true tasks: - name: バッファプールを16GB以上のサーバーは8GBに設定する lineinfile: path: /etc/my.cnf.d/server.cnf regexp: '^innodb_buffer_pool_size' line: 'innodb_buffer_pool_size = 8G' when: ansible_memtotal_mb >= 16384 - name: バッファプールを8GB未満のサーバーは1GBに設定する lineinfile: path: /etc/my.cnf.d/server.cnf regexp: '^innodb_buffer_pool_size' line: 'innodb_buffer_pool_size = 1G' when: ansible_memtotal_mb < 8192

ansible_memtotal_mbはMB単位の整数として格納されるため、>= 16384は「16GB以上」を意味する。数値の比較にはクォートが不要だ(文字列比較との混同に注意)。

3. ホスト名・IPアドレスを条件にした制御

命名規則でホストの役割を識別している環境で、特定のホストグループにのみ処理を適用したいときに使う。

--- - name: DBホストにのみ監視エージェントの設定を追加する hosts: all tasks: - name: DB用の監視設定を配置する(ホスト名に "db" が含まれるホストのみ) copy: src: files/db-monitor.conf dest: /etc/monitor/db-monitor.conf when: "'db' in ansible_hostname" - name: 特定サブネットのホストにのみファイアウォール設定を適用する firewalld: zone: internal permanent: true state: enabled when: ansible_default_ipv4.address | ansible.utils.ipaddr('192.168.10.0/24')

文字列の含有チェックには'db' in ansible_hostnameのようにPythonのin演算子を使える。IPアドレスのサブネット判定にはansible.utilsコレクションのipaddressフィルターが便利だ。

カスタムfacts|独自情報を/etc/ansible/facts.d/に定義する

setupモジュールが収集する標準factsに加えて、独自のfactsをホスト側に定義することもできる。これが「カスタムfacts」だ。アプリのバージョン情報や環境区分(開発/ステージング/本番)など、Ansibleが自動収集できない情報を管理するのに便利だ。

1. /etc/ansible/facts.d/にINIファイルを作成する

対象ホストの/etc/ansible/facts.d/ディレクトリに.fact拡張子のファイルを置く。INI形式のファイルが最も手軽だ。

# /etc/ansible/facts.d/app.fact(INI形式) # このファイルを対象ホストに事前に配置する [app] version = 2.5.1 environment = production deploy_user = appuser

このファイルをAnsibleで配置するには次のように書く。

--- - name: カスタムfactsファイルを配置する hosts: appservers become: true tasks: - name: facts.d ディレクトリを作成する file: path: /etc/ansible/facts.d state: directory mode: '0755' - name: カスタムfactsファイルをコピーする copy: src: files/app.fact dest: /etc/ansible/facts.d/app.fact mode: '0644' - name: カスタムfactsを再収集する setup: filter: ansible_local

2. カスタムfactsをPlaybookで使う

カスタムfactsはansible_local変数の配下に格納される。INIファイル名(拡張子なし)がキー名になる。

--- - name: カスタムfactsを使ってアプリの設定を制御する hosts: appservers tasks: - name: アプリのバージョンを表示する debug: msg: "アプリバージョン: {{ ansible_local.app.app.version }}" - name: 本番環境のみロギングを有効化する template: src: templates/logging.conf.j2 dest: /etc/app/logging.conf when: ansible_local.app.app.environment == "production"

参照パスはansible_local.ファイル名.セクション名.キー名の形式になる。上の例ではansible_local.app.app.versionとなる(ファイル名"app"、セクション名"app"、キー名"version")。

facts活用時のトラブルと注意点|よくある落とし穴の解決策

gather_facts: false時に変数参照するとエラーになる:収集を無効化したPlaybookでansible_distribution等を参照するとundefined変数エラーが発生する。「when: ansible_os_family == 'RedHat'」のような条件があると、そのタスクはエラーで止まる(skippingにはならない)。gather_factsをfalseにするPlaybookはfacts変数を参照していないことを必ず確認すること

fact gatheringが失敗するケース:SSH接続に問題があるとsetupモジュールの実行自体が失敗する。「FAILED! => Timeout (12s) waiting for privilege escalation prompt」や「Failed to connect to the host via ssh」が出た場合は接続設定・sudoの設定を先に確認する

変数優先順位との関係:factsはgroup_vars・host_varsより低い優先順位を持つ。同名の変数がhost_varsに定義されている場合、factsの値はその変数で上書きされる。意図しない上書きが起きていないか、変数の優先順位を把握した設計が必要だ

カスタムfactsのパスの注意:/etc/ansible/facts.d/は対象ホスト(管理対象サーバー)側に置くディレクトリだ。Ansibleを実行するコントロールノード(自分のPC)側に置いても読み込まれないので注意すること

本記事のまとめ

やりたいこと 方法・変数名
対象ホストの全factsを確認する ansible ホスト名 -m setup
ディストリビューション情報のみ確認する ansible ホスト名 -m setup -a "filter=ansible_distribution*"
OSファミリーで処理を分岐する when: ansible_os_family == "RedHat"
メモリ容量で処理を分岐する when: ansible_memtotal_mb >= 16384
ホスト名で処理を絞り込む when: "'db' in ansible_hostname"
facts収集を完全に無効化する Playbook冒頭に gather_facts: false
必要な情報だけ収集する gather_subset: ['network', '!hardware']
独自情報をfactsに追加する /etc/ansible/facts.d/*.fact にINIファイルを配置
カスタムfactsを参照する ansible_local.ファイル名.セクション名.キー名
Ansibleのfactsを活用すると、1本のPlaybookで複数のOS・スペックのホストに対応できる。まずansible ホスト名 -m setupで対象ホストのfactsを実際に確認し、使いたい変数の実値を把握してからPlaybookを書く習慣をつけると、when条件のミスや変数名の勘違いを防げる。

カスタムfactsまで活用できるようになると、Ansibleが収集できないアプリケーション固有の情報も含めた設計が可能になる。構成管理の自由度が大きく広がるステップだ。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Ansible実践ハンズオンの詳細を見る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

登録10秒/合わなければ解除3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。