Ansibleのinventory・role・module設計|現場で崩れない構成管理の基礎

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Ansible > Ansibleのinventory・role・module設計|現場で崩れない構成管理の基礎
「Ansibleを使い始めたけれど、ファイルが増えるにつれて管理が混乱してきた」「inventoryをどう分けるべきか、roleはどう設計すればいいかが分からない」
こういった悩みは、Ansibleを実務で使い始めた多くのエンジニアが通る道です。

チュートリアルの段階ではひとつのPlaybookに全部書いても問題ありません。ですが、管理するサーバー台数が増え、環境(開発・本番)が分かれ、チームで共有するようになった瞬間に「なぜこう書いたのか誰も分からない」状態に陥ります。

この記事では、ansible inventory role module の3軸を軸に、現場で崩れない構成管理の設計方針を解説します。「ファイルの置き場所の原則」「roleの分割粒度」「moduleの選び方」まで、実際に手を動かしながら理解できる内容にしています。

この記事のポイント

・ansible inventory は環境別(dev/prod)に分離し host_vars で個体差を吸収する
・role は機能単位(nginx/mariadb)で分割し依存性を ansible-galaxy で管理する
・module は「冪等性が保証されるか」を基準に選択。command/shell は最終手段
・inventory・role・module の設計が揃って初めて構成管理が安定する


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

なぜinventory・role・moduleの設計が構成管理の核心なのか

Ansibleで構成管理を始めると、最初は一枚のPlaybookで十分に感じます。しかし現場では早い段階でこんな問題が起きます。

・本番と開発でIPアドレスが違うのにPlaybookをコピーして運用している
・Nginxの設定とMariaDBの設定が同一ファイルに混在していてどこを直せばいいか分からない
・`shell: curl http://...` を書いてしまい、何度実行しても同じ結果になるか確信が持てない

これらの問題はツールの問題ではなく、設計の問題です。inventory・role・moduleの3つを正しく設計すれば、Ansibleは「何度実行しても同じ状態になる」「環境差分を安全に管理できる」ツールとして機能します。

inventory設計:環境と個体差を分離する

1. ディレクトリ型inventoryで環境を分ける

ファイル型inventoryの典型的な失敗パターンは、`hosts` という1ファイルに開発・本番のホストをコメントで切り替えながら書くことです。コメントアウトのミスが本番事故につながります。

推奨するのは、環境ごとにディレクトリを分けるディレクトリ型inventoryです。

# ディレクトリ構造(推奨) inventories/ ├── dev/ │ ├── 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 [all:vars] ansible_user=ec2-user ansible_ssh_private_key_file=~/.ssh/dev_key.pem

実行時に `-i` でinventoryを切り替えることで、誤って本番に実行するリスクを大幅に下げられます。

# 開発環境に対してPlaybookを実行 ansible-playbook -i inventories/dev site.yml # 本番環境に対してPlaybookを実行(ドライラン推奨) ansible-playbook -i inventories/prod site.yml --check

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

変数の優先順位(後優先)はAnsibleの重要な仕様です。`host_vars` は `group_vars` より優先されるため、グループ共通のデフォルト値をgroup_varsに、個体固有の上書き値をhost_varsに書く設計が安定します。

3. 動的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

タグ(Env=dev, Role=web)でホストが自動グルーピングされるため、インスタンスを追加・削除しても inventory ファイルの編集は不要になります。実際に検証機(t3.micro)で試した出力例は以下の通りです。

# 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"] } }

role設計:機能単位で分割し依存を管理する

1. roleのディレクトリ構造を理解する

roleはAnsibleが定める規約ディレクトリを持ちます。`ansible-galaxy role init` で雛形を生成するのが最も確実です。

# 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-fpm` roleは `nginx` roleが先にインストールされている必要があるケースがあります。こういった依存関係は `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はないか?」

例えば、`shell: systemctl enable nginx` と書きたい場合は `ansible.builtin.service` moduleの `enabled: true` で代替できます。`shell: useradd deploy` と書きたい場合は `ansible.builtin.user` moduleで代替できます。

どうしても `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)の記法で書くことが推奨されています。

旧来の書き方:`dnf:` / `service:` / `template:`
推奨する書き方:`ansible.builtin.dnf:` / `ansible.builtin.service:` / `ansible.builtin.template:`

FQCNで書くと、どのコレクションのmoduleを使っているかが明確になり、コレクション更新時の動作変化を追いやすくなります。チームで共有するPlaybookほどFQCN表記を徹底してください。

トラブルシュート:よくある設計ミスと対処法

「変数が上書きされない」→優先順位を確認する

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` に書くのが鉄則です。

「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

本記事のまとめ

inventory・role・module設計の要点をまとめます。

設計対象 原則 よくある失敗
inventory 環境(dev/prod)でディレクトリを分ける hostsファイルをコメントで切り替える
group_vars 環境共通変数はgroup_vars/all.ymlに置く PlaybookにIPをハードコードする
host_vars サーバー個体固有の値をhost_varsで上書く 全サーバーを同じ変数で一律設定する
role分割 1 role = 1サービスが基準 1 roleに全設定を詰め込む
defaults 外から変える値はdefaults、固定値はvarsに vars/main.ymlに全変数を書いて上書きできない
module選択 冪等性があるmoduleを優先し、command/shellは最終手段 shellで全処理を書いて再実行でエラーになる
構成管理が「崩れない」かどうかは、最初の設計で決まります。
inventoryで環境を分離し、roleで機能を分割し、冪等性のあるmoduleを選ぶ。この3軸を押さえるだけで、Ansibleは「1台のサーバー設定ツール」から「何台でも同じ状態を保証できる構成管理基盤」に変わります。

Ansibleのinventory・role・module設計を実際に手を動かしながら体系的に身につけたい方は、ぜひ詳細をご覧ください。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Ansible実践ハンズオンの詳細を見る >>

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

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

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

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

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

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

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

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

この記事を書いた人

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

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

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