📚 Ansibleが理解できない理由はLinuxにあった【Ansible編】第9回:なぜPlaybookが読めないのか
⓪ なぜShellの知識がないとAnsibleは壊れるのか
① なぜshellモジュールは危険なのか
② なぜmoduleを使うと安全になるのか
③ なぜ毎回changedになるのか
④ なぜ条件分岐が壊れるのか(Ansible編)
⑤ なぜ変数が意図通りに扱えないのか
⑥ なぜタスクの順序で壊れるのか
⑦ なぜエラー制御が破綻するのか
⑧ なぜ非同期・待機で失敗するのか
⑨ なぜPlaybookが読めないのか
⑩ なぜAnsibleを正しく設計できるようになるのか
🗺️ 初めての方・シリーズの全体像を知りたい方はこちら
本記事は、OSの仕組みからAnsible設計までを繋ぐ連載シリーズの一部です。
「どこから読み始めればいいか」あるいは、「OS/Shell/Ansible編の関係性」 について把握されたい場合は、以下の統合ガイドで整理しています。
📑 【Ansible編】全体のまとめはこちら
「Ansibleが理解できない理由はLinuxにあった【Ansible編】まとめ」
📑 連載の移動
前の記事:【Ansible編】第8回 | 次の記事:【Ansible編】第10回
📋 目次
- 前回の振り返り(第8回)
- 逆引き辞典との連動
- はじめに(この記事のスタンス)
- 問題定義:手続き的記述による「巨大な単一ファイル」の限界
- 構造解剖:Ansible Roleによる関心の分離とディレクトリ構造
- Roleのディレクトリ構造と各フォルダの役割
- 原因分析:タスク・変数・テンプレートが混在する「密結合」の問題
- 設計:再利用性を高めるRole分割と変数のカプセル化
- 本質①:手続き的な「コード」の問題
- 本質②:Roleを用いた「定義書」への置き換え
- まとめ
- 次回予告
- 連載一覧:Ansibleが理解できない理由はLinuxにあった【Ansible編】
※本記事の位置とシリーズ全体の関係を先に確認します。
シリーズ全体構造(学習 × 問題解決)
本シリーズは
「理解(各記事)」と「問題解決(逆引き辞典)」を組み合わせて
スキルを身につける構成になっています。
この図は、どこから学び、どこに進めばよいかを示した“ロードマップ”です。
📍 現在の位置
現在はこの図の「Ansible編の第9回」になります。
1. 前回の振り返り(第8回)
前回 は、非同期実行(async)と待機(wait_for)のメカニズムを取り上げました。長時間タスクでSSH接続が切断される問題を回避する方法と、プロセスの完了を待つのではなくサービスの健全性(ポート開放等)を確認してから後続処理を開始する設計手法を確認しました。
2. 逆引き辞典との連動
「設計上の原因」を特定したい場合は、以下の逆引き辞典を活用してください。
Ansibleが理解できない理由はLinuxにあった【Ansible編】:トラブル逆引き辞典(設計パターン版)
- まず「逆引き辞典」で該当ステップを特定する
- 次に「本編(各連載記事)」で仕組みを理解する
3. はじめに(この記事のスタンス)
Ansibleの適用範囲が広がるにつれて、Playbookは肥大化していきます。
当初は「一つのYAMLファイルに全てを記述する」手法で十分だったものが、やがて「どこで何が定義されているのか分からない」「特定の処理だけを再利用できない」といった問題を引き起こします。
本記事では、Ansibleにおける構造化の中核となる「Role」を取り上げます。Roleは単なるファイル分割の仕組みではなく、責務ごとに処理を分離し、再利用可能な単位として設計するための枠組みです。
この設計原則を理解することで、可読性が高く、長期的な保守に耐えるインフラ構成管理の実践的な手法を身につけることができます。
※ 本記事の内容はAnsible公式ドキュメントに基づいています
📄 参考
4. 問題定義:手続き的記述による「巨大な単一ファイル」の限界
実務において、構造化が不十分なPlaybookでは以下のような問題が発生します。
-
スパゲッティコード化: 多数の
when条件やregister変数が複雑に絡み合い、特定のタスクを修正した際の影響範囲が把握できなくなります。 - 変数の重複と衝突: 異なる機能で同じ変数名を使用した結果、Ansibleの変数優先順位によって値が上書きされ、意図しない挙動を引き起こします。
- 再利用性の喪失: 他のプロジェクトで同様の設定が必要になっても、既存のPlaybookから該当箇所を抽出・コピーするしかなく、コードの重複と保守コストの増大を招きます。
これらの問題は、Ansibleを「コマンドの羅列」として扱い、機能ごとの分離や再利用可能な単位での設計(Role化)を行っていないことが根本原因です。
5. 構造解剖:Ansible Roleによる関心の分離とディレクトリ構造
Ansible Roleは、関連するタスク・変数・ファイル・テンプレートを一定のディレクトリ構造に従って整理する仕組みです。
【検証】Roleのディレクトリ構造と自動読み込みの確認
ansible-galaxy initで生成されるディレクトリ構造と、各main.ymlが自動読み込みされることを確認します。またtemplates/がタスクからの参照時のみ使用される動作も合わせて確認します。
■事前準備
【コントローラーノード側】
以下のコマンドでroles/配下にRoleの雛形を生成します。
実行コマンド
ansible-galaxy init roles/verify_structure
▼ 実行結果
(ansible) [ansible@localhost workspace]$ ansible-galaxy init roles/verify_structure
- Role roles/verify_structure was created successfully
生成されたディレクトリ構造を確認します。
実行コマンド
find roles/verify_structure -type f | sort
▼ 実行結果
(ansible) [ansible@localhost workspace]$ find roles/verify_structure -type f | sort
roles/verify_structure/README.md
roles/verify_structure/defaults/main.yml
roles/verify_structure/handlers/main.yml
roles/verify_structure/meta/main.yml
roles/verify_structure/tasks/main.yml
roles/verify_structure/tests/inventory
roles/verify_structure/tests/test.yml
roles/verify_structure/vars/main.yml
次に、自動読み込みの動作確認用Playbookを作成します。
ファイル名:roles/verify_structure/defaults/main.yml
---
nginx_worker_processes: 1
ファイル名: roles/verify_structure/vars/main.yml
---
nginx_env: "production"
ファイル名: roles/verify_structure/tasks/main.yml
---
- name: defaults/main.ymlの変数を確認する
ansible.builtin.debug:
msg: "worker_processes: {{ nginx_worker_processes }}"
- name: vars/main.ymlの変数を確認する
ansible.builtin.debug:
msg: "env: {{ nginx_env }}"
ファイル名: verify_role_structure.yml
---
- name: Roleのディレクトリ構造と自動読み込みの確認
hosts: test_servers
gather_facts: false
roles:
- verify_structure
実行コマンド
ansible-playbook -i inventory.ini verify_role_structure.yml
▼ 実行結果
PLAY [Roleのディレクトリ構造と自動読み込みの確認] *******************************************************************************************************************************************
TASK [verify_structure : defaults/main.ymlの変数を確認する] *********************************************************************************************************************************
ok: [192.168.1.21] => {
"msg": "worker_processes: 1"
}
ok: [192.168.1.22] => {
"msg": "worker_processes: 1"
}
TASK [verify_structure : vars/main.ymlの変数を確認する] *************************************************************************************************************************************
ok: [192.168.1.21] => {
"msg": "env: production"
}
ok: [192.168.1.22] => {
"msg": "env: production"
}
PLAY RECAP **********************************************************************************************************************************************************************************
192.168.1.21 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.22 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
■結果
ansible-galaxy init roles/verify_structureの実行により、tasks/・vars/・defaults/・handlers/・meta/・tests/の各ディレクトリとmain.ymlが自動生成されることを確認しました。
Playbookの実行結果では、defaults/main.ymlに定義したnginx_worker_processes: 1と、vars/main.ymlに定義したnginx_env: "production"がいずれも明示的なインクルード記述なしで参照できています。これにより、Roleを呼び出すだけでdefaults/とvars/のmain.ymlが自動的に読み込まれることが確認できました。
なおtemplates/はタスクでtemplateモジュールを使用した際に自動参照される仕組みであり、defaults/・vars/とは読み込みのタイミングが異なります。この違いは次セクション以降で実際のテンプレート利用を通じて確認します。
📄 出典
6. Roleのディレクトリ構造と各フォルダの役割
ansible-galaxy initで生成される各フォルダには決められた役割があります。Roleを呼び出すだけで自動的に読み込まれるものと、タスクから明示的に参照するものに分かれます。
| フォルダ | 役割 | 自動読み込み | 使用するケース |
|---|---|---|---|
tasks/ |
Roleが実行する処理手順を配置する。main.ymlがRoleのエントリーポイントになる |
✅ | パッケージのインストール、設定ファイルの配置、サービスの起動など |
vars/ |
Role内部で使用する固定値を定義する。変数優先順位が高く、Playbook側からの上書きは効かない | ✅ | Role内部でのみ使用する値(パスや固定パラメータなど) |
defaults/ |
Roleのデフォルト変数を定義する。変数優先順位が最も低く、Playbook側から自由に上書きできる | ✅ | 外部から変更されることを前提とした値(ポート番号、プロセス数など) |
handlers/ |
notifyを受けて実行される処理を定義する。変更がなければ実行されない |
✅ | 設定ファイル変更時のサービスreloadなど、条件付きで実行したい処理 |
templates/ |
Jinja2形式のテンプレートファイルを配置する | ❌ | 環境ごとに値が異なる設定ファイルを動的に生成したい場合 |
templates/だけ自動読み込みの対象外です。タスク内でtemplateモジュールを使用した際に、templates/フォルダが自動的に参照されます。
各フォルダの役割が整理できたところで、次にこれらが混在した場合に何が起きるかを見ていきます。
📄 出典
7. 原因分析:タスク・変数・テンプレートが混在する「密結合」の問題
Playbookが読めなくなる根本的な原因は、「手順(タスク)」「パラメータ(変数)」「リソース(設定ファイル)」が適切に分離されておらず、密結合の状態にあることです。
Linuxのシェルスクリプトに置き換えると分かりやすいです。設定ファイルの生成から処理ロジックまでを一つのスクリプトに詰め込み、引数で挙動を制御しようとすれば、すぐに保守不能な状態に陥ります。Ansibleでも同じことが起きます。
タスク・変数・テンプレートをそれぞれ適切なディレクトリに配置し、疎結合に保つことが、可読性と保守性を維持するための基本的な設計原則です。
8. 設計:再利用性を高めるRole分割と変数のカプセル化
堅牢な自動化基盤を構築するには、Roleを「一つの完結したコンポーネント」として設計する必要があります。
-
単一責任の原則: 一つのRoleは一つの目的(例:
nginxのインストールと設定)に特化させ、責務を明確に分離します。 -
デフォルト値の分離: 外部(PlaybookやInventory)から変更されることを前提とした値は
defaults/main.ymlに定義します。Role本体を変更せずに挙動を制御できるため、再利用性が高まります。 -
main.ymlの簡素化:
tasks/main.ymlが長くなる場合は、import_tasksを使って処理単位でファイルを分割します。ただしimport_tasksはコンパイル時に静的に解決されるため、loopと組み合わせて使用することはできません。ループが必要な場合はinclude_tasksを使用します。
【検証】import_tasksによるタスク分割の動作確認
tasks/main.ymlからimport_tasksで別ファイルを読み込んだ際の動作と、import_tasksとinclude_tasksのループ制約の違いを確認します。
■事前準備
【コントローラーノード側】
以下のコマンドでroles/配下にRoleの雛形を生成します。
実行コマンド
ansible-galaxy init roles/nginx_setup
▼ 実行結果
(ansible) [ansible@localhost workspace]$ ansible-galaxy init roles/nginx_setup
- Role roles/nginx_setup was created successfully
次に、各ファイルを作成します。
ファイル名: roles/nginx_setup/tasks/main.yml
---
# tasks file for roles/nginx_setup
- name: インストール処理を読み込む
ansible.builtin.import_tasks: install.yml
- name: ループ処理を読み込む
ansible.builtin.include_tasks: loop_tasks.yml
ファイル名: roles/nginx_setup/tasks/install.yml
---
- name: import_tasksで読み込まれたタスクを確認する
ansible.builtin.debug:
msg: "install.ymlが読み込まれました"
ファイル名: roles/nginx_setup/tasks/loop_tasks.yml
---
- name: include_tasksでループを実行する
ansible.builtin.debug:
msg: "item: {{ item }}"
loop:
- "item1"
- "item2"
- "item3"
ファイル名: verify_import_tasks.yml
---
- name: import_tasksとinclude_tasksの動作確認
hosts: test_servers
gather_facts: false
roles:
- nginx_setup
実行コマンド
ansible-playbook -i inventory.ini verify_import_tasks.yml
▼ 実行結果
PLAY [import_tasksとinclude_tasksの動作確認] ************************************************************************************************************************************************
TASK [nginx_setup : import_tasksで読み込まれたタスクを確認する] *****************************************************************************************************************************
ok: [192.168.1.21] => {
"msg": "install.ymlが読み込まれました"
}
ok: [192.168.1.22] => {
"msg": "install.ymlが読み込まれました"
}
TASK [nginx_setup : ループ処理を読み込む] ***************************************************************************************************************************************************
included: /opt/ansible/workspace/roles/nginx_setup/tasks/loop_tasks.yml for 192.168.1.21, 192.168.1.22
TASK [nginx_setup : include_tasksでループを実行する] ****************************************************************************************************************************************
ok: [192.168.1.21] => (item=item1) => {
"msg": "item: item1"
}
ok: [192.168.1.21] => (item=item2) => {
"msg": "item: item2"
}
ok: [192.168.1.21] => (item=item3) => {
"msg": "item: item3"
}
ok: [192.168.1.22] => (item=item1) => {
"msg": "item: item1"
}
ok: [192.168.1.22] => (item=item2) => {
"msg": "item: item2"
}
ok: [192.168.1.22] => (item=item3) => {
"msg": "item: item3"
}
PLAY RECAP **********************************************************************************************************************************************************************************
192.168.1.21 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.22 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
■結果
import_tasksで読み込んだinstall.ymlのタスクが正常に実行され、「install.ymlが読み込まれました」というメッセージが出力されました。import_tasksはPlaybook実行前のコンパイル時にタスクとして展開されるため、実行ログ上ではmain.ymlのタスク名ではなくinstall.yml内のタスク名(「import_tasksで読み込まれたタスクを確認する」)が直接表示されています。
include_tasksで読み込んだloop_tasks.ymlについては、実行ログに「ループ処理を読み込む」というinclude自体のタスクが表示された後、loop_tasks.yml内のタスクが動的に読み込まれてitem1・item2・item3の順にループ実行されています。include_tasksは実行時に動的に解決されるため、loopとの組み合わせが可能です。
この挙動の違いが、import_tasksとinclude_tasksの使い分けの基準となります。静的に展開されれば良い処理はimport_tasks、ループや条件分岐で動的に読み込む必要がある処理はinclude_tasksを使用します。
📄 出典
9. 本質①:手続き的な「コード」の問題
Ansibleを「何をするか(How)」の命令セットとして扱うと、Playbookはやがて手続き的なシェルスクリプトと変わらない状態になります。
以下はsedコマンドで設定ファイルを書き換える典型的な悪い例です。一見すると動作しそうに見えますが、この書き方には環境依存という根本的な問題が潜んでいます。
# site.yml(単一ファイルに詰め込み)
- hosts: web_servers
tasks:
- name: パッケージを入れる
dnf:
name: nginx
state: latest
- name: 設定ファイルを書き換える
shell: sed -i 's/worker_processes 1/worker_processes 4/' /etc/nginx/nginx.conf
- name: 文法チェックして再起動
shell: nginx -t && systemctl restart nginx
【検証】sedによる設定ファイル書き換えの動作確認
dnfでnginxをインストールし、デフォルトのnginx.confに対してsedのパターンがマッチするかを確認します。
■事前準備:nginxのインストールとポート80番の開放
【コントローラーノード側】
ファイル名: setup_nginx.yml
---
- name: Setup nginx
hosts: test_servers
gather_facts: false
become: true
tasks:
- name: nginxをインストールする
ansible.builtin.dnf:
name: nginx
state: present
- name: ポート80を開放する
ansible.builtin.firewalld:
service: http
permanent: true
state: enabled
immediate: true
実行コマンド
ansible-playbook -i inventory.ini setup_nginx.yml
▼ 実行結果
PLAY [Setup nginx] **************************************************************************************************************************************************************************
TASK [nginxをインストールする] **************************************************************************************************************************************************************
ok: [192.168.1.22]
ok: [192.168.1.21]
TASK [ポート80を開放する] *******************************************************************************************************************************************************************
ok: [192.168.1.21]
ok: [192.168.1.22]
PLAY RECAP **********************************************************************************************************************************************************************************
192.168.1.21 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.22 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
【リモートノード側の確認】
インストール後のデフォルトnginx.confのworker_processesの値を確認します。
実行コマンド
grep worker_processes /etc/nginx/nginx.conf
▼ 実行結果
- リモートノード1 (
192.168.1.21)
[root@localhost ~]# grep worker_processes /etc/nginx/nginx.conf
worker_processes auto;
- リモートノード2 (
192.168.1.22)
[root@localhost ~]# grep worker_processes /etc/nginx/nginx.conf
worker_processes auto;
■検証内容
【コントローラーノード側】
ファイル名: verify_sed.yml
---
- name: 悪い例の動作確認
hosts: test_servers
gather_facts: false
become: true
tasks:
- name: 【悪い例】設定ファイルをsedで書き換える
ansible.builtin.shell: sed -i 's/worker_processes 1/worker_processes 4/' /etc/nginx/nginx.conf
- name: 【悪い例】書き換え結果を確認する
ansible.builtin.shell: grep worker_processes /etc/nginx/nginx.conf
register: result
- name: 【悪い例】結果を表示する
ansible.builtin.debug:
var: result.stdout
- name: 【悪い例】文法チェックして再起動する
ansible.builtin.shell: nginx -t && systemctl restart nginx
実行コマンド
ansible-playbook -i inventory.ini verify_sed.yml
▼ 実行結果
PLAY [悪い例の動作確認] *********************************************************************************************************************************************************************
TASK [【悪い例】設定ファイルをsedで書き換える] **********************************************************************************************************************************************
changed: [192.168.1.21]
changed: [192.168.1.22]
TASK [【悪い例】書き換え結果を確認する] *****************************************************************************************************************************************************
changed: [192.168.1.22]
changed: [192.168.1.21]
TASK [【悪い例】結果を表示する] *************************************************************************************************************************************************************
ok: [192.168.1.21] => {
"result.stdout": "worker_processes auto;"
}
ok: [192.168.1.22] => {
"result.stdout": "worker_processes auto;"
}
TASK [【悪い例】文法チェックして再起動する] *************************************************************************************************************************************************
fatal: [192.168.1.21]: FAILED! => {"changed": true, "cmd": "nginx -t && systemctl restart nginx", "delta": "0:00:02.670295", "end": "2026-06-12 10:10:34.094327", "msg": "non-zero return code", "rc": 1, "start": "2026-06-12 10:10:31.424032", "stderr": "nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\nnginx: configuration file /etc/nginx/nginx.conf test is successful\nJob for nginx.service failed because the control process exited with error code.\nSee \"systemctl status nginx.service\" and \"journalctl -xeu nginx.service\" for details.", "stderr_lines": ["nginx: the configuration file /etc/nginx/nginx.conf syntax is ok", "nginx: configuration file /etc/nginx/nginx.conf test is successful", "Job for nginx.service failed because the control process exited with error code.", "See \"systemctl status nginx.service\" and \"journalctl -xeu nginx.service\" for details."], "stdout": "", "stdout_lines": []}
fatal: [192.168.1.22]: FAILED! => {"changed": true, "cmd": "nginx -t && systemctl restart nginx", "delta": "0:00:02.689177", "end": "2026-06-12 10:10:34.417651", "msg": "non-zero return code", "rc": 1, "start": "2026-06-12 10:10:31.728474", "stderr": "nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\nnginx: configuration file /etc/nginx/nginx.conf test is successful\nJob for nginx.service failed because the control process exited with error code.\nSee \"systemctl status nginx.service\" and \"journalctl -xeu nginx.service\" for details.", "stderr_lines": ["nginx: the configuration file /etc/nginx/nginx.conf syntax is ok", "nginx: configuration file /etc/nginx/nginx.conf test is successful", "Job for nginx.service failed because the control process exited with error code.", "See \"systemctl status nginx.service\" and \"journalctl -xeu nginx.service\" for details."], "stdout": "", "stdout_lines": []}
PLAY RECAP **********************************************************************************************************************************************************************************
192.168.1.21 : ok=3 changed=2 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
192.168.1.22 : ok=3 changed=2 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
■結果
sedのパターンworker_processes 1はRocky Linuxのデフォルト値worker_processes autoに対してマッチしませんでした。sedはパターンが一致しない場合でもエラーを返さずchangedを返すため、書き換えが失敗していても気づけないという問題があります。
さらにnginx.confが書き換わっていない状態でsystemctl restart nginxを実行したところ、nginxの起動に失敗しました。shellモジュールによるコマンド連結は前提条件の変化に対応できず、環境が変わった瞬間に動作しなくなる脆さを持っています。
この2点が「手続き的なコード」の根本的な問題です。次のセクションでは、これらの問題をtemplateモジュールを使った「定義書」への置き換えによって解決します。
📄 出典
10. 本質②:Roleを用いた「定義書」への置き換え
前のセクションで確認した通り、sedによる書き換えは環境依存の問題を抱えており、パターンが一致しない場合でもエラーを返しません。
このセクションでは、sedという「手続き」をtemplateモジュールによる「あるべき状態の定義」に置き換えることで、これらの問題がどのように解消されるかを確認します。
① 表層:Playbook(site.yml)
利用者は「あるべき値」を宣言するだけに徹します。
# site.yml(構成の定義)
- hosts: web_servers
roles:
- role: nginx_setup
vars:
nginx_worker_processes: 4 # ここを変えるだけで構成が変わる
② 隠蔽された実装:Role内部
sedによる加工を排除し、templateモジュールで「状態」を管理します。
# roles/nginx_setup/tasks/main.yml
- name: 設定ファイルのデプロイ
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: reload nginx
📋 手続き(sed)から定義(Role)への対応関係
| 手続き的な要素(悪い例) | Roleでの対応要素 | 代替の仕組み |
|---|---|---|
sedコマンド実行 |
templateモジュール |
差分編集ではなく、正しい内容のファイルを丸ごと生成して配置する |
worker_processes 1(検索パターン) |
nginx.conf.j2(雛形) |
ファイル内のどこを変数にするか予め定義。探す「手続き」を排除 |
4(置換後の値) |
vars(変数定義) |
設定したい値をロジックから分離 |
【検証】templateモジュールによる設定ファイル配置とHandlerの動作確認
templateモジュールでnginx.confを生成・配置し、変数が正しく展開されることと、notifyによってHandlerが発火することを確認します。
■事前準備
【コントローラーノード側】
以下のファイルを作成します。
ファイル名: roles/nginx_setup/defaults/main.yml
---
nginx_worker_processes: 1
nginx_port: 80
ファイル名: roles/nginx_setup/templates/nginx.conf.j2
worker_processes {{ nginx_worker_processes }};
events {
worker_connections 1024;
}
http {
server {
listen {{ nginx_port }};
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
}
ファイル名: roles/nginx_setup/handlers/main.yml
---
- name: reload nginx
ansible.builtin.service:
name: nginx
state: restarted
ファイル名: roles/nginx_setup/tasks/main.yml
---
# tasks file for roles/nginx_setup
- name: 設定ファイルのデプロイ
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: reload nginx
ファイル名: verify_template.yml
---
- name: templateモジュールによる設定ファイル配置の確認
hosts: test_servers
gather_facts: false
become: true
roles:
- role: nginx_setup
vars:
nginx_worker_processes: 4
実行コマンド
ansible-playbook -i inventory.ini verify_template.yml
▼ 実行結果
PLAY [templateモジュールによる設定ファイル配置の確認] ***************************************************************************************************************************************
TASK [nginx_setup : 設定ファイルのデプロイ] *************************************************************************************************************************************************
changed: [192.168.1.21]
changed: [192.168.1.22]
RUNNING HANDLER [nginx_setup : reload nginx] ************************************************************************************************************************************************
changed: [192.168.1.21]
changed: [192.168.1.22]
PLAY RECAP **********************************************************************************************************************************************************************************
192.168.1.21 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.1.22 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
【リモートノード側の確認】
配置されたnginx.confの内容とworker_processesの値を確認します。
実行コマンド
grep worker_processes /etc/nginx/nginx.conf
systemctl status nginx
▼ 実行結果
- リモートノード1 (
192.168.1.21)
[root@localhost ~]# grep worker_processes /etc/nginx/nginx.conf
worker_processes 4;
- リモートノード2 (
192.168.1.22)
[root@localhost ~]# grep worker_processes /etc/nginx/nginx.conf
worker_processes 4;
■結果
nginx.confの内容が変更されたためchangedが発生し、notifyによってHandlerが発火してnginxが再起動されました。リモートノード側の確認でもworker_processes 4;が正しく反映されており、defaults/main.ymlのデフォルト値(1)がPlaybook側の変数(4)で上書きされていることが確認できました。
第9セクションのsedと比較すると、templateモジュールは環境のデフォルト値に依存せず、変数で指定した値を確実に設定ファイルに反映します。またsedのように「パターンが一致しなくてもchangedを返す」という問題もなく、実際に変更が発生したかどうかを正確に把握できます。
📄 出典
11. まとめ
本記事では、Ansible Roleを用いた構造化設計の考え方と、その実践的な適用方法を確認しました。
- 巨大なPlaybookはRoleを使って機能単位にカプセル化することで、可読性と保守性が高まります。
-
sed等の手続き的な処理は、環境のデフォルト値に依存するため、OSやディストリビューションが変わった瞬間に動作しなくなるリスクがあります。templateモジュールによる「あるべき状態の定義」に置き換えることで、この問題を回避できます。 - 変数・タスク・テンプレートを疎結合に保つことが、再利用可能で保守しやすいインフラ資産への近道です。
📄 出典
12. 次回予告
Roleによる構造化を理解したことで、Playbookは「読める状態」になりました。しかし実務で求められるのは、読めるだけでなく「壊れない設計」を一貫して組み立てられることです。
シリーズ最終回となる第10回では、これまでの知識を統合し、LinuxやShellの構造理解とAnsibleの設計思想がどのように結びつくのかを整理します。現場で通用する再現性のある自動化設計の全体像をまとめます。
次回:【Ansible編】第10回:なぜAnsibleを正しく設計できるようになるのか
📑 連載の移動
前の記事:【Ansible編】第8回 | 次の記事:【Ansible編】第10回
📑 【Ansible編】全体のまとめはこちら
「Ansibleが理解できない理由はLinuxにあった【Ansible編】まとめ」
🗺️ 初めての方・シリーズの全体像を知りたい方はこちら
本記事は、OSの仕組みからAnsible設計までを繋ぐ連載シリーズの一部です。
「どこから読み始めればいいか」あるいは、「OS/Shell/Ansible編の関係性」 について把握されたい場合は、以下の統合ガイドで整理しています。
📚13. 連載一覧:Ansibleが理解できない理由はLinuxにあった【Ansible編】
※以下随時更新
| 回数とタイトル | 内容(概要) |
|---|---|
| 【Ansible編】第0回:なぜShellの知識がないとAnsibleは壊れるのか | AnsibleはShellのラッパーとして動作している。Shell編で学んだ構造(Ansible → SSH → Shell → Linux)を再確認し、「なぜmoduleを使うべきなのか」の前提を整理する。 |
| 【Ansible編】第1回:なぜshellモジュールは危険なのか | shellモジュールはShellの仕様(分割・展開・環境)に依存するため壊れやすい。commandとの違いを理解し、「どこまでShellを許容すべきか」を判断できるようになる。 |
| 【Ansible編】第2回:なぜmoduleを使うと安全になるのか | file / copy / lineinfile などのmoduleは状態管理を前提としている。「結果」ではなく「状態」を扱うことで、Shell依存を排除し安全な構成を実現する。 |
| 【Ansible編】第3回:なぜ毎回changedになるのか | shellモジュールは状態を判定できないため、常にchangedになる。changed_whenを使った制御と冪等性(idempotency)の設計を理解する。 |
| 【Ansible編】第4回:なぜ条件分岐が壊れるのか(Ansible編) | when条件が意図通りに動かない原因は、rc / stdout / stderrの扱いにある。Shell編で学んだ終了ステータスと組み合わせ、正しい条件分岐を設計する。 |
| 【Ansible編】第5回:なぜ変数が意図通りに扱えないのか | vars / host_vars / group_vars のスコープや、registerの扱いを誤ると値が壊れる。Ansible内での変数管理と評価タイミングを整理する。 |
| 【Ansible編】第6回:なぜタスクの順序で壊れるのか | Ansibleのタスクはそれぞれ独立したプロセスとして実行される。chdir / environment の制御と、「前の結果に依存する設計」の危険性を理解する。 |
| 【Ansible編】第7回:なぜエラー制御が破綻するのか | ignore_errors や failed_when の使い方を誤るとエラーが隠蔽または暴発する。Shellのrcと連動させた正しいエラー制御を設計する。 |
| 【Ansible編】第8回:なぜ非同期・待機で失敗するのか | サービス起動や外部依存処理はタイミング問題を引き起こす。async / poll / wait_for / retries を使い、安定した実行制御を実現する。 |
| 【Ansible編】第9回:なぜPlaybookが読めないのか | role構成やタスク分割が不適切だと可読性が崩壊する。実務で保守できるPlaybook設計と構造化の原則を理解する。 |
| 【Ansible編】第10回:なぜAnsibleを正しく設計できるようになるのか | これまでの知識を統合し、Shell編と接続することで「壊れない設計」ができるようになる。Ansibleを実務で使いこなすための設計思想を完成させる。 |