Sự cố Pipeline lúc 2 giờ sángĐã khuya, và bạn đang đẩy một bản sửa lỗi khẩn cấp (hotfix) quan trọng lên một microservice môi trường production. Playbook Ansible bắt đầu mượt mà nhưng đột nhiên bị treo trong quá trình kiểm tra sức khỏe (health check). Sau 60 giây im lặng đáng sợ, terminal của bạn bùng nổ với một khối thông báo lỗi màu đỏ ANSI:
FAILED! => {"attempts": 5, "changed": false, "msg": "non-zero return code"}
Lỗi này xảy ra khi bạn sử dụng từ khóa until để thăm dò (poll) một tài nguyên và Ansible đạt đến giới hạn retries. Về cơ bản, lệnh bên trong vòng lặp của bạn liên tục thất bại, hoặc điều kiện thành công của bạn không bao giờ trở thành đúng trước khi hết thời gian chờ.
Tại sao điều kiện 'until' của bạn bị đình trệAnsible đánh giá cả sự thành công của module và logic until tùy chỉnh của bạn trong mỗi lần lặp. Thông thường, một trong ba vấn đề sau đang xảy ra:
- Khoảng cách thành công (The Success Gap): Dịch vụ của bạn mất 90 giây để khởi tạo, nhưng vòng lặp của bạn chỉ đợi trong 50 giây. Tác vụ không bị hỏng; nó chỉ là không đủ kiên nhẫn.- Khoảng trắng ẩn: Bạn đang kiểm tra
stdoutcho từ "ready", nhưng hệ thống thực tế lại trả về "ready\n". Ký tự xuống dòng đơn lẻ đó sẽ làm hỏng việc so sánh chuỗi mọi lúc.- Bẫy Shell: Khi sử dụng moduleshell, bất kỳ mã thoát khác không (non-zero exit code) nào (như lỗi 404 từ curl) đều khiến Ansible coi lần thử đó là thất bại, ngay cả khi bạn có ý định đợi mã 200 OK.## Giải pháp: Logic thăm dò thông minh hơn### 1. Xử lý các lỗi lệnh tạm thờiNếu bạn sử dụngcurlđể kiểm tra một API đang khởi động, bản thân lệnh đó sẽ trả về mã lỗi cho đến khi cổng được mở. Bạn phải đảm bảo vòng lặp tiếp tục ngay cả khi lệnh thất bại. Mặc dùuntiltự động thử lại khi thất bại, việc sử dụng moduleurisẽ sạch sẽ hơn nhiều so với các lệnh shell thô.
- name: Đợi API Spring Boot báo cáo trạng thái UP
uri:
url: "http://localhost:8080/actuator/health"
status_code: 200
register: api_result
until: api_result.status == 200
retries: 20
delay: 10
# Dành cho các script shell cũ:
- name: Kiểm tra trạng thái dịch vụ cũ
shell: "/opt/bin/check_app_status.sh"
register: shell_out
until: shell_out.rc == 0
retries: 15
delay: 5
failed_when: false # Ngăn tác vụ bị dừng đột ngột trước khi kết thúc các lượt thử lại
2. Debug với khả năng hiển thị caoNếu điều kiện của bạn có vẻ đúng nhưng vẫn thất bại, bạn cần xem chính xác những gì Ansible thấy. Một sai lầm phổ biến là giả định stdout là một chuỗi sạch. Sử dụng bộ lọc trim hoặc kiểm tra search để làm cho logic của bạn linh hoạt hơn trước các khoảng trắng thừa hoặc dấu thời gian trong log.
- name: Đợi log xác nhận 'Database Migration Complete'
shell: tail -n 5 /var/log/deploy.log
register: log_check
# Sử dụng search an toàn hơn so với so khớp chuỗi chính xác
until: log_check.stdout is search("Complete")
retries: 12
delay: 10
3. Hiệu chỉnh khoảng thời gian thăm dò (Polling Window)Phép toán rất quan trọng ở đây. Nếu một quá trình di chuyển cơ sở dữ liệu nặng mất 5 phút để chạy, nhưng tác vụ Ansible của bạn sử dụng retries: 5 và delay: 10, bạn chỉ mới cho phép 50 giây đệm. Luôn tính toán kịch bản tệ nhất và thêm 20% biên độ an toàn.
- name: Đợi di chuyển DB nặng (Tối đa 6 phút)
command: /usr/bin/verify_db_schema.sh
register: db_status
until: db_status.rc == 0
# 36 lần thử * 10 giây trễ = 360 giây
retries: 36
delay: 10
Xác minh: Xác nhận giải phápChạy playbook của bạn với cờ -v để theo dõi các lần thử trong thời gian thực. Bạn sẽ thấy bộ đếm thử lại tăng dần mà playbook không dừng lại:
FAILED - RETRYING: Wait for API (20 retries left).
FAILED - RETRYING: Wait for API (19 retries left).
...
changed: [app_server] => {"attempts": 12, "changed": true, "status": 200}
Nếu tác vụ cuối cùng chuyển sang màu xanh, thời gian và logic của bạn đã đồng bộ. Nếu nó chạm đến lần thử thứ 36 và vẫn thất bại, vấn đề nằm ở dịch vụ ứng dụng của bạn, không phải ở mã Ansible.

