TL;DR: クイックフィックス
多くの場合、このエラーは重いJavaアプリケーションやデータベースなどのサービスが、Ansibleのデフォルト設定である30秒よりも初期化に時間がかかるために発生します。解決するには、timeoutを増やし、delayを追加してサービスに余裕を持たせます。
- name: Wait for MySQL to be ready
wait_for:
host: 127.0.0.1
port: 3306
delay: 10 # 最初の10秒間はチェックすら行わない
timeout: 90 # 合計で最大90秒間待機する
state: started
この構成では、Ansibleは10秒間スリープした後、ポートが開くか、90秒のタイマーがゼロになるまでポートをポーリングします。
なぜこのエラーが発生するのか?
エラーログに "elapsed": 30 と表示されている場合、それは wait_for モジュールがTCP接続を正常に確立できないまま制限時間に達したことを意味します。これは必ずしもコードのバグではなく、多くの場合、単なるタイミングの不一致です。
典型的な原因には以下が含まれます:
- データベースのマイグレーション: 新しい MySQL や PostgreSQL のコンテナは、初回起動時にテーブルの構築や500MBのSQLダンプのインポートで忙しい場合があります。
- JVMのウォームアップ: Jenkins や Elasticsearch などのJavaベースのツールは、ポートに完全にバインドされるまで45〜120秒かかることがよくあります。
- リソースの競合: CPUが制限された月額5ドルの小規模なVPSなどでは、ローカルの開発マシンよりもサービスの起動が大幅に遅くなります。
- 「サイレントクラッシュ」: サービスが起動しようとしたものの、設定ファイルのタイポにより失敗して終了した場合です。プロセスが実行されていないため、ポートが開くことはありません。
タイムアウトを解決するための実証済みの戦略
1. 重いワークロードに対してタイムアウト値を増やす
サービスは正常だが起動が遅い場合は、待機時間を延長します。GitLab のような大規模なアプリケーションや、複雑な Kubernetes 管理下のポッドの場合、デフォルトよりも300秒(5分)のタイムアウトを設定する方が安全です。
- name: Wait for slow-starting legacy app
wait_for:
port: 8080
timeout: 300
2. 'delay' を使用してログのノイズを減らす
systemd の起動コマンドの直後にミリ秒単位でポートへの接続試行を繰り返すのは非効率的です。接続リセットエラーでサービスのログが埋まってしまうこともあります。delay: 15 を設定すると、Ansible は最初の試行を行う前に15秒間待機します。
- name: Start app and wait
service:
name: my_custom_app
state: started
- name: Wait for port 8443
wait_for:
port: 8443
delay: 15
timeout: 120
3. 最初にプロセスの状態を確認する
サービス自体がすでにクラッシュしている場合、ポートを待つ必要はありません。サービスの起動タスクの結果を register で取得し、実際に実行され続けているかを確認できます。
- name: Start PostgreSQL
service:
name: postgresql
state: started
register: pg_start_result
- name: Fail fast if service isn't even running
fail:
msg: "PostgreSQLの起動に失敗しました!"
when: pg_start_result.state != 'started'
- name: Wait for Postgres port
wait_for:
port: 5432
timeout: 60
4. ネットワークバインドを確認する
サービスは想定通りの場所でリッスンしていますか?アプリがプライベートIP 10.0.0.5 のみにバインドするように設定されているのに、Ansible のタスクが 127.0.0.1 をチェックしている場合、常にタイムアウトになります。ターゲットサーバーで次のコマンドを実行して、実際にリッスンしているアドレスを確認してください:
ss -tulpn | grep :3306
出力に 0.0.0.0:3306 と表示されていれば、すべてのインターフェースでリッスンしています。特定のIPが表示されている場合は、Ansible の host: パラメータがそのIPと正確に一致していることを確認してください。
修正を確認する方法
プレイブックを -v フラグ付きで実行し、モジュールの内部的なタイミングを確認します。これにより、制限時間にどれくらい近づいているかを把握できます。
ansible-playbook site.yml -v
成功した実行は以下のようになります:
ok: [db_server] => {
"changed": false,
"elapsed": 22,
"port": 3306,
"state": "started"
}
このシナリオでは、"elapsed": 22 はサービスが起動するのに22秒かかったことを示しています。これはデフォルトの30秒に近いため、将来的なディスクI/OやCPU負荷のスパイクに備えて、タイムアウトを60秒に設定するのが適切でしょう。
注意すべき落とし穴
- 外部ファイアウォール: Ansible をコントロールノードから実行し、リモートポートをチェックしている場合は、AWS のセキュリティグループやハードウェアファイアウォールが通信をブロックしていないか確認してください。
- Localhost の曖昧さ: 一部のシステムでは、
localhostが::1(IPv6) に解決されます。サービスが127.0.0.1(IPv4) のみでリッスンしている場合は、ホスト名の代わりに明示的なIPアドレスを使用してください。

