Error Handling #
Playbook yang hanya berjalan di kondisi sempurna tidak cukup untuk production. Server terkadang tidak responsif, network putus di tengah deployment, atau proses yang dijalankan mengembalikan error yang tidak terduga. Tanpa error handling yang tepat, satu kegagalan bisa membiarkan sistem dalam kondisi setengah jalan — sebagian server sudah diupdate, sebagian belum, dan tidak ada cara yang jelas untuk melanjutkan atau membatalkan. Artikel ini membahas mekanisme error handling Ansible dan pola untuk recovery yang bersih.
block / rescue / always #
Ansible menyediakan konstruksi block yang mirip try-catch-finally di bahasa pemrograman:
- name: Deployment dengan error handling
block:
# Task-task di sini dieksekusi secara normal
- name: Stop aplikasi lama
systemd:
name: myapp
state: stopped
- name: Backup konfigurasi saat ini
copy:
src: /etc/app/app.conf
dest: /etc/app/app.conf.bak
remote_src: true
- name: Deploy konfigurasi baru
template:
src: app.conf.j2
dest: /etc/app/app.conf
notify: Restart myapp
- name: Start aplikasi dengan konfigurasi baru
systemd:
name: myapp
state: started
rescue:
# Dieksekusi HANYA jika ada task di block yang gagal
- name: Kembalikan konfigurasi backup
copy:
src: /etc/app/app.conf.bak
dest: /etc/app/app.conf
remote_src: true
- name: Restart aplikasi dengan konfigurasi lama
systemd:
name: myapp
state: restarted
- name: Catat kegagalan deployment
debug:
msg: "Deployment gagal! Aplikasi dikembalikan ke konfigurasi sebelumnya."
- name: Set flag bahwa rescue dijalankan
set_fact:
deployment_failed: true
always:
# Selalu dieksekusi, baik block berhasil maupun rescue berjalan
- name: Hapus file backup sementara
file:
path: /etc/app/app.conf.bak
state: absent
- name: Log hasil deployment
local_action:
module: lineinfile
path: /var/log/deployments.log
line: >
{{ ansible_date_time.iso8601 }}
{{ inventory_hostname }}
{{ 'FAILED — rollback dilakukan' if deployment_failed | default(false) else 'SUCCESS' }}
create: true
- name: Hentikan playbook jika deployment gagal
fail:
msg: "Deployment gagal di {{ inventory_hostname }}. Lihat log untuk detail."
when: deployment_failed | default(false)
ignore_errors: Lanjutkan Meski Gagal #
Ada situasi di mana kegagalan sebuah task tidak perlu menghentikan seluruh play — misalnya saat menghapus resource yang mungkin sudah tidak ada:
- name: Hentikan service lama jika ada
systemd:
name: old-service
state: stopped
ignore_errors: true # Tidak apa-apa jika service tidak ada
- name: Hapus file lama jika ada
file:
path: /opt/old-app
state: absent
ignore_errors: true
ignore_errors: truebisa menyembunyikan masalah nyata. Gunakan secara selektif dan hanya saat kamu benar-benar tahu bahwa error bisa diabaikan. Lebih baik gunakan kondisiwhenyang tepat ataufailed_whenuntuk kontrol yang lebih presisi.
failed_when: Kontrol Kapan Task Dianggap Gagal #
# Task yang selalu return exit code 0 meski ada error di output
- name: Cek status layanan eksternal
command: curl -s -o /dev/null -w "%{http_code}" https://api.external.com/health
register: health_status
failed_when:
- health_status.stdout != "200"
- health_status.stdout != "204"
changed_when: false
# Abaikan error tertentu, gagalkan yang lain
- name: Jalankan migrasi database
command: python manage.py migrate
register: migration_result
failed_when:
- migration_result.rc != 0
- "'No migrations to apply' not in migration_result.stdout"
Rollback Otomatis saat Deployment Gagal #
Pola rollback yang lebih lengkap untuk deployment aplikasi:
# playbooks/deploy-with-rollback.yml
---
- name: Deploy dengan rollback otomatis
hosts: appservers
vars:
deploy_success: false
pre_tasks:
- name: Catat versi yang sedang berjalan
command: cat /opt/app/VERSION
register: current_version
changed_when: false
ignore_errors: true
- name: Simpan versi saat ini untuk rollback
set_fact:
rollback_version: "{{ current_version.stdout | default('unknown') }}"
tasks:
- block:
- name: Pull kode versi baru
git:
repo: https://github.com/company/app.git
dest: /opt/app
version: "{{ deploy_version }}"
- name: Install dependencies
pip:
requirements: /opt/app/requirements.txt
virtualenv: /opt/app/venv
- name: Jalankan migrasi database
command: /opt/app/venv/bin/python manage.py migrate
args:
chdir: /opt/app
- name: Restart aplikasi
systemd:
name: myapp
state: restarted
- name: Tunggu aplikasi siap
uri:
url: "http://localhost:{{ app_port }}/health"
status_code: 200
register: health_check
until: health_check.status == 200
retries: 12
delay: 5
- name: Tandai deployment berhasil
set_fact:
deploy_success: true
rescue:
- name: Rollback ke versi sebelumnya
git:
repo: https://github.com/company/app.git
dest: /opt/app
version: "{{ rollback_version }}"
when: rollback_version != 'unknown'
- name: Restart aplikasi dengan versi lama
systemd:
name: myapp
state: restarted
when: rollback_version != 'unknown'
- name: Gagalkan playbook setelah rollback
fail:
msg: >
Deployment {{ deploy_version }} gagal.
Rollback ke {{ rollback_version }} sudah dilakukan.
any_errors_fatal vs max_fail_percentage #
Dua cara mengontrol perilaku saat ada host yang gagal:
# any_errors_fatal — hentikan SEMUA host saat satu gagal
- name: Deploy kritis yang harus atomik
hosts: webservers
any_errors_fatal: true
tasks:
- name: Update konfigurasi kritikal
template:
src: critical.conf.j2
dest: /etc/app/critical.conf
# max_fail_percentage — toleransi sejumlah host yang boleh gagal
- name: Update dengan toleransi kegagalan 10%
hosts: webservers
max_fail_percentage: 10 # Hentikan jika lebih dari 10% host gagal
serial: 5 # Update 5 host sekaligus
tasks:
- name: Apply patch
apt:
upgrade: dist
Ringkasan #
block / rescue / alwaysadalah mekanisme try-catch-finally Ansible — gunakan untuk membungkus task yang butuh rollback jika gagal.rescuehanya berjalan saat ada kegagalan diblock;alwaysselalu berjalan — cocok untuk cleanup dan logging.ignore_errors: trueuntuk kegagalan yang bisa diabaikan; lebih baik gunakanfailed_whenuntuk kontrol yang lebih presisi.- Pola deployment yang baik selalu menyimpan versi saat ini sebelum update, sehingga rollback bisa dilakukan ke kondisi yang dikenal baik.
any_errors_fatal: trueuntuk deployment yang harus atomik — satu kegagalan menghentikan semua.max_fail_percentageuntuk toleransi kegagalan yang lebih fleksibel — update bisa lanjut selama persentase kegagalan di bawah threshold.
← Sebelumnya: Scheduled Task Berikutnya: Delegation & Local Action →