Ansibleのgatherfactsとはなんぞや

Page content

1.目的

Ansibleを使い始めて「gather_factsとはなんぞや」と思ったので、理解を深めるために色々試してみます。

ざっくり調べた限りでは、gather_factsとは、対象ノードからいろんな情報を収集して変数(ファクト変数)に保存するよ、その変数は設定とかに流用できるよ。というもののようです。

デフォルトで有効となっており、あえて無効にするメリットとしてはPlaybook実行時間の短縮くらいだと思いますので、大量のノードを短時間で処理したいなどの性能的な要件がなければ、基本は有効にして良いと思います。

2.参考

参考にした公式Docは以下の通りです。

3.前提

Ansibleからノードに対し、SSH接続できている状態を前提とします。

4.手順

a)ansbileコマンドでfact変数取ってみる

先ほど、「gather_fact≒対象ノードからいろんな情報(変数)を取得すること」と書きましたが、実はansibleコマンドのオプションからfact変数を取得できるので試してみます。

コマンドは以下の通りです。

$ ansible -i <インベントリファイル> <グループ/ホスト名> -m setup

それでは実際に実行した結果を確認してみましょう。ここではインベントリファイルは./ansible/hosts、対象グループ名としてwebserversを指定しています。

$ ansible -i ./ansible/hosts webservers -m setup
al199 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.1.199"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::20c:29ff:fe27:880e"
        ],
        "ansible_apparmor": {
            "status": "disabled"
        },
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "11/12/2020",
        "ansible_bios_vendor": "Phoenix Technologies LTD",
        "ansible_bios_version": "6.00",
        "ansible_board_asset_tag": "NA",
        "ansible_board_name": "440BX Desktop Reference Platform",
        "ansible_board_serial": "NA",
        "ansible_board_vendor": "Intel Corporation",
        "ansible_board_version": "None",
        "ansible_chassis_asset_tag": "No Asset Tag",
        "ansible_chassis_serial": "NA",
        "ansible_chassis_vendor": "No Enclosure",
        "ansible_chassis_version": "N/A",
        "ansible_cmdline": {
            "BOOT_IMAGE": "/boot/vmlinuz-4.14.314-237.533.amzn2.x86_64",
            "KEYTABLE": "us",
            "LANG": "ja_JP.utf8",
            "biosdevname": "0",
            "console": "tty0",
            "net.ifnames": "0",
            "nvme_core.io_timeout": "4294967295",
            "ro": true,
            "root": "UUID=7e408eb8-6208-481e-87ea-42d040d5977b"
        },
        "ansible_date_time": {
            "date": "2023-05-25",
            "day": "25",
            "epoch": "1684979238",
            "epoch_int": "1684979238",
            "hour": "10",
            "iso8601": "2023-05-25T01:47:18Z",
            "iso8601_basic": "20230525T104718942767",
            "iso8601_basic_short": "20230525T104718",
            "iso8601_micro": "2023-05-25T01:47:18.942767Z",
            "minute": "47",
            "month": "05",
            "second": "18",
            "time": "10:47:18",
            "tz": "JST",
            "tz_dst": "JST",
            "tz_offset": "+0900",
            "weekday": "木曜日",
            "weekday_number": "4",
            "weeknumber": "21",
            "year": "2023"
        },
        "ansible_default_ipv4": {
            "address": "192.168.1.199",
            "alias": "eth0",
            "broadcast": "192.168.1.255",
            "gateway": "192.168.1.1",
            "interface": "eth0",
            "macaddress": "00:0c:29:27:88:0e",
            "mtu": 1500,
            "netmask": "255.255.255.0",
            "network": "192.168.1.0",
            "prefix": "24",
            "type": "ether"
        },
### 以下省略 ###

想像していたよりたくさんの情報が取得できました。H/Wの情報なども一部ありますね、この情報をCSVなどに整形すればサーバ一覧なども作れそうです。

解説すると、コマンドオプション-m setupによってAnsibleのsetupモジュールを有効化しています。(-m は Modulesのmですね)

setupモジュールは、Ansibleに標準で搭載されているモジュールでfact変数を取得する機能になります。

コマンド実行時にsetupモジュールを有効にして実行することで、fact変数を取得しているわけです。

b)PlaybookでIPアドレスを取得

次は具体的にIPアドレスを取得し、標準出力(デバッグメッセージ)にIPアドレス情報を表示するPlayBookを作成します。

以下が実行するymlファイル(PlayBook)です。

---
- hosts: webservers # インベントリファイル内のグループ名を指定
gather_facts: yes # fact変数の取得を有効化
become: no # root権限で実行しない
tasks:
- name: check ip address # タスク名の指定
    debug: 
    # 取得したfact変数からinventory_hostnameとansible_default_ipv4.addressを表示
    msg: System {{ inventory_hostname }} has ip address {{ ansible_default_ipv4.address }} 

それでは実行してみます。

$ ansible-playbook ansible/test_get_ipaddr.yml

PLAY [webservers] ********************************************************************

TASK [Gathering Facts] ********************************************************************
ok: [al199]
ok: [al197]

TASK [check ip address] ********************************************************************
ok: [al197] => {
    "msg": "System al197 has ip address 192.168.1.197"
}
ok: [al199] => {
    "msg": "System al199 has ip address 192.168.1.199"
}

PLAY RECAP ********************************************************************
al197                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
al199                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

TASK [check ip address] で、ホスト名とIPアドレスが取得&表示できています。

"msg": "System al197 has ip address 192.168.1.197"
"msg": "System al199 has ip address 192.168.1.199"

ここまでで、任意の変数をデバッグ表示することに成功しました。

c)ファクト変数をトリガに何かする

さらに、fact変数の値をチェックして特定の値の場合のみ条件分岐で別な処理を行うPlayBookを作ります。

ここではアーキテクチャが「x86_64」のときにkernel情報を出力するPlaybookを作ってみました。

---
- hosts: webservers # インベントリファイル内のグループ名を指定
gather_facts: yes # fact変数の取得を有効化
become: no # root権限で実行しない
tasks:
- name: check archtecture and kernel # タスク名の指定
    debug:
    # inventory_hostnameとansible_kernelを出力
    msg: System {{ inventory_hostname }} has {{ ansible_kernel }}
    # 条件設定:ansible_architectureが"x86_64"と等しいとき
    when: ansible_architecture == "x86_64"

実行します。

$ ansible-playbook ansible/test_get_arch.yml

PLAY [webservers] **********************************************************

TASK [Gathering Facts] ****************************************************************************
ok: [al199]
ok: [al197]

TASK [check archtecture and kernel] ****************************************************************************
ok: [al197] => {
    "msg": "System al197 has 4.14.311-233.529.amzn2.x86_64"
}
ok: [al199] => {
    "msg": "System al199 has 4.14.314-237.533.amzn2.x86_64"
}

PLAY RECAP ****************************************************************************
al197                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
al199                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Kernelの情報を表示することができました。ただ、今回の場合だと条件分岐が正常に動いているかがわかりません。念のため、PlayBookのwhenをx86_64ではなくhogeにして実行してみるとどうでしょうか。これに該当するノードは無いので、何も処理されなくなるはずです。

# 変更箇所のみ記載
when: ansible_architecture == "hoge"

このPlayBookを実行してみると、TASKがSkiipingになりました。問題なく処理されているようです。

TASK [check archtecture and kernel] **********************************************************
skipping: [al197]
skipping: [al199]

5.まとめ

gather_factsの本質は情報収集なので、例えば「定期的にサーバの情報を一括収集して、パッチの適用漏れや意図しない変更がないこと」を確認するあたりが最も適切な使い方なのかもしれません。

あとは、セキュリティ設定のサーベイ的な使い方として、DMZに配置されたサーバをIPアドレスで判別して、firewallの設定などをチェックするあたりでしょうか。

個人的には、fact変数を元にゴリゴリに条件分岐をするとPlayBookの可読性が下がりそうなので、そもそも普段のオペレーションとして分類が可能なのであればPlayBookそのものを分けて管理した方がよいのかな?と考えています。