📚 連載:Ansibleが理解できない理由はLinuxにあった【Shell編】第5回:なぜ環境が違うのか(Execution Context)
※Shellの仕組みからAnsibleを理解するシリーズです。
① なぜShellを理解しないとAnsibleは使えないのか
② なぜAnsibleで出力が取得できないのか
③ なぜ結果が消えるのか
④ なぜgrepで見つからないのか
⑤ なぜ正規表現で壊れるのか
⑥ なぜ環境が違うのか
⑦ なぜ条件分岐が失敗するのか
⑧ なぜループがうまく動かないのか
⑨ なぜ途中で処理が止まるのか
⑩ なぜ変数が意図通りに展開されないのか
⑪ なぜAnsibleの挙動が読めるようになるのか
🗺️ 初めての方・シリーズの全体像を知りたい方はこちら
本記事は、OSの仕組みからAnsible設計までを繋ぐ連載シリーズの一部です。
「どこから読み始めればいいか」あるいは、「OS/Shell/Ansible編の関係性」 について把握されたい場合は、以下の統合ガイドで整理しています。
📑 【Shell編】全体のまとめはこちら
→ Ansibleが理解できない理由はLinuxにあった【Shell編】まとめ
📑 連載の移動
前の記事:【Shell編】第4回 | 次の記事:【Shell編】第6回
📋 目次
- 前回の振り返り(第4回)
- 逆引き辞典との連動
- 問題定義:同じコマンドなのに、なぜ結果が変わるのか
- なぜ環境が違うのか:Shellの「ログイン方式」による棲み分け
- 観測方法:Ansibleから見える「本当のPATH」を暴く
- よくある実務ミス2選(環境編)
- まとめ:環境を支配するための「設計の原則」
- 次回予告
- 連載一覧:Ansibleが理解できない理由はLinuxにあった【Shell編】
※本記事の位置とシリーズ全体の関係を先に確認します。
シリーズ全体構造(学習 × 問題解決)
本シリーズは
「理解(各記事)」と「問題解決(逆引き辞典)」を組み合わせて
スキルを身につける構成になっています。
この図は、どこから学び、どこに進めばよいかを示した“ロードマップ”です。
📍 現在の位置
現在はこの図の「Shell編の第5回」になります。
📍 はじめに:この記事のスタンス
前回の第4回では、Ansibleから渡した「文字列」がShellによってどう評価(書き換え)されるかを学びました。
これで
「何を実行するか(コマンドの内容)」 は制御できるようになりました。
しかし、実務ではまだ依然として以下の現象が発生します
- 「Teratermでログインして叩けば動くコマンドが、Ansibleからだと『command not found』になる」
- 「
.bashrcに書いた環境変数が、Ansibleのタスク内では空っぽになっている」 - 「絶対パスで指定すれば動くが、なぜ相対パスやコマンド名だけでは動かないのかが分からない」
これらの原因は、コマンドの書き間違いではありません。
「どこで実行されるか(実行コンテキスト)」
のルールが、手動実行時とAnsible実行時で根本的に異なっていることにあります。
⚠️ 注意事項
この記事は、PATH の通し方を教えるものではありません。
目的はただひとつです。
「手動実行環境とAnsible実行環境の“棲み分け”を理解し、環境を完全に制御すること」
手動実行とAnsible実行の環境差を整理します。
1. 前回の振り返り(第4回)
前回は「文字列がどう解釈されるか(評価)」を扱いました。
しかし、その解釈は
「どの環境(PATHや環境変数)で実行されるか」
によって結果が変わります。
前回は、Shellが実行直前に文字列をスキャンして書き換える「評価」のプロセスを学びました。
今回は、その評価が行われるための「前提条件」である
環境(PATHや環境変数)が、なぜ実行の入り口によって切り替わってしまうのか
を紐解きます。
2. 逆引き辞典との連動
具体的なエラー名から原因を特定したい場合は、以下の逆引き辞典を活用してください。
→ 【保存版】Ansibleよくあるエラー一覧と原因まとめ(Shell編)
- まず「逆引き辞典」で該当ステップを特定する
- 次に「本編(各連載記事)」で仕組みを理解する
このサイクルが、OS編から続くトラブル解決の最短ルートです。
3. 問題定義:同じコマンドなのに、なぜ結果が変わるのか
エンジニアを最も悩ませるのが、この現象です。
【手動(SSHログイン後)】
$ my_command --version
my_command version 1.2.3 <-- 正常に動く
【Ansible(shellモジュール)】
"stderr": "/bin/sh: my_command: command not found" <-- 失敗する
同じサーバーの、同じユーザーで実行しているはずなのに、なぜ結果が変わるのか?
それは、OSにとって
「ログインして操作するあなた」と
「SSH経由でコマンドだけを投げるAnsible」は、全く別の環境設定で起動されるから です。
4. なぜ環境が違うのか:Shellの「ログイン方式」による棲み分け
Shell(bash等)には、起動する際の入り口によって、読み込む設定ファイルを変える仕組みがあります。
これを
「実行コンテキスト(Execution Context:Shellがどの設定・環境で起動されるか)」と呼びます。
💡 技術的裏付け:ログインシェルと非ログインシェルの差
| 実行形態 | 分類 | 読み込まれる設定ファイル(代表例) |
|---|---|---|
| 手動実行(SSHログイン) | Interactive Login Shell | /etc/profile, ~/.bash_profile, ~/.bashrc |
| Ansible (shell/command) | Non-Interactive Non-Login Shell | (ユーザー設定ファイルは基本的に読み込まれない) |
【解説:なぜAnsibleはPATHを忘れるのか】
-
手動ログイン時:
Shellは「これから人間が対話的に操作する」と判断し、~/.bash_profileなどの設定ファイルを読み込んでPATHを設定します。 -
Ansible実行時:
AnsibleはSSH経由で「コマンドだけを最短ルートで実行」しようとします。
この時、Shellは「対話は不要」と判断し、ユーザーがカスタマイズした設定ファイルを読み飛ばします。
結果として、Ansibleから見えるPATHは /usr/bin などの最低限のものだけになり、/usr/local/bin や ~/.local/bin に入れたコマンドは見つからない状態になります。
5. 観測方法:Ansibleから見える「本当のPATH」を暴く
「環境が違う」と疑ったら、想像で設定を変える前に
「今のAnsibleが何を見ているか」 を直接確認します。
観測用タスク:envコマンドの実行
- name: Ansibleから見える環境変数をすべて出力する
shell: env
register: remote_env
- debug:
var: remote_env.stdout_lines
この出力結果を、手動でログインして叩いた env の結果と比較してください。
特に以下に注目してください:
- PATHの長さ(明らかに短い)
- HOME配下の設定が反映されていない
📑 実機検証について
本記事のテーマである「手動実行とAnsible実行の環境差」については、OS編第2回にて実機検証を行っています。
SSH手動ログイン・Ansible非対話モード・特権昇格(become)・フルパス指定の4パターンで実際の挙動を確認していますので、あわせて参照してください。
→ 【OS編】第2回:Ansibleでcommand not foundになる理由(PATH)「6. 実機検証:Ansible実行環境における環境変数の差異」
6. よくある実務ミス2選(環境編)
ミス①:.bashrc への依存
-
現象:
インストールしたアプリのパスを~/.bashrcに追記したが、Ansibleからは無視される。 -
解決策:
タスク内でenvironmentキーワードを使用し、明示的にパスを指定する。
または、実行コマンドを絶対パス(/usr/local/bin/my_command)で記述する。
ミス②:独自環境変数の消失
-
現象:
export APP_ENV=productionをプロファイルに書いたのに、Ansibleのタスクで$APP_ENVが空になる。 -
解決策:
Ansibleのvarsやgroup_varsで変数を定義し、タスクに渡す。
「OSの設定に頼らず、Ansible側で環境を定義する」 のが自動化の原則です。
7. まとめ:環境を支配するための「設計の原則」
Ansibleで「コマンドが見つからない」「環境変数が反映されない」時の調査手順です。
【環境 失敗の調査手順】
1. 「絶対パス」で実行して動くか確認する
→ 動くなら、原因はほぼ確実に PATH の欠落(実行コンテキストの差)である。
2. Ansibleの env 出力を確認する
→ 手動時と何が違うのか、どの変数が足りないのかを特定する。
3. 設定ファイルに頼らない
→ .bashrc が読まれることを期待せず、Ansibleの environment オプションで環境を注入する。
設計の原則
自動化において
「OS側の設定ファイル(.bashrc等)に環境を隠し持たせない」 こと。
必要な環境(PATH、変数)はすべてAnsible側から明示的に与える。
これが「どこでも動く」タスクを作る最短ルートです。
環境問題は
入り口の違いを把握しておけば、大抵の原因は特定できます
8. 次回予告
これで「中身(第4回)」と「場所(第5回)」の制御ができるようになりました。
しかし、コマンドを実行した「後」にも罠があります。
shell: grep "error" /var/log/app.log
when: ...
「ログに error は無いはずなのに、なぜか Ansible が失敗(赤色)判定になる」
「Shell側では成功したつもりなのに、Ansibleがそれを汲み取ってくれない」
原因は、ShellがOSに返す
「終わりの合図」
の解釈差にあります。
exit code(終了ステータス)と、Ansibleの when 判定がどう連動しているのか。
その判定ロジックの裏側を解説します。
📑 連載の移動
前の記事:【Shell編】第4回 | 次の記事:【Shell編】第6回
📑 【Shell編】全体のまとめはこちら
→ Ansibleが理解できない理由はLinuxにあった【Shell編】まとめ
🗺️ 初めての方・シリーズの全体像を知りたい方はこちら
本記事は、OSの仕組みからAnsible設計までを繋ぐ連載シリーズの一部です。
「どこから読み始めればいいか」あるいは、「OS/Shell/Ansible編の関係性」 について把握されたい場合は、以下の統合ガイドで整理しています。
9. 連載一覧:Ansibleが理解できない理由はLinuxにあった【Shell編】
| 回数とタイトル | 内容(概要) |
|---|---|
| 【Shell編】第0回:なぜShellを理解しないとAnsibleは使えないのか | AnsibleはShellを通してコマンドを実行している。Ansible → SSH → Shell → Linux の構造を理解し、なぜShell理解が必須なのかを整理する。 |
| 【Shell編】第1回:なぜAnsibleで出力が取得できないのか | Ansibleでregisterが空になる・エラーが見えない原因はstdout / stderrの違いにある。Shellの出力構造を理解することで原因を特定できる。 |
| 【Shell編】第2回:なぜ結果が消えるのか | Ansibleで実行結果が見えなくなる原因はパイプ・リダイレクトによる出力先の変化にある。どこに出力が流れたのかを追うことで原因を特定できる。 |
| 【Shell編】第3回:なぜgrepで見つからないのか | Ansibleでgrepがヒットしない原因は検索対象ではなく入力(stdin)の問題にある。Shellの入力(ストリーム)構造を理解することで原因を特定できる。 |
| 【Shell編】第4回:なぜ正規表現で壊れるのか | Ansibleで検索や置換が壊れる原因は正規表現の評価ルールにある。文字列ではなくルールとして解釈される仕組みを理解し、リテラルの境界を学ぶ。 |
| 【Shell編】第5回:なぜ環境が違うのか | 手動では動くのにAnsibleで失敗する原因は実行環境(PATH・環境変数)の差分にある。Shellの実行コンテキストの違いを理解し、環境を制御する。 |
| 【Shell編】第6回:なぜ条件分岐が失敗するのか | Ansibleのwhen条件が意図通りに動かない原因はexit codeにある。Shellの終了ステータスの判定ロジックを理解することで、正しく条件を組める。 |
| 【Shell編】第7回:なぜループがうまく動かないのか | Ansibleで繰り返し処理が期待通り動かない原因はShellのループと入力・サブシェルの干渉にある。データの扱い方を理解し、ループを安定させる。 |
| 【Shell編】第8回:なぜ途中で処理が止まるのか | Ansible実行中に意図せず処理が止まる原因はShellのエラーハンドリング(set -e 等)にある。エラー伝播の仕組みを理解し、停止条件を制御する。 |
| 【Shell編】第9回:なぜ変数が意図通りに展開されないのか | Ansibleで変数が空になる・壊れる原因はShellのクォートと展開順序にある。値の保護と展開のルールを理解し、安定したコマンドを記述する。 |
| 【Shell編】第10回:なぜ値が分割されてしまうのか | スペースや改行で意図せず値が壊れる原因はShellの単語分割(word splitting)にある。IFSなどの内部処理の本質を理解し、データの整合性を守る。 |
| 【Shell編】第11回:なぜAnsibleの挙動が読めるようになるのか | これまでの知識を統合し、AnsibleのエラーをShellの実行プロセスから逆算して分解できるようになる。実務で使える“読み方”を完成させる。 |