Sửa lỗi Ansible Async Task Timeout: "Timeout waiting for async task" và async_status không trả về kết quả

intermediate🔧 Ansible2026-05-07| Ansible 2.9+, mọi remote host Linux/Unix, các task shell/command/yum/apt chạy lâu

Error Message

FAILED! => {"msg": "Timeout (30s) waiting for async task to finish", "finished": 0, "started": 1}
#async#async_status#timeout#poll#long-running-tasks

Lỗi Gặp Phải

Bạn khởi động một tác vụ chạy lâu với asyncpoll: 0, sau đó dùng async_status để thu thập kết quả. Thay vì thành công, bạn nhận được thông báo này:

FAILED! => {"msg": "Timeout (30s) waiting for async task to finish", "finished": 0, "started": 1}

"finished": 0 là chi tiết mấu chốt — tác vụ vẫn đang chạy khi async_status bỏ cuộc. Nó khởi động bình thường. Chỉ là cần nhiều thời gian hơn bạn đã cấp.

Nguyên Nhân

Với poll: 0, Ansible kích hoạt tác vụ rồi chuyển sang ngay lập tức. Không chờ đợi. Sau đó, async_status kiểm tra kết quả bằng cách lặp với retriesdelay. Timeout xảy ra khi một trong hai trường hợp sau xảy ra:

  • Ngân sách retries × delay cạn kiệt trước khi tác vụ hoàn thành.
  • Bản thân giá trị async quá nhỏ — Ansible sẽ tắt tiến trình từ xa sau số giây đó.

Đây là vòng lặp polling mặc định bạn thấy trong hầu hết các playbook. Tính thử: 10 lần thử × 3 giây = tổng 30 giây. Ổn với một script nhanh, nhưng sẽ không đủ cho việc cài gói, migration cơ sở dữ liệu, hay bất cứ thứ gì thực sự tốn thời gian.

- async_status:
    jid: "{{ async_result.ansible_job_id }}"
  register: job_result
  until: job_result.finished
  retries: 10
  delay: 3   # 10 × 3 = 30 seconds total wait

Cách Khắc Phục Từng Bước

Bước 1 — Đo thời gian thực tế tác vụ của bạn chạy mất bao lâu

SSH vào máy chủ từ xa và đo trực tiếp:

time apt-get install -y some-large-package
# or
time ./long_migration_script.sh

Lấy con số đó và cộng thêm 50–100% dự phòng. Một tác vụ chạy trong 3 phút trên máy nhanh có thể mất 6 phút trên máy chủ quá tải lúc 2 giờ sáng. Hãy tính cho trường hợp xấu nhất.

Bước 2 — Đặt async đủ cao

async là bộ đếm thời gian kill cứng trên máy chủ từ xa. Khi đã hết số giây đó, Ansible sẽ kết thúc tiến trình — dù nó sắp hoàn thành hay chưa. Hãy đặt giá trị này cao hơn rõ rệt so với thời gian chạy xấu nhất của bạn:

- name: Run long database migration
  command: /opt/app/migrate.sh
  async: 600      # allow up to 10 minutes
  poll: 0
  register: migration_job

Bước 3 — Sửa vòng lặp polling của async_status

Tăng retriesdelay sao cho tích của chúng bằng (hoặc vượt) giá trị async của bạn:

- name: Wait for migration to complete
  async_status:
    jid: "{{ migration_job.ansible_job_id }}"
  register: job_result
  until: job_result.finished
  retries: 60     # check up to 60 times
  delay: 10       # every 10 seconds = 600 seconds total

Quy tắc rất đơn giản: retries × delay phải ≥ async. Nếu vòng lặp polling bỏ cuộc trước, bạn sẽ gặp lỗi timeout dù tác vụ vẫn đang chạy bình thường trên máy chủ từ xa.

Ví dụ hoàn chỉnh

---
- hosts: app_servers
  tasks:
    - name: Run package upgrade (long task)
      apt:
        upgrade: dist
      async: 900        # 15-minute hard cap on remote
      poll: 0
      register: apt_job

    - name: Wait for package upgrade
      async_status:
        jid: "{{ apt_job.ansible_job_id }}"
      register: apt_result
      until: apt_result.finished
      retries: 90       # 90 × 10s = 900s — matches async value
      delay: 10

    - name: Show upgrade result
      debug:
        var: apt_result

Bước 4 — Bỏ qua tất cả những bước này nếu bạn không cần kiểu "bắn rồi quên"

Kiểu "bắn rồi quên" với poll: 0 chỉ hữu ích khi bạn cần khởi động nhiều tác vụ chạy lâu song song, hoặc làm việc khác trong khi chờ tác vụ chậm hoàn thành. Với một tác vụ chạy lâu đơn lẻ, chỉ cần bỏ poll: 0 và để Ansible tự xử lý việc polling:

- name: Run long task with built-in polling
  command: /opt/app/migrate.sh
  async: 600
  poll: 15      # Ansible checks every 15 seconds, no async_status needed

Ít thành phần hơn, kết quả như nhau.

Xác Minh Bản Sửa

Chạy với -v để theo dõi quá trình polling theo thời gian thực:

ansible-playbook site.yml -v

Bạn sẽ thấy các dòng như thế này trong khi tác vụ chạy:

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 nghĩa là thành công. Vẫn bị timeout? Hãy tăng retries và chạy lại. Nếu timeout ngay lập tức, hãy kiểm tra lại xem giá trị async của bạn có phải là điểm nghẽn không.

Bạn cũng có thể kiểm tra tác vụ trực tiếp trên máy chủ từ xa — Ansible ghi các file async job tại đây:

ls ~/.ansible_async/
cat ~/.ansible_async/<jid>

Tham Khảo Nhanh: async vs poll vs async_status

  • async: N — Bộ đếm thời gian kill cứng trên máy chủ từ xa. Tiến trình sẽ bị kết thúc sau N giây, dù đã xong hay chưa.
  • poll: 0 — Bắn rồi quên. Bạn chịu trách nhiệm thu thập kết quả bằng async_status.
  • poll: N (N > 0) — Ansible tự động polling mỗi N giây. Không cần async_status.
  • retries × delay — Phải ≥ async, nếu không vòng lặp polling của bạn sẽ hết thời gian trước.

Mẹo Hay

  • Chỉ dùng poll: 0 khi bạn thực sự cần thực thi song song — chạy nhiều tác vụ chậm đồng thời trên nhiều host, hoặc làm việc khác trong khi chờ. Nếu không, polling tích hợp sẽ gọn gàng hơn.
  • Lưu job ID ngay sau khi khởi động tác vụ async. Nếu play bị crash trước khi async_status chạy, bạn vẫn có thể kiểm tra thủ công trên máy chủ từ xa bằng jid từ ~/.ansible_async/.
  • Nhận được "failed": true từ async_status? Đó không phải timeout — tác vụ đã hoàn thành nhưng trả về exit code khác không. Kiểm tra job_result.stderr để tìm lỗi thực sự.
  • Người dùng Ansible Tower / AWX: Tower có cài đặt timeout riêng cho job, tách biệt với giá trị async. Một tác vụ có thể bị Tower kết thúc trước khi bộ đếm async phía từ xa kích hoạt. Hãy đảm bảo cả hai đều được cấu hình với đủ thời gian dự phòng.

Related Error Notes