午前2時のパイプライン停止深夜、本番環境のマイクロサービスに重要なホットフィックスをデプロイしているとします。Ansible プレイブックはスムーズに開始されましたが、ヘルスチェック中に突然停止してしまいました。60秒間の沈黙の後、ターミナルに強烈な赤いエラーブロックが表示されます:
FAILED! => {"attempts": 5, "changed": false, "msg": "non-zero return code"}
このエラーは、until キーワードを使用してリソースをポーリングしている際に、Ansible が retries(リトライ)制限に達したときに発生します。基本的には、ループ内のコマンドが失敗し続けたか、タイマーが切れる前に成功条件が満たされなかったことを意味します。
「until」条件が停滞する理由Ansible は、ループの各反復においてモジュールの成功とカスタムの until ロジックの両方を評価します。通常、以下の3つのいずれかが原因です:
- 成功までのタイムラグ: サービスの初期化に90秒かかるのに、ループの待ち時間が50秒しかない場合。タスクが壊れているのではなく、単に待ちきれていない状態です。- 隠れた空白文字:
stdoutで「ready」という単語をチェックしているが、システムが実際には「ready\n」を返している場合。その改行文字1つで文字列比較は失敗します。- シェルの罠:shellモジュールを使用する場合、curl からの 404 エラーなどの 0 以外の終了コードが返されると、たとえ 200 OK を待つ意図があっても、Ansible はその試行を失敗と見なします。## 解決策:よりスマートなポーリングロジック### 1. 一時的なコマンド失敗の処理起動中の API をcurlでチェックする場合、ポートが開放されるまでコマンド自体がエラーコードを返します。コマンドが失敗してもループを継続させる必要があります。untilは本来失敗時にリトライしますが、生の shell コマンドよりもuriモジュールを使用する方がはるかにクリーンです。
- name: Spring Boot API が UP を報告するまで待機
uri:
url: "http://localhost:8080/actuator/health"
status_code: 200
register: api_result
until: api_result.status == 200
retries: 20
delay: 10
# レガシーなシェルスクリプトの場合:
- name: レガシーサービスのステータスを確認
shell: "/opt/bin/check_app_status.sh"
register: shell_out
until: shell_out.rc == 0
retries: 15
delay: 5
failed_when: false # リトライが完了する前にタスクがクラッシュするのを防ぐ
2. 可視性の高いデバッグ条件が正しく見えるのに失敗し続ける場合は、Ansible が実際に何を受け取っているかを確認する必要があります。よくある間違いは、stdout がきれいな文字列であると思い込むことです。trim フィルタや search テストを使用して、余分なスペースやログのタイムスタンプに対してロジックを堅牢にしましょう。
- name: ログで「Database Migration Complete」を確認するまで待機
shell: tail -n 5 /var/log/deploy.log
register: log_check
# 完全一致よりも search を使う方が安全
until: log_check.stdout is search("Complete")
retries: 12
delay: 10
3. ポーリング期間の調整ここでは計算が重要です。重いデータベース移行に5分かかるのに、Ansible タスクで retries: 5 と delay: 10 を設定している場合、50秒の猶予しか与えていないことになります。常に最悪のシナリオを計算し、20% の安全マージンを追加してください。
- name: 重い DB 移行を待機(最大6分)
command: /usr/bin/verify_db_schema.sh
register: db_status
until: db_status.rc == 0
# 36回の試行 * 10秒の遅延 = 360秒
retries: 36
delay: 10
検証:解決策の確認プレイブックを -v フラグ付きで実行し、試行状況をリアルタイムで監視します。プレイブックが停止することなく、リトライカウンターが増えていくのが見えるはずです:
FAILED - RETRYING: Wait for API (20 retries left).
FAILED - RETRYING: Wait for API (19 retries left).
...
changed: [app_server] => {"attempts": 12, "changed": true, "status": 200}
タスクが最終的に緑色(success)になれば、タイミングとロジックが同期しています。36回目の試行に達しても失敗する場合は、Ansible のコードではなく、アプリケーションサービス側に問題があります。

