前談
時間を遡って19:00~にAnsibleの忘年会に登壇以外で初めて参加しました!
ほぼ全員が初見にもかかわらず、皆さんに暖かく迎えて頂けて改めてコミュニティのすばらしさを感じました!
私自身にとっても久しぶりの外部コミュニティへの参加だったので、同じレイヤーの話ができて楽しかったです。
さて、忘年会で調子こいでアドベに投稿します!と言ってしまったので、ただいま緊急で書いています!
なにげにここ何年間はずっとアドベに投稿してきたので、記録を継続するための良いきっかけでもあったかと思います。
本題
さて、ここから本番です。
(初心者向け)Ansible開発でエラーが発生した際に都度エラーメッセージをコピペしてAIに質問したりしてませんか?都度コピーだと何気にめんどくさいので、エラーが発生した際に自動的にAIに質問できる仕組みを検証したので、共有したいと思います。
この検証ではローカルLLM環境Ollamaを経由していますが、適宜他のAIサービスに変更して頂いても問題ないです。
早速コード
ポイント:rescueで失敗した際に実行されるので、try catchのcatchのような役割を果たします。
- name: Ansible AI Error Handling Example
hosts: localhost
connection: local
gather_facts: False
tasks:
- block:
- name: 通常のタスク1
command: /bin/true
- name: 失敗するタスク
command: /bin/false
- name: タスク3(実行されない)
debug:
msg: "ここには到達しない"
rescue:
- name: 後処理タスク
debug:
msg: "{{ ansible_failed_result.msg }}"
- name: OllamaにAPI経由でリクエスト
uri:
# Ansible環境がコンテナのため、ホストのOllamaを呼び出すためにhost.docker.internalに設定しています。
url: "http://host.docker.internal:11434/api/generate"
method: POST
body_format: json
body:
# Ollamaで管理しているモデル名
model: "gpt-oss:20b"
# システムプロンプトになります
prompt: "Ansibleでエラーが発生しました: {{ ansible_failed_result.msg }}"
stream: false
return_content: yes
register: ollama_response
- name: レスポンスを表示
debug:
msg: "{{ ollama_response.json.response | split('\n') }}"
出力結果
システムプロンプトがガバガバのせいか結構いろんな情報が出力されています。
PLAY [localhost] ***********************************************************************************************
TASK [通常のタスク1] ***********************************************************************************************
Friday 05 December 2025 23:08:24 +0900 (0:00:00.022) 0:00:00.022 *******
changed: [localhost]
TASK [失敗するタスク] ***********************************************************************************************
Friday 05 December 2025 23:08:25 +0900 (0:00:00.264) 0:00:00.286 *******
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["/bin/false"], "delta": "0:00:00.002209", "end": "2025-12-05 23:08:25.292293", "msg": "non-zero return code", "rc": 1, "start": "2025-12-05 23:08:25.290084", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
TASK [後処理タスク] ***********************************************************************************************
Friday 05 December 2025 23:08:25 +0900 (0:00:00.178) 0:00:00.464 *******
ok: [localhost] => {
"msg": "non-zero return code"
}
TASK [OllamaにAPI経由でリクエスト] ***********************************************************************************************
Friday 05 December 2025 23:08:25 +0900 (0:00:00.009) 0:00:00.474 *******
ok: [localhost]
TASK [レスポンスを表示] ***********************************************************************************************
Friday 05 December 2025 23:08:41 +0900 (0:00:16.407) 0:00:16.882 *******
ok: [localhost] => {
"msg": [
"## 「non‑zero return code」が出たときに確認すべきポイント",
"",
"Ansible のタスクで **`non-zero return code`** が出ると、基本的に「実行したコマンド/スクリプトが失敗した」ことを意味します。 ",
"ただし、何が失敗したのかはタスクの種類や環境によって変わるので、まずは詳細を掴むことが重要です。",
"",
"以下は、エラーを特定・修正するためにすぐに実行できるチェックリストです。 ",
"",
"---",
"",
"### 1. まずは実行結果を確認",
"",
"#### a. `-vvv` オプションで再実行",
"```bash",
"ansible-playbook playbook.yml -vvv",
"```",
"`-vvv`(最大デバッグ)を付けると、 ",
"- 実際に送信されたコマンド ",
"- 返ってきた標準出力 / 標準エラー ",
"- `rc`(return code)が何であるかが表示されます。",
"",
"#### b. `stdout` / `stderr` を確認",
"タスクで `register: result` しているなら",
"```yaml",
"- name: サンプルタスク",
" command: /usr/bin/xxx",
" register: result",
" ignore_errors: yes",
"",
"- name: エラー出力を確認",
" debug:",
" var: result",
"```",
"`result.stderr` が有用です。多くのコマンドはエラー時にメッセージを標準エラーに吐きます。",
"",
"---",
"",
"### 2. タスクの種類別チェック",
"",
"| タスク | チェックポイント | 典型的な失敗原因 |",
"|--------|------------------|------------------|",
"| `shell` / `command` | 1. 実行ファイルが存在するか<br>2. 必要な環境変数がセットされているか<br>3. 権限 (sudo / root) が足りているか | ファイルが無い、パスが通っていない、`sudo` が必要 |",
"| `script` | 1. スクリプトが `ansible` に転送されているか<br>2. 実行権限があるか | スクリプトが壊れている、`chmod +x` が無い |",
"| `uri` | 1. URL が正しいか<br>2. ネットワーク許可があるか | ネットワーク障害、認証失敗 |",
"| `service` / `systemd` | 1. サービス名が正しいか<br>2. 必要な依存があるか | サービスが無い、起動時エラー |",
"| `yum` / `apt` | 1. リポジトリが有効か<br>2. パッケージ名が正しいか | パッケージが無い、リポジトリ未設定 |",
"",
"---",
"",
"### 3. 環境差異を確認",
"",
"- **ホスト変数** / **インベントリ変数** ",
" - 例: `{{ ansible_distribution }}` で OS を判別し、適切なコマンドを実行しているか。",
"- **ユーザー権限** ",
" - `become: yes` が必要なのに抜けていないか。 ",
" - ターゲットホストの `/etc/sudoers` でパスワードなしが許可されているか。",
"- **Python / Ansible バージョン** ",
" - `ansible --version` と `python --version` を確認。 ",
" - モジュールが古いバージョンで動作しないことも。",
"",
"---",
"",
"### 4. 失敗条件を細かく制御",
"",
"```yaml",
"- name: コマンドを実行",
" command: /usr/bin/xxx",
" register: res",
" failed_when: res.rc != 0 and res.rc != 2 # 2 を許容例",
" changed_when: res.rc == 0",
"```",
"",
"- **`failed_when`** で、ある return code は失敗として扱わないようにできます。 ",
"- **`changed_when`** で、`rc` が 0 の時だけ変更とみなすように。",
"",
"---",
"",
"### 5. 実際のコマンドを手動で試す",
"",
"Ansible のタスクが失敗している場合、**同じホストに SSH でログインし、手動でコマンドを実行**してみてください。 ",
"- 例えば `ssh user@host \"sudo /usr/bin/xxx\"` ",
"- コマンドの戻り値 (`echo $?`) で `rc` を確認。",
"",
"---",
"",
"### 6. 具体的なサンプル",
"",
"#### 例1: `shell` タスクが失敗",
"",
"```yaml",
"- name: /opt/script.sh を実行",
" shell: /opt/script.sh",
" register: sh_res",
" ignore_errors: yes",
"",
"- name: 実行結果を表示",
" debug:",
" var: sh_res",
"```",
"",
"**デバッグ出力例** ",
"```",
"TASK [実行結果を表示] ****************************************",
"ok: [host] => {",
" \"sh_res\": {",
" \"changed\": true,",
" \"cmd\": \"/opt/script.sh\",",
" \"delta\": \"0:00:01.234\",",
" \"end\": \"2025-12-05 12:00:01.234567\",",
" \"rc\": 127,",
" \"stderr\": \"/bin/sh: /opt/script.sh: No such file or directory\",",
" \"stderr_lines\": [",
" \"/bin/sh: /opt/script.sh: No such file or directory\"",
" ],",
" \"stdout\": \"\",",
" \"stdout_lines\": [],",
" \"failed\": true",
" }",
"}",
"```",
"",
"**対策** ",
"- `/opt/script.sh` が存在しているか確認。 ",
"- `chmod +x /opt/script.sh` で実行権限を付与。 ",
"- パスを正しく書く(相対パス vs 絶対パス)。",
"",
"#### 例2: `yum` タスクが失敗",
"",
"```yaml",
"- name: nginx をインストール",
" yum:",
" name: nginx",
" state: present",
" register: yum_res",
"```",
"",
"デバッグで ",
"```",
"\"yum_res\": {",
" \"changed\": true,",
" \"failed\": true,",
" \"rc\": 1,",
" \"msg\": \"Failed to download meta data for repoid=repoid, result: Failed to download metadata for repo 'repoid': Error: Cannot retrieve repository metadata from http://example.com/repos/mirror: HTTP Error 404 - Not Found\"",
"}",
"```",
"",
"**対策** ",
"- `http://example.com/repos/mirror` がアクセス可能か確認。 ",
"- インターネットプロキシ設定が必要か確認。 ",
"- `yum repolist` でリポジトリが有効かチェック。",
"",
"---",
"",
"### 7. よくある「非 0 返却コード」の原因と対策まとめ",
"",
"| 失敗原因 | 主な原因 | 対策 |",
"|-----------|-----------|------|",
"| コマンドが存在しない | パスミス、ファイル未転送 | `shell` → `command` に変更、`path:` オプションでパスを指定 |",
"| 権限不足 | `sudo` が必要 | `become: yes` / `become_user: root` を追加 |",
"| 環境変数不足 | PATH が限定 | `environment:` で追加 |",
"| スクリプトが壊れた | 転送時の文字化け | `script` モジュールで `check` を行う |",
"| ネットワーク / リポジトリ | アクセス不能 | `timeout:` を増やす、プロキシ設定 |",
"| 条件付き失敗 | return code 1 を許容したい | `failed_when:` で明示的に制御 |",
"| 依存関係 | 必要なパッケージが無い | 事前に `apt` / `yum` でインストール |",
"",
"---",
"",
"## 次にやるべきこと",
"",
"1. **`-vvv`** で実行結果を再確認。 ",
"2. `debug` モジュールで `stdout` / `stderr` を確認。 ",
"3. 手動で同じコマンドを実行し、エラー内容を確認。 ",
"4. 必要に応じて `failed_when` / `changed_when` で失敗条件を調整。 ",
"5. 具体的なエラー文(`stderr` 等)が分かれば、さらに詳しくアドバイスできます。",
"",
"---",
"",
"### もしさらに詳しく知りたい場合",
"",
"- **Playbook の該当タスク** ",
"- **実際に返ってきた `rc` と `stderr`** ",
"- **使用している Ansible バージョン** ",
"- **ターゲットホストの OS / バージョン** ",
"",
"これらを教えていただければ、より具体的な対処方法を提案できます。ぜひ情報を共有してください! ",
"",
"---",
"",
"お役に立てれば幸いです。ご不明点があれば遠慮なく質問してくださいね。"
]
}
あとがき
今回は思いつきのAIとAnsibleの活用法だったですが、こんな方法もあるよという方がいらっしゃいましたら是非ご紹介ください!
良いAnsible Lifeを!