Task & Module Execution

Task & Module Execution #

Menulis task dalam playbook adalah satu hal — memahami bagaimana Ansible benar-benar mengeksekusi task tersebut di balik layar adalah hal lain. Pengetahuan ini sangat berguna saat debugging eksekusi yang lambat, memahami mengapa urutan task penting, atau menjelaskan perilaku yang tidak terduga saat ada task yang gagal. Artikel ini membahas mekanisme eksekusi task dan module dari sudut pandang yang praktis.

Bagaimana Task Dieksekusi: Linear Strategy #

Secara default, Ansible menggunakan linear strategy — setiap task diselesaikan di semua host sebelum task berikutnya dimulai:

Task 1: Install nginx
  ├── Eksekusi di web-01 ✓
  ├── Eksekusi di web-02 ✓
  └── Eksekusi di web-03 ✓
       ↓ (semua host selesai Task 1)

Task 2: Deploy konfigurasi nginx
  ├── Eksekusi di web-01 ✓
  ├── Eksekusi di web-02 ✓
  └── Eksekusi di web-03 ✓
       ↓ (semua host selesai Task 2)

Task 3: Start nginx service
  ├── Eksekusi di web-01 ✓
  ├── Eksekusi di web-02 ✓
  └── Eksekusi di web-03 ✓

Jika satu host gagal di Task 1, host tersebut dikeluarkan dari eksekusi selanjutnya — tapi host lain tetap melanjutkan ke Task 2.


Paralelisme dan Forks #

Meski menggunakan linear strategy, Ansible tidak menunggu satu host selesai sebelum mulai ke host berikutnya dalam task yang sama. Ansible mengelola paralelisme via forks:

Forks = 3, target = 6 host

Task 1: Install nginx
  Batch 1 (paralel): web-01, web-02, web-03  ← dieksekusi bersamaan
  Batch 2 (paralel): web-04, web-05, web-06  ← dieksekusi setelah Batch 1 selesai

Task 2: Deploy config
  Batch 1 (paralel): web-01, web-02, web-03
  Batch 2 (paralel): web-04, web-05, web-06

Nilai forks dikonfigurasi di ansible.cfg:

[defaults]
forks = 10    # Default: 5

Free Strategy #

Sebagai alternatif linear, Ansible menyediakan free strategy di mana setiap host berjalan secepat mungkin tanpa menunggu host lain:

- name: Deploy aplikasi (tiap host secepat mungkin)
  hosts: webservers
  strategy: free          # ← aktifkan free strategy

  tasks:
    - name: Pull kode terbaru
      git:
        repo: https://github.com/org/app.git
        dest: /opt/app
Free strategy — tiap host berjalan independen:

web-01: Task 1 → Task 2 → Task 3 → selesai
web-02:   Task 1 → Task 2 → Task 3 → selesai
web-03:     Task 1 → Task 2 → Task 3 → selesai

(host yang lebih cepat menyelesaikan lebih dulu tanpa menunggu)

Free strategy cocok saat task-task dalam play tidak bergantung satu sama lain dan kamu ingin menyelesaikan eksekusi secepat mungkin. Tapi gunakan dengan hati-hati — tanpa sinkronisasi antar host, sulit memastikan semua host dalam kondisi yang sama di titik waktu tertentu.


Bagaimana Module Dikirim dan Dieksekusi #

Setiap kali task dieksekusi, Ansible melakukan langkah-langkah berikut:

1. Ansible mengambil kode module (misalnya: apt.py)
       │
2. Ansible menggabungkan module dengan argumen yang diberikan
       │
3. Kode Python hasil gabungan dikirim ke managed node via SSH/SFTP
       │
4. Module dieksekusi di managed node menggunakan Python
       │
5. Hasil eksekusi (JSON) dikirim kembali ke control node
       │
6. Ansible mengurai hasil dan menentukan status: ok / changed / failed
       │
7. File sementara di managed node dibersihkan

Seluruh proses ini terjadi untuk setiap task di setiap host. Inilah mengapa mengaktifkan SSH pipelining (di ansible.cfg) bisa mempercepat eksekusi secara signifikan — pipelining menggabungkan beberapa langkah ke dalam satu koneksi SSH.


register: Menyimpan Hasil Task #

Setiap task mengembalikan data — status eksekusi, output, error. Kamu bisa menyimpan data ini menggunakan register dan menggunakannya di task berikutnya:

- name: Cek status service nginx
  systemd:
    name: nginx
  register: nginx_status          # Simpan hasil task ke variabel nginx_status

- name: Debug — tampilkan status nginx
  debug:
    var: nginx_status             # Lihat semua data yang tersimpan

- name: Restart nginx hanya jika sedang berjalan
  systemd:
    name: nginx
    state: restarted
  when: nginx_status.status.ActiveState == "active"

Hasil dari command atau shell module menyimpan output di field stdout:

- name: Dapatkan versi nginx yang terinstal
  command: nginx -v
  register: nginx_version_output
  changed_when: false             # Perintah ini tidak mengubah sistem

- name: Tampilkan versi nginx
  debug:
    msg: "Nginx version: {{ nginx_version_output.stderr }}"
    # nginx -v menulis ke stderr, bukan stdout

changed_when dan failed_when #

Secara default, Ansible menentukan status changed dan failed berdasarkan return code dan output module. Tapi ada kasus di mana kamu perlu override perilaku ini:

# changed_when: false — task ini tidak pernah mengubah sistem
# Berguna untuk task yang hanya membaca atau mengecek kondisi
- name: Cek apakah database sudah diinisialisasi
  command: psql -U postgres -c "SELECT 1"
  register: db_check
  changed_when: false             # Hanya baca, tidak mengubah apapun

# changed_when dengan kondisi — tandai sebagai changed hanya jika kondisi terpenuhi
- name: Jalankan migrasi database
  command: python manage.py migrate
  register: migration_result
  changed_when: "'No migrations to apply' not in migration_result.stdout"

# failed_when — tentukan sendiri kondisi gagal
- name: Cek disk usage
  command: df -h /
  register: disk_usage
  failed_when:
    - disk_usage.rc != 0         # Gagal jika perintah error
    - "'9' in disk_usage.stdout" # Atau jika disk usage di atas 90%

Timeout Task #

Untuk task yang bisa berjalan lama, kamu bisa mengatur timeout:

- name: Tunggu aplikasi siap
  uri:
    url: "http://localhost:8080/health"
    status_code: 200
  register: health_check
  until: health_check.status == 200
  retries: 10                     # Coba maksimal 10 kali
  delay: 5                        # Tunggu 5 detik antar percobaan
  timeout: 10                     # Timeout per request: 10 detik

Pola until + retries + delay sangat berguna untuk menunggu kondisi tertentu — misalnya menunggu service siap setelah restart, atau menunggu database menerima koneksi setelah migrasi.


any_errors_fatal #

Secara default, jika satu host gagal, host lain tetap melanjutkan eksekusi. Untuk situasi di mana kegagalan satu host harus menghentikan seluruh play:

- name: Deploy ke production
  hosts: webservers
  any_errors_fatal: true          # Jika satu host gagal, hentikan semua

  tasks:
    - name: Deploy kode aplikasi
      git:
        repo: https://github.com/org/app.git
        dest: /opt/app

Ini berguna untuk deployment yang harus atomik — jika satu server gagal di-update, lebih baik tidak melanjutkan ke server lain untuk menghindari kondisi di mana sebagian server menjalankan versi lama dan sebagian versi baru.


Ringkasan #

  • Linear strategy (default): setiap task diselesaikan di semua host sebelum task berikutnya dimulai — konsisten tapi lebih lambat dari free strategy.
  • Free strategy: setiap host berjalan secepat mungkin tanpa sinkronisasi — lebih cepat tapi perlu kehati-hatian ekstra.
  • Forks menentukan berapa host dieksekusi secara paralel dalam setiap batch — tingkatkan untuk infrastruktur besar.
  • Gunakan register untuk menyimpan hasil task dan menggunakannya di task berikutnya untuk membuat alur eksekusi yang kondisional.
  • changed_when: false untuk task yang hanya membaca/mengecek kondisi, tidak mengubah sistem.
  • until + retries + delay untuk menunggu kondisi tertentu terpenuhi — sangat berguna saat menunggu service siap.
  • any_errors_fatal: true untuk deployment yang harus atomik — satu kegagalan menghentikan seluruh play.

← Sebelumnya: Struktur   Berikutnya: Handler →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact