エラーの内容
async と poll: 0 を使って長時間かかるタスクを起動し、その後 async_status で結果を収集しようとしました。ところが成功ではなく、次のようなエラーが返ってきます:
FAILED! => {"msg": "Timeout (30s) waiting for async task to finish", "finished": 0, "started": 1}
注目すべき点は "finished": 0 です — async_status が諦めた時点で、タスクはまだ実行中でした。タスク自体は正常に開始されています。ただ、指定した時間より多くの時間が必要だったというわけです。
原因
poll: 0 を指定すると、Ansible はタスクを起動してすぐに次の処理へ移ります。待機は一切しません。その後、async_status が retries と delay によるループで結果をポーリングします。次のいずれかが発生するとタイムアウトになります:
- タスクが完了する前に
retries × delayの合計時間を使い切ってしまう。 asyncの値が小さすぎる — Ansible はその秒数が経過するとリモートプロセスを強制終了します。
多くのプレイブックで見かける、デフォルトのポーリングループはこちらです。計算してみましょう:10 回 × 3 秒 = 合計 30 秒。短いスクリプトなら問題ありませんが、パッケージのインストールやデータベースのマイグレーション、時間のかかる処理には対応できません。
- async_status:
jid: "{{ async_result.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 10
delay: 3 # 10 × 3 = 合計 30 秒の待機
手順ごとの修正方法
手順 1 — タスクの実際の所要時間を計測する
リモートホストに SSH 接続して、直接時間を計測します:
time apt-get install -y some-large-package
# または
time ./long_migration_script.sh
その数値に 50〜100% のバッファを加えてください。高速なマシンで 3 分かかるタスクが、深夜 2 時の高負荷サーバーでは 6 分かかることもあります。最悪のケースを想定して余裕を持たせましょう。
手順 2 — async を十分に大きな値に設定する
async はリモートホスト上での強制終了タイマーです。指定した秒数が経過すると、Ansible は完了間近であってもプロセスを終了させます。最悪ケースの実行時間を大きく上回る値を設定してください:
- name: 長時間かかるデータベースマイグレーションを実行
command: /opt/app/migrate.sh
async: 600 # 最大 10 分を許容
poll: 0
register: migration_job
手順 3 — async_status のポーリングループを修正する
retries と delay の積が async の値以上になるよう調整します:
- name: マイグレーションの完了を待つ
async_status:
jid: "{{ migration_job.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 60 # 最大 60 回チェック
delay: 10 # 10 秒ごと = 合計 600 秒
ルールはシンプルです:retries × delay は async 以上でなければなりません。ポーリングループが先にタイムアウトすると、リモートホスト上でタスクが正常に動いていてもタイムアウトエラーが発生します。
完全な動作例
---
- hosts: app_servers
tasks:
- name: パッケージのアップグレードを実行(長時間タスク)
apt:
upgrade: dist
async: 900 # リモートへの 15 分のハードキャップ
poll: 0
register: apt_job
- name: パッケージのアップグレード完了を待つ
async_status:
jid: "{{ apt_job.ansible_job_id }}"
register: apt_result
until: apt_result.finished
retries: 90 # 90 × 10s = 900s — async の値と一致
delay: 10
- name: アップグレード結果を表示
debug:
var: apt_result
手順 4 — ファイア・アンド・フォーゲットが不要な場合はこれをスキップ
poll: 0 によるファイア・アンド・フォーゲットが有用なのは、複数の長時間タスクを並行して起動したい場合や、遅い処理の実行中に別の作業をしたい場合に限られます。単一の長時間タスクであれば、poll: 0 を削除して Ansible 自身にポーリングを任せましょう:
- name: 組み込みポーリングで長時間タスクを実行
command: /opt/app/migrate.sh
async: 600
poll: 15 # Ansible が 15 秒ごとに確認。async_status は不要
構成がシンプルになり、同じ結果が得られます。
修正の確認
-v オプションを付けて実行すると、ポーリングの状況をリアルタイムで確認できます:
ansible-playbook site.yml -v
タスク実行中は次のような行が表示されます:
ASYNC POLL on host1: jid=..., started=1, finished=0
ASYNC POLL on host1: jid=..., started=1, finished=0
ASYNC OK on host1: jid=..., finished=1, rc=0
finished=1, rc=0 は成功を意味します。まだタイムアウトしている場合は retries を増やして再実行してください。即座にタイムアウトする場合は、async の値がボトルネックになっていないか確認しましょう。
リモートホスト上で直接ジョブを確認することもできます — Ansible は非同期ジョブファイルをここに書き込みます:
ls ~/.ansible_async/
cat ~/.ansible_async/<jid>
クイックリファレンス:async vs poll vs async_status
- async: N — リモートホスト上の強制終了タイマー。完了の有無にかかわらず、N 秒後にプロセスが終了します。
- poll: 0 — ファイア・アンド・フォーゲット。
async_statusを使って結果を収集する責任はユーザーにあります。 - poll: N(N > 0) — Ansible が N 秒ごとに自動でポーリングします。
async_statusは不要です。 - retries × delay —
async以上でなければなりません。そうでないとポーリングループが先にタイムアウトします。
Tips
poll: 0を使うのは、複数の遅いタスクをホスト間で並行実行する場合や、待機中に別の作業をする場合など、本当に並行実行が必要な場面だけにしましょう。それ以外では、組み込みポーリングの方がすっきりします。- 非同期タスクを起動した直後にジョブ ID を保存しておきましょう。
async_statusが実行される前にプレイがクラッシュしても、~/.ansible_async/の jid を使ってリモートホスト上でジョブを手動確認できます。 async_statusから"failed": trueが返ってきた場合、タイムアウトではありません — タスクは完了しましたが、終了コードがゼロ以外だったということです。実際のエラーはjob_result.stderrを確認してください。- Ansible Tower / AWX ユーザーへ:Tower には
asyncの値とは別に、Tower 独自のジョブタイムアウト設定があります。リモートのasyncタイマーが発動する前に Tower がタスクを終了させることがあります。両方とも十分な余裕を持って設定してください。

