こういった問題に直面したことはないでしょうか。
Ansibleはタスクを上から順に実行しますが、サービスの起動やポートの開放には数秒から数十秒かかります。
その「待ち時間」を無視して次のタスクへ進むPlaybookは、タイミング依存のエラーを起こしやすいです。
この記事では、Ansibleのuntil・retries・delayを組み合わせたリトライ設計の基本構文から、サービス起動待ち・ポート開放待機・HTTPレスポンス確認の実践パターンまでを解説します。
想定環境はAnsible 2.14以降、マネージドノードはRHEL 9系(Rocky Linux 9含む)・Ubuntu 22.04 LTSです。
この記事のポイント
・until: は「条件が満たされるまで繰り返す」リトライ制御の中心的な構文
・retries で最大試行回数を、delay で試行間の待機秒数を設定する
・サービス起動確認・ポート待機・HTTP確認は command/uri モジュールと組み合わせて設計する
・冪等性を保つには retry 対象を「確認専用のタスク」として切り出す設計が重要
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜAnsibleにリトライ設計が必要なのか
Ansibleのタスクは、デフォルトでは1回だけ実行します。タスクが成功すれば次のタスクへ進み、失敗すれば(failed_whenの設定に応じて)そこでPlaybookが停止します。
この仕組みは大半の場面で適切ですが、以下のようなケースでは1回の実行では不十分なことがあります。
・サービスの起動完了まで待ちたい:systemctlでサービスを起動した直後、プロセスが完全に立ち上がるまで数秒かかります。その状態で続くタスクが接続を試みると、タイミングによっては失敗します
・外部リソースの準備を待ちたい:DNSの伝播・証明書の発行・別サーバーの起動完了など、外部要因で準備が整うまでに時間が必要な場面があります
・一時的なエラーに対応したい:ネットワークの瞬断・パッケージリポジトリの一時障害など、即座にリトライすれば成功するエラーがあります
Ansibleにはこういった「待つ・繰り返す」ニーズに応えるための構文として、until・retries・delayが用意されています。
3つを組み合わせることで、「最大N回・X秒間隔で、Y という条件を満たすまで繰り返す」というリトライを宣言的に書けます。
これはシェルスクリプトでwhileループを手書きするより可読性が高く、べき等性も設計しやすいです。
retries・delay・until句の基本構文
1. 3つのキーワードの役割を整理する
3つのキーワードはそれぞれ次の役割を持ちます。・until:タスクの実行結果が「この条件を満たすまで」繰り返す、という終了条件を書きます
・retries:最大試行回数を整数で指定します(デフォルトは3)。until の条件が満たされなくてもこの回数で打ち切られます
・delay:試行と試行の間に待機する秒数を指定します(デフォルトは5秒)
3つはセットで使います。untilだけ書いてretriesとdelayを省略した場合、デフォルト値(retries:3、delay:5)が使われます。
until の条件が最後の試行でも満たされなかった場合、タスクは失敗として扱われます。
失敗時の挙動はignore_errorsやfailed_whenと組み合わせて制御できます。
2. 基本的な書き方
シンプルな例として、ポートが開くまで待機するタスクを見てみます。# サービスが起動するまで最大5回(10秒間隔)チェックするPlaybook例 - name: Wait until port 8080 is open ansible.builtin.command: bash -c "echo > /dev/tcp/localhost/8080" register: port_check until: port_check.rc == 0 retries: 5 delay: 10 changed_when: false
・bash の /dev/tcp 疑似デバイスを使ってポート 8080 への接続を試みます
・結果を `port_check` という変数に登録します(register: の値は任意の変数名)
・終了コード(rc)が 0 になるまで繰り返します
・最大5回、10秒間隔で試みます
・`changed_when: false` を設定して冪等性を保ちます(確認専用タスクは「変更した」と記録しない)
3. register変数で評価できる値
until: の条件に使えるregister変数のフィールドには次のようなものがあります。・rc:コマンドの終了コード。0が成功、非0が失敗です(commandモジュール使用時)
・stdout:標準出力の文字列(特定の文字列が含まれるか確認するときに使います)
・stdout_lines:標準出力を行ごとにリスト化したもの
・failed:タスクが失敗したかどうかのbool値
・status:HTTPステータスコード(uriモジュール使用時)
uriモジュールは特殊で、rcではなくstatusフィールドにHTTPステータスコードが入ります。
使うモジュールによって評価できるフィールドが異なるため、事前にdebugモジュールで変数の中身を確認しておくと設計しやすいです。
サービス起動待ちの実践パターン
1. systemdサービスの起動確認
MariaDBなどのデータベースサービスをPlaybookで起動した後、起動完了を確認してから次のタスクへ進みたい場面があります。systemctlでサービスを起動するだけでは「プロセスが実際にリクエストを受け付けられる状態」を保証できません。
ActiveState が "active (running)" になっていても、内部の初期化が終わっていない場合があります。
より確実な方法は、実際に接続できるかどうかを確認することです。
# MariaDB を起動してから、実際に接続できるまで待機するPlaybook例 - name: Start MariaDB ansible.builtin.service: name: mariadb state: started enabled: yes - name: Wait until MariaDB accepts connections ansible.builtin.command: mysqladmin ping -u root register: mariadb_ping until: mariadb_ping.rc == 0 retries: 10 delay: 3 changed_when: false
接続できなければ非0で終了するため、until: での判定に使いやすいです。
retriesを10・delayを3に設定すると、最大30秒の待機になります。
サービスの起動にかかる想定時間に合わせてこの値を調整してください。
2. TCPポートの開放待機(wait_forモジュール)
TCPポートが開いているかどうかを確認するパターンには、`ansible.builtin.wait_for` モジュールが便利です。このモジュールはリトライの仕組みを内蔵しており、until・retries・delayを自分で書かずにポート待機を実現できます。
# TCPポートが開くまで最大60秒待機するPlaybook例 - name: Wait for port 3306 to be open ansible.builtin.wait_for: host: localhost port: 3306 timeout: 60 state: started
単純なポート待機であれば、until・retries・delayを自分で書くよりも `wait_for` を使うほうが設計の意図が明確です。
ただし注意点があります。
ポートが開いていても、アプリケーションの内部初期化が終わっていない場合があります。
その場合は次のHTTPレスポンス確認のパターンと組み合わせる設計が有効です。
3. HTTPレスポンス確認(uriモジュール)
WebアプリケーションのヘルスチェックエンドポイントにGETリクエストを送り、200が返るまで待機するパターンです。Ansibleを使った複数台の自動設定を体系的に理解したい場合は、Ansibleの専門学習ページも参考にしてください。
# ヘルスチェックエンドポイントが200を返すまでリトライするPlaybook例 - name: Wait until app health endpoint returns 200 ansible.builtin.uri: url: "http://localhost:8080/health" method: GET status_code: 200 register: health_check until: health_check.status == 200 retries: 12 delay: 5 failed_when: false
`uri` モジュールは200以外のステータスコードを受け取るとデフォルトで失敗として扱い、until に到達する前にタスクが止まります。
`failed_when: false` を設定することで「タスクは実行し続け、until の条件だけで判定する」という挙動に変わります。
retriesを12・delayを5に設定すると、最大60秒の待機です。
アプリケーションの起動に要する時間に合わせて調整してください。
リトライ設計の落とし穴と冪等性の注意点
1. タスクそのものにリトライをかけるときの副作用
リトライ設計を組み込むタスクは「何度実行しても同じ結果になる(冪等な)」タスクであることが前提です。次のようなタスクにリトライをかけると、副作用が生じる可能性があります。
・ファイルの末尾に追記するタスク(失敗のたびに同じ行が追加される)
・外部サービスへデータを送信するタスク(リトライのたびに重複送信される)
・一意制約のあるデータベースにレコードを挿入するタスク
こういった場面では、「実行するタスク」と「確認するタスク」を分けて設計します。
確認専用のタスクにuntil・retries・delayを組み合わせて、実際の操作タスクは別に切り出すのが安全です。
確認専用タスクには `changed_when: false` を必ず設定してください。
これにより、Playbookの実行結果レポートで「changed」が増えず、実際の変更と確認の操作を区別できます。
2. register変数のフィールド名のミスに注意する
リトライ設計でよくある失敗のひとつに、register変数のフィールド名を間違えることがあります。# フィールド名のミス例(return_code は存在しない。正しくは rc) - name: Check service (wrong field name - 失敗例) ansible.builtin.command: systemctl is-active mariadb register: svc_status until: svc_status.return_code == 0 retries: 5 delay: 5 # 正しい書き方 - name: Check service ansible.builtin.command: systemctl is-active mariadb register: svc_status until: svc_status.rc == 0 retries: 5 delay: 5 changed_when: false
うまく動かない場面では、まず `-vvv` オプションを指定してPlaybookを実行し、register変数の中身を確認するのが最初の手順です。
# register変数の中身を確認するタスクを追加してデバッグする例(確認後は削除する) - name: Check service status ansible.builtin.command: systemctl is-active mariadb register: svc_status changed_when: false - name: Debug svc_status variable ansible.builtin.debug: var: svc_status
確認後は `debug` タスクを削除するか、条件(when: 環境変数など)で無効にしてください。
本記事のまとめ
AnsibleのPlaybookで「待つ・繰り返す」が必要な場面に使うuntil・retries・delayについて解説しました。| やりたいこと | 使うキーワード・モジュール |
|---|---|
| サービスの起動完了をコマンドで確認する | register + until: rc == 0 |
| TCPポートが開くまで待機する | ansible.builtin.wait_for |
| HTTPレスポンスが正常になるまで待機する | ansible.builtin.uri + until: status == 200 |
| 最大試行回数・待機間隔を設定する | retries: N / delay: 秒数 |
| 確認専用タスクを「変更なし」として扱う | changed_when: false |
| エラー前にuntilを評価させる | failed_when: false |
確認専用タスクとして切り出し、`changed_when: false` を設定する習慣をつけると、後から見たときに意図が明確なPlaybookになります。
実際の現場ではMariaDB起動待ち・Nginxのポート開放待機・外部APIの応答待ちなど、複数のリトライが組み合わさるPlaybookを設計する場面も出てきます。
それぞれのパターンで「最大何秒待つのか」「失敗したときにPlaybookを止めるのかどうか」を明示的に設計しておくと、夜間バッチや本番デプロイでの予期せぬ停止を防げます。
>>【初心者向けLinuxセミナー(2日間ハンズオン)】
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:Ansibleのpre_tasks・post_tasksで実行順序を設計する方法|Playbookの前処理・後処理とhandlerの実行タイミングを理解する設計入門
- この記事の属するカテゴリ:Ansibleへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます