こういった悩みは、Ansibleを実務で使い始めた多くのエンジニアが通る道です。
チュートリアルの段階ではひとつのPlaybookに全部書いても問題ありません。ですが、管理するサーバー台数が増え、環境(dev・stg・prod)が分かれ、チームで共有するようになった瞬間に「なぜこう書いたのか誰も分からない」「本番に間違えた設定が流れた」といった問題が起きます。
この記事では、ansible inventory role module の3軸を軸に、現場で崩れない構成管理の設計方針を解説します。「inventoryのINI形式・YAML形式の使い分け」「ファイルの置き場所の原則」「roleの分割粒度」「moduleの選び方」「ansible-inventoryによる変数の事前検証」まで、実際に手を動かしながら理解できる内容にしています。動作確認環境はRHEL 9.4 / Rocky Linux 9.4、ansible-core 2.15系です。
この記事のポイント
・inventory は INI形式(手軽)とYAML形式(階層記述)から用途に合わせて選択する
・ansible inventory は dev/stg/prod のディレクトリに分割し group_vars で環境差分を自動適用する
・ansible-inventory --list / --graph で変数とグループ構造を実行前に必ず検証する
・role は機能単位(nginx/mariadb)で分割し依存性を meta/main.yml で管理する
・module は「冪等性が保証されるか」を基準に選択。command/shell は最終手段
・vars_prompt で本番実行に確認ステップを追加し、--check --diff でドライラン必須
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜinventory・role・moduleの設計が構成管理の核心なのか
Ansibleで構成管理を始めると、最初は一枚のPlaybookで十分に感じます。しかし現場では早い段階でこんな問題が起きます。・本番と開発でIPアドレスが違うのにPlaybookをコピーして運用している
・Nginxの設定とMariaDBの設定が同一ファイルに混在していてどこを直せばいいか分からない
・
shellモジュールで冪等性のない処理を書いてしまい、何度実行しても同じ結果になるか確信が持てない・hostsファイルのコメントアウトミスで本番に開発用の設定が流れた
これらの問題はツールの問題ではなく、設計の問題です。さらにもう1つ、「Playbookを実行する前に変数が正しく解決されているか確認する方法が分からない」という声も実務の現場でよく聞きます。
特によく見かけるのが「環境別にPlaybookをコピーして管理する」パターンです。一見整理されているように見えますが、これは崩壊の入口です。
# よくある「コピー管理」のディレクトリ構造 ansible-project/ ├── playbooks/ │ ├── site-staging.yml # stg用Playbook │ └── site-production.yml # prodはstagingのコピー ├── inventories/ │ ├── staging.ini │ └── production.ini └── roles/ └── webserver/
inventory・role・moduleの3つを正しく設計し、
ansible-inventoryコマンドで事前検証する習慣を身につければ、Ansibleは「何度実行しても同じ状態になる」「環境差分を安全に管理できる」ツールとして機能します。inventoryファイルの形式:INI形式とYAML形式の書き分け
Ansibleの静的inventoryには、INI形式とYAML形式の2種類があります。どちらも機能は同等ですが、可読性と複雑な構成への対応力が異なります。プロジェクト規模とチームの習熟度に合わせて選択してください。1. INI形式の基本構造
最もシンプルな形式です。テキストエディタで直感的に編集でき、Ansibleを使い始めて最初に触れる形式でもあります。# hostsファイル(INI形式の例) [webservers] web01.example.com web02.example.com [dbservers] db01.example.com db02.example.com [all:vars] ansible_user=rocky ansible_ssh_private_key_file=/home/ansible/.ssh/id_ed25519
[グループ名]ヘッダでグループを区切ります。[all:vars]セクションに書いた変数は全ホストに適用されます。ただし後述のgroup_vars/host_varsを使う場合、inventoryファイル本体にvarsを書く必要はほとんどなくなります。複数グループをまとめる親グループは
[親グループ名:children]セクションで表現します。# INI形式でchildrenを使う例(webserversとdbserversをapp_serversにまとめる) [app_servers:children] webservers dbservers
hosts: app_serversとPlaybookで指定すると、webserversとdbserversの全ホストに対して実行されます。2. YAML形式で書く場合の構造
YAML形式はPlaybookと同じフォーマットで書けるため、グループの階層をより明示的に記述できます。3層以上の複雑な階層や、CI/CDで自動生成する場合に特に有効です。# inventory.yml(YAML形式の例) all: children: webservers: hosts: web01.example.com: web02.example.com: dbservers: hosts: db01.example.com: app_servers: # 親グループ(webservers + dbservers をまとめる) children: webservers: dbservers:
hosts:の下にホスト固有の変数を直接書けるため、後述のhost_varsとの併用設計も柔軟に対応できます。3. どちらを選ぶか——実務での判断基準
・INI形式が向くケース:小規模なinventory(20台以下)、チームがAnsible初心者、手軽に始めたい場合・YAML形式が向くケース:複雑なグループ階層(3層以上)、CI/CDで自動生成する場合、Ansible経験者チーム
大規模プロジェクトでは「YAML形式 + group_vars/host_vars分離」が長期保守性の観点から推奨されます。一方、10台以下のシンプルな構成管理ならINI形式で十分です。どちらの形式も共存できるため、移行は段階的に進められます。
inventory設計:環境と個体差を分離する
1. ディレクトリ型inventoryで環境を分ける
ファイル型inventoryの典型的な失敗パターンは、hosts.iniという1ファイルに開発・本番のホストをコメントで切り替えながら書くことです。コメントアウトのミスが本番事故につながります。推奨するのは、環境ごとにディレクトリを分けるディレクトリ型inventoryです。dev(開発)・stg(ステージング)・prod(本番)の3環境を分離するのが現場での標準構成です。
# ディレクトリ構造(推奨) inventories/ ├── dev/ │ ├── hosts # 開発環境のホスト定義 │ └── group_vars/ │ └── all.yml # 開発環境共通変数 ├── stg/ │ ├── hosts # ステージング環境のホスト定義 │ └── group_vars/ │ └── all.yml # ステージング環境共通変数 └── prod/ ├── hosts # 本番環境のホスト定義 └── group_vars/ └── all.yml # 本番環境共通変数
# inventories/dev/hosts の例 [web] web01.dev.example.com web02.dev.example.com [db] db01.dev.example.com # 親グループ dev_all が子グループ web / db を持つ宣言(変数の継承に使う) [dev_all:children] web db [all:vars] ansible_user=ec2-user ansible_ssh_private_key_file=~/.ssh/dev_key.pem
# inventories/prod/hosts の例(本番ホストのみ) [web] prod-web01 ansible_host=10.0.1.11 prod-web02 ansible_host=10.0.1.12 [db] prod-db01 ansible_host=10.0.2.11 [all:vars] ansible_user=ec2-user ansible_ssh_private_key_file=~/.ssh/prod_key.pem
group_vars/を置くことです。-i inventories/devと指定した場合、Ansibleはinventories/dev/group_vars/を自動的に参照します。実行時に-i inventories/env名でinventoryを切り替えることで、誤って本番に実行するリスクを大幅に下げられます。事前にssコマンドでSSH 22番ポートの開放状況を確認してから接続設定を進めると安心です。# 開発環境に対してPlaybookを実行 ansible-playbook -i inventories/dev site.yml # stg環境に対してPlaybookを実行 ansible-playbook -i inventories/stg site.yml # 本番環境への適用前にドライランで差分確認(推奨) ansible-playbook -i inventories/prod site.yml --check --diff # 本番環境に対してPlaybookを実行 ansible-playbook -i inventories/prod site.yml
2. host_varsで個体差を吸収する
同じ役割(webサーバー)でもホストごとにIPアドレスやディスク構成が違うことがあります。これをhost_varsで管理します。# host_vars/web01.dev.example.com.yml の例 # サーバー固有の変数をここで定義する nginx_port: 80 data_volume: /dev/xvdb max_conn: 512
# host_vars/web02.dev.example.com.yml の例 nginx_port: 80 data_volume: /dev/xvdc # 別デバイスでも吸収できる max_conn: 256
host_varsはgroup_varsより優先されるため、グループ共通のデフォルト値をgroup_varsに、個体固有の上書き値をhost_varsに書く設計が安定します。特定ホストだけ例外的な設定を入れたい場合(メンテナンスモードの有効化など)も、host_varsで安全に管理できます。
# inventories/prod/host_vars/prod-web02.yml # prod-web02のみメンテナンスモードを有効にする場合 nginx_maintenance_mode: true maintenance_message: "定期メンテナンス中です(10:00~12:00)"
3. group_varsの設計例|dev・prod別の変数定義
inventoryディレクトリを分けると、group_varsのスコープも自動的に分離されます。-i inventories/devで実行した場合はinventories/dev/group_vars/が、-i inventories/prodで実行した場合はinventories/prod/group_vars/が参照されます。Playbookは1本のまま維持できるため、環境間での変更漏れも起きません。# inventories/dev/group_vars/all.yml env: dev app_port: 8080 nginx_worker_processes: 1 log_level: debug allow_debug_endpoints: true db_host: 192.168.10.21 db_name: myapp_dev ansible_user: ansible ntp_server: ntp.nict.jp
# inventories/prod/group_vars/all.yml env: prod app_port: 80 nginx_worker_processes: auto log_level: warn allow_debug_endpoints: false db_host: 10.0.2.11 db_name: myapp_prod ansible_user: ansible ntp_server: ntp.nict.jp
env変数を定義しておくと、Jinja2テンプレートやwhen:条件で環境を判別できます。またansible_userやntp_serverのように全環境共通のデフォルト値もall.ymlに置いておくと、inventory切り替えのたびに同じ設定を書き直す手間が省けます。グループ別の変数ファイルも活用すると、webグループ専用の設定を独立したファイルで管理できます。
# inventories/dev/group_vars/webservers.yml(webグループ専用) http_port: 80 https_port: 443 webserver_package: nginx max_connections: 512
# tasks/main.yml の例 — env変数で環境ごとに条件分岐する - name: デバッグエンドポイントを有効化する(dev環境のみ) ansible.builtin.template: src: debug_routes.conf.j2 dest: /etc/nginx/conf.d/debug.conf when: env == "dev" - name: worker_processesを設定する ansible.builtin.lineinfile: path: /etc/nginx/nginx.conf regexp: '^worker_processes' line: "worker_processes {{ nginx_worker_processes }};"
4. ansible-inventoryで変数解決を事前検証する
inventoryを設計したら、Playbookを実行する前にansible-inventoryコマンドで変数が正しく解決されているかを確認してください。これを習慣にするだけで、「思っていた値と違った」「本番に開発用の値が流れた」という実行時エラーの大半を未然に防げます。# 本番inventoryの全変数をJSON形式で確認する ansible-inventory -i inventories/prod --list # 特定ホストの変数だけを絞り込んで確認する ansible-inventory -i inventories/prod --host prod-web01 # グループ階層をツリー形式で視覚的に確認する ansible-inventory -i inventories/prod --graph
--graphオプションは、グループの親子関係とホストの所属を一目で確認できます。@ungroupedに意図しないホストが入っていないかの確認にも使えます。ungroupedのホストはgroup_varsが適用されないため、設定漏れの原因になります。# ansible-inventory --graph の出力例 $ ansible-inventory -i inventories/prod --graph @all: |--@web: | |--prod-web01 | |--prod-web02 |--@db: | |--prod-db01 |--@ungrouped:
{ "_meta": { "hostvars": { "prod-web01": { "ansible_host": "10.0.1.11", "ansible_user": "ansible", "app_port": 80, "db_host": "10.0.2.11", "env": "prod", "log_level": "warn", "nginx_worker_processes": "auto", "ntp_server": "ntp.nict.jp" } } }, "web": { "hosts": ["prod-web01", "prod-web02"] } }
env: "prod"とlog_level: "warn"が正しく反映されていることを目視確認します。group_vars/all.ymlから来たansible_userやntp_serverと、prod専用のall.ymlから来たenvやapp_portが1つのホストにマージされて表示されているのが分かります。本番実行前に素早く全ホストの環境を一覧確認したい場合は、以下のワンライナーが便利です。
# 全ホストのenv変数を一覧表示して環境を目視確認する ansible-inventory -i inventories/prod --list | python3 -c " import json, sys inv = json.load(sys.stdin) for host, vars in inv['_meta']['hostvars'].items(): print(host, '->', vars.get('env', '未設定')) " # 出力例(全ホストが prod を指していることを確認する) # prod-web01 -> prod # prod-web02 -> prod # prod-db01 -> prod
5. prod専用のシークレット管理(Ansible Vault)
本番環境のDBパスワードなど機密情報は、group_vars内にAnsible Vaultで暗号化したファイルを置きます。inventoryディレクトリが分かれているため、devのシークレットとprodのシークレットが完全に分離されます。# prod専用のVaultファイルを新規作成する ansible-vault create inventories/prod/group_vars/vault.yml # ファイル内に記述する変数(暗号化前の平文イメージ): # db_password: "ProdP@ssw0rd" # app_secret_key: "prod-secret-key-xxxxx" # Vaultパスワードファイルを使ってPlaybookを実行する ansible-playbook -i inventories/prod --vault-password-file ~/.vault_pass.txt site.yml
~/.vault_pass.txt(パスワードファイル)は.gitignoreに必ず含め、リポジトリに含めないよう注意してください。6. 動的inventoryで台数が増えても崩れない
AWSやAzureのようにインスタンスが動的に増減する環境では、静的なhostsファイルは追いつきません。Ansibleには動的inventory用のプラグインが用意されています。# inventories/dev/aws_ec2.yml(動的inventory設定例) plugin: amazon.aws.aws_ec2 regions: - ap-northeast-1 filters: "tag:Env": dev "tag:Role": web keyed_groups: - key: tags.Role prefix: role # 実行時のコマンド ansible-playbook -i inventories/dev/aws_ec2.yml site.yml
# ansible-inventory -i inventories/dev/aws_ec2.yml --list の出力例(抜粋) { "_meta": { "hostvars": { "10.0.1.23": { "ansible_host": "10.0.1.23", "tags": {"Env": "dev", "Role": "web"} } } }, "role_web": { "hosts": ["10.0.1.23", "10.0.1.24"] } }
複数環境を安全に実行する
1. ansible-playbook -i で環境を指定して実行する
環境の切り替えは-iオプションでinventoryディレクトリを指定するだけです。Playbookは共通のsite.ymlを使い回せます。# dev環境へのPlaybook実行 ansible-playbook -i inventories/dev site.yml # stg環境へのPlaybook実行 ansible-playbook -i inventories/stg site.yml # prod環境への適用前にcheckモードで差分確認(必須) ansible-playbook -i inventories/prod --check --diff site.yml # prod環境への本番適用 ansible-playbook -i inventories/prod site.yml
--check --diffでドライランを行い、変更内容を確認してから実行するのが現場の鉄則です。以下は実際のドライラン出力例(prod-web01 への適用前確認)です。$ ansible-playbook -i inventories/prod --check --diff site.yml PLAY [web] ****************************************** TASK [nginx : Deploy nginx.conf from template] ****** --- before: /etc/nginx/nginx.conf +++ after: /home/ansible/.ansible/tmp/ansible-tmp-xxx/source @@ -3,7 +3,7 @@ -worker_processes 1; +worker_processes auto; PLAY RECAP ****************************************** prod-web01 : ok=4 changed=1 unreachable=0 failed=0 prod-web02 : ok=4 changed=1 unreachable=0 failed=0
nginx_worker_processesがprod用のautoに正しく切り替わっていることが確認できます。dev環境で1を設定していた値がprod用に上書きされており、inventoryの分離が正しく機能しています。2. Makefileでラップして誤実行を防ぐ
-iオプションの指定忘れは誤実行の原因になります。Makefileでコマンドをラップすると、環境名をターゲット名として指定するだけで実行できます。# Makefile(タブでインデントすること) .PHONY: dev stg check-prod prod dev: ansible-playbook -i inventories/dev site.yml stg: ansible-playbook -i inventories/stg site.yml check-prod: ansible-playbook -i inventories/prod --check --diff site.yml prod: check-prod @echo "prod本番適用を実行します。本当によいですか? [y/N]" @read ans && [ "651618{ans}" = "y" ] && ansible-playbook -i inventories/prod site.yml || echo "中止しました。"
make dev / make stg / make check-prodとターゲットを指定するだけでよく、長いコマンドをそのまま入力するより誤操作が減ります。prodターゲットはcheck-prodに依存しているため、ドライランなしに本番適用するルートが物理的になくなります。3. vars_promptで本番実行に確認ステップを追加する
Makefileと組み合わせるとさらに安全性が上がります。Playbookにvars_promptを使った確認ステップを仕込み、env変数がprodのときだけ「yes」入力を要求する設計です。# site.yml(本番確認フローの例) --- - name: Production confirmation check hosts: all gather_facts: false vars_prompt: - name: confirm_production prompt: "本番環境への実行です。続行しますか? [yes/no]" private: false tasks: - name: Abort if not confirmed ansible.builtin.fail: msg: "実行を中断しました。" when: - env == 'prod' - confirm_production != 'yes' - name: Configure web servers hosts: web roles: - common - nginx
envはinventories/prod/group_vars/all.ymlでprodに設定した変数です。stg・dev実行時はこのfailタスクがスキップされてそのまま進みます。本番だけ二重の確認が走るため、-iを間違えて本番を指定してしまった場合にも人間が気づける構造になります。role設計:機能単位で分割し依存を管理する
1. roleのディレクトリ構造を理解する
roleはAnsibleが定める規約ディレクトリを持ちます。ansible-galaxy role init nginxで雛形を生成するのが最も確実です。# role雛形の生成 ansible-galaxy role init nginx # 生成されるディレクトリ構造 nginx/ ├── defaults/ │ └── main.yml # デフォルト変数(最低優先度) ├── handlers/ │ └── main.yml # handler(サービス再起動など) ├── tasks/ │ └── main.yml # タスク本体 ├── templates/ │ └── nginx.conf.j2 # Jinja2テンプレート ├── vars/ │ └── main.yml # role内固定変数(高優先度) ├── files/ # 静的ファイル ├── meta/ │ └── main.yml # roleの依存関係定義 └── tests/ └── test.yml
2. roleの分割粒度:「1 role = 1サービス」が基準
roleを分割する粒度で悩む人は多いです。現場で安定している基準は「1 role = 1サービス(またはデーモン)」です。・nginx role:Nginxのインストール・設定・起動
・mariadb role:MariaDBのインストール・初期化・設定
・php-fpm role:PHP-FPMのインストール・設定
・common role:全サーバー共通の設定(SELinux・ファイアウォール・ユーザー作成等)
# site.yml(roleを束ねるエントリポイント) --- - name: Configure web servers hosts: web roles: - common - nginx - php-fpm - name: Configure DB servers hosts: db roles: - common - mariadb
3. defaults/main.ymlで可変値を外に出す
role内にIPアドレスやポート番号をハードコードするのは最も避けるべきパターンです。defaults/main.ymlにデフォルト値を定義し、inventoryのgroup_varsやhost_varsから上書きできる設計にします。# nginx/defaults/main.yml --- nginx_port: 80 nginx_worker_processes: auto nginx_access_log: /var/log/nginx/access.log nginx_error_log: /var/log/nginx/error.log nginx_max_body_size: 10m
# nginx/templates/nginx.conf.j2(Jinja2テンプレートで変数を展開) worker_processes {{ nginx_worker_processes }}; http { access_log {{ nginx_access_log }}; error_log {{ nginx_error_log }}; server { listen {{ nginx_port }}; client_max_body_size {{ nginx_max_body_size }}; } }
4. handlerで「変更があった時だけ再起動」を実現する
handlerはAnsibleの重要な機能です。「設定ファイルを変更したときだけNginxをreloadする」という冪等(べきとう)な動作を実装できます。# nginx/tasks/main.yml --- - name: Install nginx ansible.builtin.dnf: name: nginx state: present - name: Deploy nginx.conf ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf mode: '0644' notify: Reload nginx # 変更があった場合のみhandlerを呼ぶ - name: Ensure nginx is started and enabled ansible.builtin.service: name: nginx state: started enabled: true
# nginx/handlers/main.yml --- - name: Reload nginx ansible.builtin.service: name: nginx state: reloaded
notifyで指定したhandler名は、Playbook実行の最後にまとめて一度だけ実行されます。同じtaskが10回変更を通知しても、再起動は1回だけ行われる仕組みです。5. roleの依存関係はmeta/main.ymlで宣言する
php-fpmroleはnginxroleが先にインストールされている必要があるケースがあります。こういった依存関係はmeta/main.ymlに明示します。# php-fpm/meta/main.yml --- galaxy_info: role_name: php-fpm author: yourname min_ansible_version: "2.15" dependencies: - role: nginx
module設計:冪等性を基準に選ぶ
1. 「冪等性が保証されるmoduleか」を判断軸にする
Ansibleのmoduleを選ぶ基準は明確です。冪等性(何度実行しても同じ結果になる性質)が保証されているかどうかです。| やりたいこと | 推奨module | 冪等性 |
|---|---|---|
| パッケージをインストール | ansible.builtin.dnf / ansible.builtin.apt |
あり(state: present/absent) |
| 設定ファイルを配置 | ansible.builtin.template / ansible.builtin.copy |
あり(変更なしはスキップ) |
| サービスを起動・有効化 | ansible.builtin.service |
あり(状態を確認して実行) |
| ユーザーを作成 | ansible.builtin.user |
あり(存在確認後に作成) |
| ファイルのパーミッション設定 | ansible.builtin.file |
あり(変更なしはスキップ) |
| 設定ファイルの一部を書き換え | ansible.builtin.lineinfile |
あり(マッチした行のみ変更) |
| 任意のシェルコマンドを実行 | ansible.builtin.command / ansible.builtin.shell |
なし(最終手段) |
2. command/shellを使う前に確認すべきこと
commandモジュールやshellモジュールを使いたくなったとき、まず自問してください。「このコマンドで実現したいことに、専用のAnsible moduleはないか?」例えば、
systemctl restart nginxと書きたい場合はansible.builtin.servicemoduleのstate: restartedで代替できます。useradd usernameと書きたい場合はansible.builtin.usermoduleで代替できます。どうしても
command/shellを使う必要がある場合は、createsまたはremovesパラメータで冪等性を自前実装します。# commandで冪等性を確保する例 # creates: に指定したファイルが存在する場合はスキップされる - name: Initialize MariaDB data directory ansible.builtin.command: cmd: mysql_install_db --user=mysql --basedir=/usr creates: /var/lib/mysql/mysql # このディレクトリがあればスキップ
3. FQCNでmodule名を書く(Ansible 2.10以降の推奨形式)
Ansibleのmodule名はコレクション管理(FQCN:Fully Qualified Collection Name)の記法で書くことが推奨されています。・旧来の書き方:
yum / service / copy・推奨する書き方:
ansible.builtin.dnf / ansible.builtin.service / ansible.builtin.copyFQCNで書くと、どのコレクションのmoduleを使っているかが明確になり、コレクション更新時の動作変化を追いやすくなります。チームで共有するPlaybookほどFQCN表記を徹底してください。
トラブルシュート:よくある設計ミスと対処法
「変数が意図しない値で実行された」→ ansible.cfgのデフォルト指定を確認する
症状:stg実行のつもりがprodの設定値(log_level: warnなど)が適用された。・原因:
ansible.cfgのinventoryにprod inventoryをデフォルト指定していた、または-iを省略してデフォルトのinventoryが使われた・対処:
ansible.cfgのinventory行をコメントアウトし、全実行に-i指定を必須化する。実行前にansible-inventory -i inventories/stg/ --listでホスト変数を確認する習慣をつける# どの環境で動くか確認する ansible-inventory -i inventories/stg --list | python3 -c " import json, sys inv = json.load(sys.stdin) for host, vars in inv['_meta']['hostvars'].items(): print(host, '->', vars.get('env', '未設定')) " # 出力が staging を指しているか確認する
「No hosts matched」が出る
Playbookのhosts:に指定したグループがinventoryに定義されていない場合に発生します。ansible-inventory --graphでグループ名の綴りを確認してください。大文字・小文字の違いやtypoが最多の原因です。# グループ階層を確認して「No hosts matched」の原因を特定する $ ansible-inventory -i inventories/dev --graph @all: |--@web: | |--web01.dev.example.com |--@ungrouped: | |--db01.dev.example.com # ← ungroupedに入っているホストを発見
@ungroupedにホストが入っている場合、そのホストはどのグループにも属していません。inventoryのhostsファイルでグループを正しく定義してください。「group_varsが効かない」→配置場所を確認する
inventoryディレクトリを-iで指定した場合、Ansibleはそのディレクトリ内のgroup_vars/を参照します。プロジェクトルート直下のgroup_vars/は参照されません(inventoryファイルを直接指定する場合とは挙動が異なります)。# NG: プロジェクトルートにまとめて置くパターン project/ ├── group_vars/ │ └── all.yml # どの環境で実行しても同じ値が使われる └── inventories/ ├── dev/hosts └── prod/hosts # OK: inventoryディレクトリ配下に置く project/ └── inventories/ ├── dev/ │ └── group_vars/ │ └── all.yml # dev実行時のみ参照される └── prod/ └── group_vars/ └── all.yml # prod実行時のみ参照される
「変数が上書きされない」→優先順位を確認する
Ansibleの変数優先順位は22段階あります。現場でよく起きる混乱は、vars/main.yml(高優先度)とdefaults/main.yml(低優先度)の違いを理解していないことです。・
defaults/main.ymlに書いた値 → inventoryのgroup_varsやhost_varsで上書き可能(推奨)・
vars/main.ymlに書いた値 → inventoryの変数より優先されるため、外から上書きできない外から変えてほしい値は必ず
defaults/main.ymlに書き、roleの内部固定値のみvars/main.ymlに書くのが鉄則です。「変数ファイルが読まれない」→YAMLの構文エラーを確認する
group_vars/host_varsのYAMLファイルにインデントエラーや特殊文字の扱いミスがあると、変数ファイル全体が読み込まれません。ansible-inventory --listを実行してエラーが出るか、期待した変数が表示されない場合はYAML構文を疑ってください。# YAML構文チェック(python3が使える環境で実行する) $ python3 -c "import yaml; yaml.safe_load(open('inventories/prod/group_vars/all.yml'))" # エラーがなければ何も出力されない。エラーがある場合は行番号付きで表示される # 例: yaml.scanner.ScannerError: mapping values are not allowed here # in "inventories/prod/group_vars/all.yml", line 5, column 12
key:valueではなくkey: valueが正しい)や、文字列値をクォートせずに特殊文字(:や#など)を含めていることが原因です。修正後は必ずansible-inventory --listで変数が正しく表示されることを確認してください。「Playbook再実行でエラーが出る」→冪等性の欠如を疑う
再実行でエラーになるタスクは、冪等性がない処理が含まれている可能性があります。# エラーが出たときのデバッグ手順 # --check でドライランして変更予定タスクを確認する ansible-playbook -i inventories/dev site.yml --check --diff # 特定のtagを付けたタスクだけ実行して切り分ける ansible-playbook -i inventories/dev site.yml --tags "nginx" # 実行結果の詳細を表示する(-vvv で最大詳細) ansible-playbook -i inventories/dev site.yml -v
「roleが見つからない」→roles_pathの設定を確認する
roleのディレクトリがroles/以外にある場合、ansible.cfgでroles_pathを明示します。# ansible.cfg の設定例 [defaults] inventory = inventories/dev roles_path = roles:~/.ansible/roles remote_user = ec2-user host_key_checking = False # 検証機では無効化することが多い [privilege_escalation] become = True become_method = sudo
ansible.cfgのinventoryに開発環境を指定しておくと便利ですが、本番実行時に-iを省略した場合の誤実行リスクがあります。コメントアウトして常に-iで明示指定する運用が安全です。本記事のまとめ
inventory・role・module設計の要点をまとめます。| 設計対象 | 原則 | よくある失敗 |
|---|---|---|
| inventoryの形式 | 小規模はINI形式、複雑な階層や自動生成はYAML形式を選択 | 規模に関係なくINI形式にこだわり、階層管理が破綻する |
| inventory | dev/stg/prodでディレクトリを分ける | hostsファイルをコメントで切り替える |
| group_vars | 各inventoryディレクトリ配下に置き環境差分を自動適用 | プロジェクトルートにまとめて置き環境が分離されない |
| host_vars | IPアドレス・ロールなど真に固有の値だけをhost_varsで管理 | 全サーバーを同じ変数で一律設定する |
| 変数の事前検証 | ansible-inventory --list / --graph で解決値・グループ構造をPlaybook実行前に確認 | 実行してみるまで変数が正しいか確認しない |
| シークレット管理 | prod専用のVaultファイルでDBパスワードを暗号化 | 平文パスワードをgroup_varsに直書きする |
| 本番確認フロー | vars_promptで確認入力を要求し、--check --diffでドライランしてから本番適用 | 差分確認なしにprodへ直接実行する |
| role分割 | 1 role = 1サービスが基準 | 1 roleに全設定を詰め込む |
| defaults | 外から変える値はdefaults、固定値はvarsに | vars/main.ymlに全変数を書いて上書きできない |
| module選択 | 冪等性があるmoduleを優先し、command/shellは最終手段 | shellで全処理を書いて再実行でエラーになる |
inventoryで環境を分離し、ansible-inventoryで変数を事前検証し、roleで機能を分割し、冪等性のあるmoduleを選ぶ。この4つの習慣を押さえるだけで、Ansibleは「1台のサーバー設定ツール」から「何台でも同じ状態を保証できる構成管理基盤」に変わります。
Ansibleのinventory・role・module設計を実際に手を動かしながら体系的に身につけたい方は、ぜひ詳細をご覧ください。
Ansible実践ハンズオンの詳細を見る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 次のページへ:Terraformのmoduleとfor_each・countで構成を再利用する設計パターン
- 前のページへ:AnsibleでAWS上にNginx・PHP・MariaDBを自動構築する実践手順
- この記事の属するカテゴリ:Ansibleへ戻る

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