「エラーで止まりました。サーバーの状態? 分かりません…」が一番怖い。
こんにちは!「LINUX工房」管理人の「リナックス先生」です。
前回(第6回)は、Ansible Vaultを使って機密情報を安全に管理する方法を学びました。
これでセキュリティもバッチリですね。
さて、自動化を進めていくと、避けて通れないのが「エラー」です。
手動作業なら、エラーが出たらその場で人間が判断して、「一つ前の手順に戻そう」とか「このエラーは無視して進めよう」と対応できます。
しかし、Ansibleは指示された通りにしか動きません。
- 「パッケージのインストール中にネットワークが切れてエラーになった」
- 「設定ファイルを書き換えた後、サービスの再起動に失敗した」
この時、最悪なのは「設定ファイルは新しいのに、サービスは止まったまま」という中途半端な状態で放置されることです。
今回は、こうした事態を防ぐための「エラーハンドリング(例外処理)」と、原因を素早く突き止めるための「デバッグ技術」を学びます。
先生、耳が痛いです…。
この前、100台のサーバーにパッチを当てるPlaybookを流したんですが、50台目でエラーが出て止まっちゃって。
「どこまで終わったの?」「残りのサーバーはどうするの?」って上司に詰められてパニックになりました。
エラーが出ても安全に止まる方法、教えてください!
それは大変だったわね、コウ君。
プロの自動化は「正常に動くこと」だけでなく、「失敗した時にどうリカバリーするか」まで設計するものなの。
Ansibleには、エラー発生時に自動で元に戻す(ロールバック)機能や、実行中に変数を覗き見て修正するデバッガー機能まであるわ。
これらを使いこなせれば、障害対応のスピードが劇的に上がるわよ!
本記事では、堅牢なPlaybookを書くためのエラー制御構文(block/rescue)と、プロが愛用するデバッグオプションを徹底解説します。
🚀 【続・Ansible講座】本連載のカリキュラム
- 【第1回】変数(Variables)とFactsの活用:環境差異を吸収する
- 【第2回】ループと条件分岐 (Loop & When):複雑なロジックを組む
- 【第3回】ハンドラーと通知 (Handlers):無駄な再起動を防ぐ
- 【第4回】テンプレート (Jinja2):設定ファイルを動的に生成する
- 【第5回】ロール (Roles) の設計:Playbookを部品化して再利用する
- 【第6回】Ansible Vault:パスワードなどの機密情報を暗号化する
- 【第7回】エラーハンドリングとデバッグ(今回):止まらない自動化のために
- 【第8回】実践プロジェクト:LAMP環境の完全自動構築と総まとめ
目次
1. デバッグの基本:-v オプションとdebugモジュール
何かおかしいと思った時、まずやるべきことは「情報の可視化」です。
Ansibleには強力なログ出力機能と、変数を表示するモジュールがあります。
1-1. 実行時の詳細出力 (-v オプション)
ansible-playbook コマンドを実行する際、-v (verbose) オプションをつけると、出力される情報の量が増えます。v の数によって詳細度が変わります。
| オプション | 出力内容 | 使いどころ |
|---|---|---|
-v |
タスクの結果(changed/failed)の概要を表示。 | 普段使い。 |
-vv |
タスクの入出力データを表示。 | 変数の値がおかしい時。 |
-vvv |
ターゲットホストとの接続情報などを表示。 | SSHがつながらない時。 |
-vvvv |
接続デバッグの全情報(ユーザー名、パスワード以外)。 | Ansible自体のバグを疑う時(稀)。 |
基本的には -vv をつければ、どの変数がどう展開されたかが見えるので、原因特定に役立ちます。
1-2. 変数の中身を見る (debugモジュール)
プログラミングでいう print() や console.log() にあたるのが debug モジュールです。
「この変数は正しく定義されているか?」「registerで取得した値はどうなっているか?」を確認するのに必須です。
- name: 変数の中身を確認
ansible.builtin.debug:
var: my_variable_name
verbosity: 1 # -v オプションを付けた時だけ表示する設定
verbosity を設定しておくと、普段の実行時には表示されず、デバッグ時(-v)のみ表示されるようになるため、Playbookに埋め込んだままでも邪魔になりません。
2. エラーハンドリングの基礎:ignore_errors
通常、Ansibleはタスクが失敗(赤色)すると、そのホストに対する処理を即座に停止します。
しかし、「失敗してもいいから次に進んでほしい」場合もあります。
例えば、「古い設定ファイルがあれば削除する」タスクなどです。
ファイルがなければエラーになりますが、それは「削除成功」と同じ意味なので、止まってほしくありません。
ignore_errors: yes
- name: 古いログファイルを削除(なければエラーになるが無視)
ansible.builtin.command: rm /var/log/old_app.log
ignore_errors: yes
これを設定すると、エラーが発生しても赤文字で表示された後、...ignoring と表示されて次のタスクに進みます。
⚠️ 注意点
ignore_errors はあくまで「エラーを無視して進む」だけです。
本当にエラーだった場合(権限不足など)も見逃してしまうため、多用は禁物です。
基本的には、後述する failed_when や stat モジュールによる事前チェックを使うほうが安全です。
3. ブロック構造による例外処理:block / rescue / always
プログラミング言語(PythonやJavaなど)には try-catch-finally という構文があります。
「処理を実行し(try)、エラーが起きたら復旧処理をし(catch)、最後に必ず後始末をする(finally)」というものです。
Ansibleでも block / rescue / always を使うことで、これと同じことができます。
これが「ロールバック処理」実装の鍵です。
構成例:アップデート失敗時のロールバック
「アプリケーションを更新してみて、失敗したら旧バージョンに戻す」という処理を書いてみましょう。
tasks:
- name: アプリケーションの更新処理
block:
- name: バージョンアップを実行
ansible.builtin.command: /opt/app/bin/update.sh
- name: 起動確認(ヘルスチェック)
ansible.builtin.uri:
url: http://localhost:8080/health
status_code: 200
rescue:
- name: 【復旧】更新に失敗したため、旧バージョンに戻す
ansible.builtin.command: /opt/app/bin/rollback.sh
- name: 管理者に通知する
ansible.builtin.debug:
msg: "アップデートに失敗しました。ロールバックを実行しました。"
always:
- name: 一時ファイルの削除(成功・失敗に関わらず実行)
ansible.builtin.file:
path: /tmp/update_work_dir
state: absent
解説:
block: メインの処理を記述します。ここでエラーが発生すると、即座にrescueに飛びます。rescue:blockでエラーが起きた時だけ実行されます。ここで環境を元に戻します。always: エラーの有無にかかわらず、最後に必ず実行されます。一時ファイルの掃除などに使います。
これを使えば、「エラーで止まって中途半端な状態」を回避し、常に「正常な状態(旧バージョン)」か「新しい状態(新バージョン)」のどちらかを保つことができます。
スーパーユーザーなら知っておくべきLinuxシステムの仕組み [ Brian Ward ] 価格:3960円 |
4. 「失敗」の定義を変える:failed_when (復習と応用)
第2回でも少し触れましたが、command モジュールなどは終了コード(rc)が0以外だとエラーになります。
しかし、コマンドによっては「特定のメッセージが出たらエラー」と判断したい場合があります。
標準出力の内容でエラー判定する
例えば、「エラー」という文字が含まれていたら失敗扱いにする場合。
- name: ログチェック
ansible.builtin.command: cat /var/log/deploy.log
register: log_result
failed_when: "'ERROR' in log_result.stdout"
複数の条件を組み合わせる
「終了コードが0以外、かつ、”not found” というメッセージが含まれていない場合」のみエラーとする(”not found” は許容する)場合。
- name: ユーザー削除コマンド
ansible.builtin.command: delete_user.sh bob
register: result
failed_when:
- result.rc != 0
- '"user not found" not in result.stderr'
このように failed_when を使いこなすことで、コマンド特有の挙動に合わせた柔軟なエラー制御が可能になります。
5. 「変更」の定義を変える:changed_when (復習と応用)
Ansibleの理想は「冪等性(何度実行しても同じ結果になる)」です。
しかし、command モジュールは実行するたびに必ず changed(黄色)になってしまいます。
これでは「本当に変更があったのか?」が分からず、Handlersの無駄な発火にも繋がります。
実行結果に基づいて changed を判定する
例えば、「設定ファイルを更新するコマンド」を実行した際、標準出力に “Updated” と出た時だけ changed にしたい場合。
- name: 設定更新ツールを実行
ansible.builtin.command: /usr/local/bin/config_updater
register: update_result
changed_when: "'Updated' in update_result.stdout"
こうすることで、何も変更がなかった時は ok(緑色)になり、Handlersも発動しません。
「無駄な再起動を防ぐ」ための重要なテクニックです。
6. 【プロの技】デバッガー (debugger) の活用
これは中級者でも意外と知らない機能です。
Ansibleには、タスクが失敗した瞬間に一時停止し、インタラクティブ(対話的)に調査・修正ができる「デバッガー」が搭載されています。
6-1. デバッガーの有効化
Playbook内のタスクに debugger: on_failed を記述します。
- name: わざと失敗するタスク
ansible.builtin.service:
name: non_existent_service
state: restarted
debugger: on_failed
6-2. デバッグモードでの操作
実行してエラーになると、プロンプトが [target_host] TASK: ... (debug)> に変わります。
ここで以下のコマンドが使えます。
p task.args: 現在のタスクの引数(パラメータ)を表示する。p result: エラー内容を表示する。task.args['name'] = 'httpd': 変数やパラメータをその場で書き換える。r(redo): タスクを再実行する。c(continue): 無視して次に進む。
実際の使用イメージ:
- サービス名が間違っていてエラーで停止した。
- デバッガーで正しいサービス名に書き換える。
task.args['name'] = 'httpd' - 再実行する。
r - 成功して次へ進む!
いちいちPlaybookを修正して最初から流し直す必要がないため、開発中のトライ&エラーが劇的に速くなります。
まとめ:失敗をコントロールせよ
お疲れ様でした!
第7回では、自動化の信頼性を高めるためのエラーハンドリングとデバッグ手法を学びました。
今回の要点まとめ:
-vvオプション とdebugモジュール はトラブルシューティングの基本。ignore_errorsは安易に使わず、リスクを理解して使う。block/rescue/alwaysを使えば、エラー時のロールバック処理が書ける。failed_whenとchanged_whenで、タスクの結果判定を厳密にする。debugger: on_failedを使えば、エラー発生箇所で一時停止して修正・再開ができる。
デバッガー機能、初めて知りました!
エラーが出るたびに修正して、SSH切断して、また最初から実行して…ってやってた時間がもったいないです。
block/rescueも、シェルスクリプトで書くと超大変なのに、Ansibleならこんなにスッキリ書けるんですね。
さて、いよいよ次回は最終回です。
これまで学んだ知識(変数、ループ、ロール、Vault、エラー処理)を総動員して、実用的かつ堅牢な「LAMP環境の完全自動構築」プロジェクトに挑戦します。
次回、【第8回】実践プロジェクト:LAMP環境の完全自動構築と総まとめ で、有終の美を飾りましょう!
▼ 壊しても安心な環境でデバッグしよう ▼
スナップショット機能で即復旧
「おすすめVPS」
運用設計スキルを活かす
「ITエンジニア転職」

コメント