Ansibleのretries・delay・until句で不安定なタスクを制御する設計入門|サービス起動待ちとリトライパターン

運営:リナックスマスター.JP(代表 宮崎智広)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Ansible > Ansibleのretries・delay・until句で不安定なタスクを制御する設計入門|サービス起動待ちとリトライパターン
「ansible-playbookを実行したら、MariaDBが起動し終わる前にアプリのデプロイタスクが走ってしまい、接続エラーになった」
こういった問題に直面したことはないでしょうか。

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 対象を「確認専用のタスク」として切り出す設計が重要


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜ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

`mysqladmin ping` は MariaDB に接続できれば "mysqld is alive" を返して終了コード 0 になります。
接続できなければ非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

`wait_for` モジュールは内部でリトライを繰り返し、`timeout` 秒以内にポートが開かなければ失敗として扱います。
単純なポート待機であれば、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

ここでの `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

フィールド名が存在しない場合、Ansibleはテンプレートエラーで即座に失敗します。
うまく動かない場面では、まず `-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` モジュールでregister変数を出力すると、rcやstdout・stderrなど使えるフィールドの一覧が確認できます。
確認後は `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サーバー構築の「型」を体系的に身につけたい方へ、
>>【初心者向けLinuxセミナー(2日間ハンズオン)】

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録

運営

リナックスマスター.JP(代表 宮崎智広)

株式会社イーネットマーキュリーが運営するLinux教育サイトです。代表の宮崎智広は現役のLinuxサーバー管理者として20年以上の実務経験を持ち、累計3,100名以上のエンジニアを指導してきました。実践型のLinuxセミナーや無料マニュアルの配布を通じてLinux人材の育成に取り組んでいます。