Error handling ใน Ansible ช่วยให้ Playbook รับมือกับความล้มเหลวได้อย่างชาญฉลาด แทนที่จะหยุดทันทีเมื่อ task ใดล้มเหลว สามารถกำหนดได้ว่าจะ retry, ข้ามไป, rollback, หรือส่ง notification ก่อนหยุด
บทความนี้อธิบาย block/rescue/always pattern, การใช้ ignore_errors, failed_when, any_errors_fatal, และ max_fail_percentage สำหรับควบคุม error behavior ใน Playbook
block / rescue / always
Pattern block/rescue/always คล้ายกับ try/catch/finally ในภาษา programming ทั่วไป ใช้จัดการ error อย่างมีโครงสร้าง
---
- name: Deploy with error handling
hosts: webservers
tasks:
- name: Deploy application
block:
- name: Pull latest code
git:
repo: https://github.com/myorg/myapp.git
dest: /opt/myapp
version: main
- name: Install dependencies
pip:
requirements: /opt/myapp/requirements.txt
- name: Run migrations
shell:
cmd: python manage.py migrate
chdir: /opt/myapp
- name: Restart service
service:
name: myapp
state: restarted
rescue:
- name: Notify team on failure
debug:
msg: "Deployment failed! Rolling back..."
- name: Rollback to previous version
shell:
cmd: /opt/scripts/rollback.sh
chdir: /opt/myapp
always:
- name: Log deployment result
lineinfile:
path: /var/log/deploy.log
line: "Deploy attempt at {{ ansible_date_time.iso8601 }}"
create: yes
Tasks ใน rescue รันเมื่อ task ใดใน block ล้มเหลว ส่วน always รันเสมอไม่ว่า block จะสำเร็จหรือล้มเหลว เหมาะสำหรับ cleanup และ logging
ignore_errors: ข้ามผ่าน Error
ignore_errors: yes บอกให้ Playbook ดำเนินต่อแม้ task นั้นจะล้มเหลว ใช้เมื่อ task นั้นไม่ใช่ requirement บังคับ
---
- name: Cleanup old files
hosts: all
tasks:
- name: Remove old log files (may not exist)
file:
path: /var/log/oldapp.log
state: absent
ignore_errors: yes
- name: Stop old service (may not be running)
service:
name: oldservice
state: stopped
ignore_errors: yes
- name: Continue with new installation
package:
name: newapp
state: present
failed_when: กำหนดเงื่อนไข Failure
failed_when กำหนด condition เองว่า task ถือว่า fail เมื่อไร ใช้เมื่อ command return exit code 0 แต่ output บอกว่ามีปัญหา หรือกลับกัน
---
- name: Check and validate
hosts: all
tasks:
- name: Run health check
shell:
cmd: curl -s http://localhost/health
register: health_check
failed_when:
- health_check.rc != 0
- "'OK' not in health_check.stdout"
- name: Run database backup
shell:
cmd: mysqldump mydb > /backup/mydb.sql
register: backup_result
failed_when: backup_result.rc != 0 and 'No tables' not in backup_result.stderr
- name: Check disk space
shell:
cmd: df -h / | awk 'NR==2{print $5}' | tr -d '%'
register: disk_usage
failed_when: disk_usage.stdout | int > 90
changed_when: ควบคุม Changed Status
changed_when กำหนด condition ว่า task ถือว่า “changed” เมื่อไร ช่วยให้ handler ทำงานอย่างถูกต้องและ idempotency report แม่นยำขึ้น
---
- name: Manage configuration
hosts: all
tasks:
- name: Apply config changes
shell:
cmd: /opt/scripts/apply-config.sh
register: config_result
changed_when: "'Applied' in config_result.stdout"
# task ถือว่า changed เฉพาะเมื่อ output มีคำว่า 'Applied'
- name: Run database vacuum
shell:
cmd: psql -c "VACUUM ANALYZE;"
changed_when: false
# task นี้ไม่เคย "changed" — เป็น read/maintenance operation
any_errors_fatal: หยุดทันทีเมื่อมี Error
โดย default Ansible รัน task บน host อื่นต่อไปแม้บาง host จะล้มเหลว ใช้ any_errors_fatal: true เพื่อหยุดทุก host ทันทีเมื่อ host ใดล้มเหลว
---
- name: Critical deployment
hosts: all
any_errors_fatal: true # หยุดทุก host ทันทีถ้า host ใดล้มเหลว
tasks:
- name: Run pre-deployment check
shell:
cmd: /opt/scripts/preflight-check.sh
register: preflight
- name: Fail if preflight fails
fail:
msg: "Pre-flight check failed on {{ inventory_hostname }}"
when: preflight.rc != 0
- name: Deploy application
shell:
cmd: /opt/scripts/deploy.sh
max_fail_percentage: ยอมรับ Failure บางส่วน
max_fail_percentage กำหนดเปอร์เซ็นต์ host ที่ยอมให้ล้มเหลวได้ก่อนที่ Playbook จะหยุด เหมาะสำหรับ rolling deployment
---
- name: Rolling deployment
hosts: webservers
serial: 2 # deploy ทีละ 2 เครื่อง
max_fail_percentage: 20 # หยุดถ้ามากกว่า 20% ของ host ล้มเหลว
tasks:
- name: Remove from load balancer
shell:
cmd: /opt/lb/remove.sh {{ inventory_hostname }}
- name: Deploy new version
shell:
cmd: /opt/scripts/deploy.sh v2.0
- name: Run smoke tests
shell:
cmd: /opt/scripts/smoke-test.sh
register: smoke_test
failed_when: smoke_test.rc != 0
- name: Add back to load balancer
shell:
cmd: /opt/lb/add.sh {{ inventory_hostname }}
when: smoke_test.rc == 0
สรุป
Error handling ทำให้ Playbook มีความ robust และเชื่อถือได้ใน production Pattern ที่ควรจำ: ใช้ block/rescue/always สำหรับ deployment ที่ต้องการ rollback, ใช้ failed_when เมื่อ exit code ไม่สะท้อนความสำเร็จจริง, และใช้ any_errors_fatal สำหรับ task ที่ต้องการให้ทุก host พร้อมกัน
ignore_errors ควรใช้อย่างระมัดระวัง เพราะอาจซ่อนปัญหาจริงที่ต้องแก้ไข ให้ใช้เฉพาะกรณีที่ failure นั้นคาดได้และไม่กระทบต่อ task ถัดไปจริง ·

