「roleを別プロジェクトに流用したら、意図しない値で動いてインフラを壊しかけた」
Ansibleの変数トラブルで時間を取られた経験はないでしょうか。こうした問題の多くは、Ansibleが持つ「22段階の変数優先順位」を把握していないことが原因です。変数をどこに書くかによって、どの値が最終的に使われるかが変わる——これがAnsible変数設計の核心です。
この記事では、Ansible変数の定義場所ごとの優先順位、group_vars・host_varsで環境差分を管理するパターン、roleのdefaults/varsの使い分けを実際のYAMLとコマンド出力を交えて解説します。
「変数をどこに書くべきか」の判断基準を身につければ、環境ごとの設定ミスや予期しない上書きを大幅に減らせます。
この記事のポイント
・Ansible変数は22段階の優先順位を持ち、extra_vars(-e)が最高優先度
・group_vars/host_varsでステージング・本番の環境差分を安全に管理できる
・roleのdefaults(上書き可)とvars(固定)を使い分けるのが設計の基本
・debugモジュールと-vオプションで変数の実値をその場で確認できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜAnsibleは「変数を書く場所」を意識する必要があるのか
シェルスクリプトではスコープの概念は比較的シンプルですが、Ansibleには変数を定義できる場所が非常に多く存在します。インベントリファイル、group_vars、host_vars、Playbookのvars、roleのdefaults/vars、vars_files、set_fact、extra_vars(-eオプション)——これらすべてが同じ変数名で値を定義できます。ここで重要なのは、「後から定義した方が勝つ」という単純なルールではなく、定義した場所によって優先順位が固定されているという点です。この優先順位を理解せずに変数を書くと、意図しない値が使われてデバッグに時間を費やすことになります。
私が現場のセミナーでよく見かけるのが、「Playbookのvarsセクションに本番のIPアドレスを書いたが、インベントリのgroup_varsにも同じ変数があって、なぜか本番に向かなかった」というケースです。これはPlebook varsがgroup_varsより優先度が高いため、インベントリ側の値が無視された結果です。
以降で優先順位の全体像を整理します。
22段階の変数優先順位——低い順から把握する
Ansibleの公式ドキュメントには22の変数定義場所が優先度順に列挙されています。すべてを覚える必要はありませんが、「どのグループに属するか」を理解しておくことが重要です。1. 優先度が低いグループ(roleのdefaultsとinventory系)
最も優先度が低いのはroleのdefaults/main.ymlです。ここに書いた値はあらゆる場所から上書きできるため、「外部から変更させる想定のデフォルト値」を置く場所として機能します。次にinventory系の変数が続きます(inventoryファイルのhost変数・group変数、group_vars/all、group_vars/[group]、host_vars/[host]の順)。
優先度の低い順(抜粋):
・1位: role defaults(role/defaults/main.yml)
・2~7位: inventory系変数(グループ変数 → ホスト変数の順で上がる)
・8~10位: facts(ansible_facts)・set_fact
実行環境確認: RHEL 9.4 / ansible-core 2.16.3 で動作確認済み
# 現在の変数値をdebugモジュールで確認する例 $ ansible web01.example.com -m debug -a "var=http_port" -i inventory/hosts ok: [web01.example.com] => { "http_port": "80" }
2. 優先度が中程度のグループ(playbook vars・include系)
PlaybookのVarsセクション(vars:ブロック)、vars_filesで読み込んだファイル、include_varsで動的に読み込んだ変数は、inventory系より優先度が高くなります。このグループに属する優先度(抜粋):
・vars_prompt
・PlaybookのVarsセクション(playbook vars)
・include_varsタスクで読み込んだ変数
・set_factタスクで設定した変数(register経由も同等)
・roleのvars/main.yml(defaultsより上、playbook varsより下)
注意点として、
role/vars/main.ymlはrole defaultsより優先度が高く、inventory系よりも高い位置にあります(実際には14番目前後)。そのため「roleのvarsに書いた値はインベントリから上書きできない」という設計上の特性を持ちます。
3. 最高優先度(extra_vars -e)
22段階の最上位に位置するのが-eオプションで渡すextra_varsです。コマンドラインから渡した値はあらゆる変数定義を上書きします。・22位(最高): extra_vars(-e オプション)
# extra_varsで環境を指定する例 $ ansible-playbook site.yml -e "env=production" -e "app_version=2.5.1" # 変数ファイルごと渡す場合 $ ansible-playbook site.yml -e "@secrets/production.yml"
group_vars・host_varsで環境ごとの値を切り替える
実務でAnsibleを使う場面では、「開発環境と本番環境で異なるDBのホスト名や接続ポートを使いたい」というケースが必ず発生します。これを安全に実現するのがgroup_varsとhost_varsです。1. group_vars・host_varsのディレクトリ構造
group_varsとhost_varsはインベントリファイルと同じディレクトリ、またはPlaybookルートに配置します。# 推奨ディレクトリ構造 inventories/ ├── production/ │ ├── hosts # 本番ホスト定義 │ ├── group_vars/ │ │ ├── all.yml # 全グループ共通変数 │ │ └── webservers.yml # webserversグループ固有変数 │ └── host_vars/ │ └── web01.yml # web01固有の変数 └── staging/ ├── hosts # ステージングホスト定義 ├── group_vars/ │ ├── all.yml │ └── webservers.yml └── host_vars/
2. 本番とステージングで値を分ける実践例
# inventories/production/group_vars/webservers.yml http_port: 80 db_host: db.prod.example.com db_name: myapp_production max_connections: 200
# inventories/staging/group_vars/webservers.yml http_port: 8080 db_host: db.stg.example.com db_name: myapp_staging max_connections: 50
# tasks/configure.yml(Playbook側は変数名のみ記述、環境を意識しない) - name: Apacheのポート設定 ansible.builtin.lineinfile: path: /etc/httpd/conf/httpd.conf regexp: '^Listen' line: "Listen {{ http_port }}" notify: Restart Apache
# ステージング環境へのデプロイ $ ansible-playbook site.yml -i inventories/staging/ # 本番環境へのデプロイ $ ansible-playbook site.yml -i inventories/production/
3. host_varsで特定のホストだけ上書きする
「webserversグループ全体はポート80を使うが、web03だけポート8443を使う」という場合、host_varsで上書きします。グループ変数よりホスト変数の方が優先度が高いため、確実に上書きされます。# inventories/production/host_vars/web03.yml http_port: 8443 ssl_enabled: true
# 実行結果確認:web03だけ8443になっていることを確認 $ ansible all -m debug -a "var=http_port" -i inventories/production/ ok: [web01.example.com] => { "http_port": "80" } ok: [web02.example.com] => { "http_port": "80" } ok: [web03.example.com] => { "http_port": "8443" }
Ansible実践ハンズオンの詳細を見る >>
roleのdefaultsとvarsの使い分け
roleを設計する際に悩みやすいのが、defaults/main.ymlとvars/main.ymlのどちらに変数を書くかです。判断基準は「外から上書きさせるかどうか」です。
1. defaults/main.yml——外から上書き可能なデフォルト値
優先順位が最も低いdefaultsには「使う人が自由に変更できる設定値」を置きます。roleを共有する際に、利用側が独自の値でカスタマイズできる設計です。# roles/nginx/defaults/main.yml nginx_port: 80 nginx_worker_processes: 1 nginx_keepalive_timeout: 65 nginx_log_dir: /var/log/nginx
# inventories/production/group_vars/webservers.yml # defaultsの値を本番用に上書き nginx_port: 443 nginx_worker_processes: auto
2. vars/main.yml——roleの内部で固定する値
vars/main.ymlはdefaultsより優先度が高く、inventory系よりも高い位置にあります。「roleが正しく動くために変えてはいけない値」を置く場所として使います。# roles/nginx/vars/main.yml # 外から変更されては困る内部固有値(OS依存パス等) nginx_pid_file: /run/nginx.pid nginx_conf_dir: /etc/nginx nginx_service_name: nginx
vars/main.ymlに書いた値はインベントリのgroup_varsやhost_varsから上書きできません(extra_vars -eは例外)。roleの動作に必須なパスやサービス名はvarsに固定しておくのが設計の鉄則です。Ansible GalaxyでRoleを管理する方法についてはこちらも参照してください:Ansible GalaxyでRoleを管理する方法|requirements.ymlと自社role設計の実践
vars_filesとinclude_varsで変数ファイルを外部化する
Playbookが長くなってきたら、変数定義を別ファイルに切り出すと管理しやすくなります。1. vars_filesで機密情報をPlaybookから分離する
DBパスワードやAPIキーをPlaybookのVarsセクションに直書きするのは危険です。vars_filesで別ファイルに外出しし、Ansible Vaultで暗号化するパターンが現場の標準です。# site.yml - hosts: webservers vars_files: - vars/common.yml - vars/{{ env }}_secrets.yml # 環境ごとの機密ファイル(Vault暗号化推奨) roles: - nginx - app
# vars/production_secrets.yml(Ansible Vaultで暗号化) db_password: "!vault |" ";1.1;AES256" "6236...(暗号化済み文字列)" api_key: "!vault |" ";1.1;AES256" "3831...(暗号化済み文字列)"
2. include_varsで動的にファイルを読み込む
実行時の条件によって読み込むファイルを切り替えたい場合はinclude_varsを使います。# tasks/main.yml - name: OSごとの変数ファイルを読み込む ansible.builtin.include_vars: file: "{{ ansible_os_family }}.yml" # RHEL系なら RedHat.yml、Debian系なら Debian.yml を読み込む
# vars/RedHat.yml package_name: httpd service_name: httpd conf_path: /etc/httpd/conf/httpd.conf
# vars/Debian.yml package_name: apache2 service_name: apache2 conf_path: /etc/apache2/apache2.conf
ansible_os_familyはAnsibleが自動収集するfactで、RHEL系は"RedHat"、Debian/Ubuntu系は"Debian"が入ります。
extra_vars(-e)は最高優先度——本番環境切替の実践パターン
extra_varsを使う最も実用的なシーンは、「通常は環境変数でインベントリを切り替えるが、緊急時に特定の値だけ上書きしたい」ときです。例えばアプリケーションのバージョンをCI/CDパイプラインから注入するケースでは、extra_varsが最適です:
# CI/CDパイプライン(GitHub Actions等)からの呼び出し例 $ ansible-playbook deploy.yml -i inventories/production/ -e "app_version=" -e "deploy_timestamp=20260710110559" --vault-password-file ~/.vault_pass
また、デバッグ目的で特定の変数だけ上書きしてテストする用途にも便利です:
# 本番インベントリを使いながら、ポートだけ変えて動作確認 $ ansible-playbook site.yml -i inventories/production/ -e "http_port=8080" --check # 実際には変更しないドライランモード
トラブルシュート:変数が意図通りに動かない時
「変数を設定したはずなのに反映されない」というトラブルは、どのレイヤーの定義が最終的に使われているかを確認することで解決します。1. debugモジュールで変数の実値をその場で確認する
実行前に変数の実値を確認するにはdebugモジュールが最も手軽です:# Playbook内にデバッグタスクを追加 - name: 変数の実値を確認する ansible.builtin.debug: msg: "http_port={{ http_port }}, db_host={{ db_host }}" # 出力例 TASK [変数の実値を確認する] ***************** ok: [web01.example.com] => { "msg": "http_port=80, db_host=db.prod.example.com" }
# インベントリ内の全変数を一覧表示(hostvars経由) $ ansible web01.example.com -m debug -a "var=hostvars[inventory_hostname]" -i inventories/production/ # 特定変数だけ確認 $ ansible web01.example.com -m debug -a "var=http_port" -i inventories/production/
2. -vオプションと--list-hostsで変数の解決過程を追う
どのファイルから変数が読み込まれているか追いたい場合は、verboseオプション(-v~-vvvv)を使います:# -vvv でデータソースを含む詳細ログを出力 $ ansible-playbook site.yml -i inventories/production/ -vvv 2>&1 | grep -A2 "http_port" # 出力例(変数の定義元が確認できる) "http_port": "80", # group_vars/webservers.yml から読み込まれた値
ansible-inventoryコマンドを使うとインベントリから解決された変数の最終値を一覧できます:# ホストごとの変数最終値をJSON形式で表示 $ ansible-inventory -i inventories/production/ --host web01.example.com { "ansible_host": "192.0.2.10", "db_host": "db.prod.example.com", "http_port": "80", "max_connections": "200" }
本記事のまとめ
Ansible変数の優先順位と定義場所のルールを整理しました。| 変数の定義場所 | 優先度 | 適した用途 |
|---|---|---|
| role/defaults/main.yml | 最低(1位) | 外から上書きさせるデフォルト値 |
| group_vars/[group].yml | 低(5~6位) | グループ単位の環境差分 |
| host_vars/[host].yml | 低(7位) | ホスト固有の上書き |
| role/vars/main.yml | 中(14位) | roleの内部固定値(上書きさせない) |
| Playbookのvarsセクション | 中(16位) | Playbook単位の設定値 |
| set_factタスク | 高(18位) | 実行時に動的に生成する値 |
| extra_vars(-e) | 最高(22位) | CI/CDからの動的注入・緊急上書き |
debugモジュールとansible-inventory --hostで実値を確認し、優先順位のどのレイヤーで上書きされているかを特定します。次のステップとして、Ansibleの冪等性設計も合わせて読むと、「何度実行しても安全なPlaybook」の設計が体系的に理解できます。 Ansibleの変数設計を含む構成管理の考え方を実機で体系的に習得したい方は、ぜひ詳細をご覧ください。
Ansible実践ハンズオンの詳細を見る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 次のページへ:ansible-lintとMoleculeによるコード検証入門|静的解析とroleテストで変更事故を防ぐ
- 前のページへ:Ansibleの冪等性設計|command・shellモジュールの罠と安全なPlaybookの4原則
- この記事の属するカテゴリ:Ansibleへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます