Task & Module Execution

Task & Module Execution #

Menulis alur tugas (tasks) di dalam berkas playbook adalah langkah deklaratif awal. Namun, memahami bagaimana Ansible memproses, mentransfer, mengeksekusi, dan membersihkan skrip tugas tersebut di balik layar adalah tingkat keahlian lanjut yang wajib dikuasai oleh pengembang otomatisasi tingkat produksi. Tanpa pemahaman mendalam tentang siklus eksekusi ini, kita akan kesulitan menganalisis kegagalan jaringan di tengah jalan, menelusuri performa eksekusi yang lambat, atau menangani skenario deployment aplikasi multi-host secara atomik. Artikel ini membedah siklus hidup eksekusi modul, membandingkan strategi Linear vs Free, melakukan tuning kapasitas konkurensi (forks), menyimpan variabel luaran (register), memanipulasi status akhir (changed_when dan failed_when), hingga memaksakan proses berhenti secara global (any_errors_fatal).

Siklus Hidup Eksekusi Modul #

Setiap kali kita memicu jalannya satu baris tugas di playbook, Ansible tidak mengirimkan perintah teks mentah melalui terminal SSH. Ansible mengeksekusi rantai proses penyiapan dan transfer berkas yang sangat teratur.

Berikut adalah 7 tahapan siklus hidup eksekusi satu tugas di target server:

  1. Penyusunan Kode Modul (Compilation): Ansible Core di control node mengambil berkas source code modul Python terkait (misalnya apt.py atau template.py) dan menggabungkannya secara dinamis dengan argumen parameter yang kita tulis di task.
  2. Kompresi Payload (Packaging): Hasil penggabungan kode tersebut dikompresi menjadi sebuah paket arsip Python mandiri bertipe Zip.
  3. Transfer Berkas (Transmission): Ansible membuka koneksi SSH dan mentransfer berkas Zip tersebut ke managed node target menggunakan protokol SFTP atau SCP. Secara default, berkas diletakkan di direktori temporer tersembunyi di dalam folder home user login target (misalnya ~/.ansible/tmp/).
  4. Ekstraksi & Eksekusi (Execution): Ansible menginstruksikan target interpreter Python di managed node untuk mengekstrak berkas Zip tersebut ke memori, menjalankannya secara lokal, dan mengumpulkan output eksekusinya.
  5. Pengembalian Laporan (Reporting): Modul mengembalikan laporan hasil kerja dalam format teks berstruktur JSON bersih melalui saluran standar koneksi SSH aktif menuju control node.
  6. Analisis Status (Status Parsing): Ansible di control node mem-parsing respon JSON tersebut untuk menentukan status akhir tugas: hijau (ok), kuning (changed), atau merah (failed).
  7. Pembersihan Sampah (Cleanup): Ansible mengirimkan perintah SSH terakhir untuk menghapus berkas temporer Zip di folder ~/.ansible/tmp/ managed node agar tidak mengotori kapasitas penyimpanan piringan keras server.

Proses penyiapan, transfer, dan eksekusi di atas terjadi secara berulang untuk setiap tugas di setiap host target. Pipelining SSH (jika diaktifkan di ansible.cfg) akan menyatukan langkah 3, 4, dan 5 langsung melalui input standar SSH tanpa transfer file fisik SFTP, mempercepat proses ini hingga 3 kali lipat.


Linear Strategy (Mekanisme Default) #

Secara default, Ansible menerapkan metode penyelarasan tugas bernama Linear Strategy. Di bawah strategi ini, Ansible bertindak sebagai dirigen orkestra yang ketat: tugas pertama harus selesai dieksekusi di seluruh host target yang memenuhi syarat sebelum tugas kedua boleh dimulai.

Ilustrasi linear strategy pada tiga server target:

Task 1: Pasang Nginx
  ├── Eksekusi di web-01.example.com ✓ (Sukses)
  ├── Eksekusi di web-02.example.com ✓ (Sukses)
  └── Eksekusi di web-03.example.com ✓ (Sukses)
       ↓ (Seluruh host selesai memproses Task 1)

Task 2: Salin File Konfigurasi Nginx
  ├── Eksekusi di web-01.example.com ✓ (Sukses)
  ├── Eksekusi di web-02.example.com ✓ (Sukses)
  └── Eksekusi di web-03.example.com ✓ (Sukses)
       ↓ (Seluruh host selesai memproses Task 2)

Task 3: Nyalakan Layanan Nginx
  ├── Eksekusi di web-01.example.com ✓
  ├── Eksekusi di web-02.example.com ✓
  └── Eksekusi di web-03.example.com ✓

Jika salah satu host (misalnya web-02) mengalami kegagalan di Task 1, Ansible secara otomatis akan mengeluarkan web-02 dari antrian eksekusi tugas berikutnya. Namun, host lain (web-01 dan web-03) yang sukses tetap diizinkan melanjutkan ke Task 2. Keunggulan utama strategi linear ini adalah konsistensi: kita dijamin bahwa seluruh server target berada dalam tahapan state yang sama di setiap waktu.


forks dan Paralelisme Batch #

Meskipun menggunakan strategi linear yang mensinkronkan langkah tugas, Ansible tidak memproses server target satu per satu secara bergantian (sequential). Ansible menjalankan tugas tersebut secara paralel pada kelompok server target menggunakan kapasitas soket koneksi yang disebut Forks.

Jumlah default forks di Ansible adalah 5. Parameter ini menentukan batasan ukuran batch paralel eksekusi:

Skenario: forks = 3, target = 6 server

Task 1: Pasang Nginx
  - Batch 1 (Paralel): web-01, web-02, web-03   ← Berjalan bersamaan
  - Batch 2 (Paralel): web-04, web-05, web-06   ← Berjalan setelah Batch 1 selesai

Tuning nilai forks dapat disesuaikan pada berkas ansible.cfg proyek kita:

# File: ansible.cfg
[defaults]
# Tingkatkan forks untuk meningkatkan paralelisme di jaringan besar
forks = 20

Meningkatkan forks akan mempercepat durasi total playbook di ratusan server, tetapi membutuhkan kapasitas CPU dan RAM control node yang lebih besar untuk mengelola proses kompilasi payload paralel.


Free Strategy (Independensi Host) #

Jika kita memiliki skenario di mana tugas-tugas di setiap server sama sekali tidak memiliki ketergantungan satu sama lain, dan kita ingin agar setiap server menyelesaikan seluruh tugas secepat mungkin tanpa harus menunggu server lain yang lambat (misalnya karena latensi jaringan regional berbeda), kita dapat mengaktifkan Free Strategy.

# File: playbooks/deploy-free.yml
---
- name: Deploy Kode Aplikasi Secara Bebas dan Mandiri
  hosts: webservers
  # Mengubah dari Linear (Default) ke Free Strategy
  strategy: free
  
  tasks:
    - name: Tarik repositori Git terbaru
      git:
        repo: https://github.com/org/app.git
        dest: /opt/app

    - name: Jalankan instalasi dependensi npm
      npm:
        path: /opt/app

Di bawah Free Strategy, jalannya tugas tidak disinkronkan. Server yang memiliki koneksi internet lebih cepat akan langsung menyelesaikan Task 1, Task 2, hingga selesai, sementara server yang lambat masih memproses Task 1.

Perbandingan visual antara Linear dan Free strategy digambarkan pada diagram di bawah:

flowchart TD
    subgraph "Linear Strategy (Default - Sinkron)"
        direction TB
        L1["Task 1 (Host A, B, C)"] -->|"Tunggu Semua Host Selesai"| L2["Task 2 (Host A, B, C)"]
        L2 -->|"Tunggu Semua Host Selesai"| L3["Task 3 (Host A, B, C)"]
    end
    subgraph "Free Strategy (Asinkron / Bebas)"
        direction TB
        F1["Host A: Task 1 -> Task 2 -> Task 3"]
        F2["Host B: Task 1 -> Task 2 -> Task 3"]
        F3["Host C: Task 1 -> Task 2 -> Task 3"]
        F1 & F2 & F3 -->|"Eksekusi Independen Secepatnya"| F4["Selesai (Waktu Berbeda-beda)"]
    end

register: Menyimpan State Keluaran #

Setiap kali tugas berjalan, modul Ansible mengembalikan data JSON (seperti output perintah shell, status perubahan file, error message). Kita dapat menangkap dan menyimpan data respon ini ke dalam variabel memori menggunakan kata kunci register agar dapat digunakan oleh tugas berikutnya untuk pencabangan logika.

Struktur objek JSON yang disimpan oleh register umumnya memiliki beberapa properti kunci berikut:

  • changed: Bernilai boolean (true/false), menandakan apakah tugas tersebut berhasil memodifikasi status target server.
  • failed: Bernilai boolean, menandakan apakah tugas mengalami kegagalan eksekusi.
  • rc: Return code (exit status) khusus untuk modul yang menjalankan perintah CLI (seperti command atau shell). Angka 0 menandakan sukses, dan angka selain 0 menandakan kegagalan.
  • stdout: Berupa string teks mentah yang berisi keluaran standar (standard output) dari perintah terminal yang dijalankan.
  • stderr: Berupa string teks mentah yang berisi laporan kesalahan (standard error) jika perintah mengalami masalah.
  • stdout_lines: Hasil pemisahan teks stdout menjadi array baris demi baris, memudahkan kita melakukan iterasi loop Jinja2 di task berikutnya.
  • skipped: Bernilai boolean, menandakan apakah tugas tersebut dilewati karena tidak memenuhi kondisi conditional when.
# File: playbooks/conditional-restart.yml
---
- name: Kelola Layanan Web Berdasarkan Status
  hosts: webservers
  become: true
  
  tasks:
    - name: Ambil status aktual layanan Nginx saat ini
      systemd:
        name: nginx
      register: nginx_runtime_status # Simpan respon JSON ke variabel

    - name: Tampilkan isi variabel hasil register (Debugging)
      debug:
        var: nginx_runtime_status

    - name: Nyalakan kembali Nginx HANYA JIKA statusnya saat ini aktif
      systemd:
        name: nginx
        state: restarted
      # Mengevaluasi properti JSON di dalam variabel register
      when: nginx_runtime_status.status.ActiveState == "active"

changed_when dan failed_when #

Secara default, Ansible menentukan status tugas changed (kuning) jika mendeteksi adanya modifikasi pada managed node, dan failed (merah) jika perintah mengembalikan kode error (exit code non-zero). Namun, ada kasus di mana kriteria default ini menghasilkan laporan yang salah. Kita dapat memanipulasi kriteria tersebut menggunakan parameter kendali.

1. changed_when (Manipulasi Status Perubahan) #

Banyak modul eksekusi perintah (seperti command atau shell) akan selalu mengembalikan status changed = true setiap kali dijalankan karena Ansible tidak tahu apakah perintah shell tersebut memodifikasi sistem atau tidak.

Jika perintah tersebut hanya bertugas membaca informasi (seperti mengecek versi atau status kapasitas disk), kita harus memaksanya bernilai false agar tidak mengotori statistik idempotensi playbook kita:

# Contoh mematikan status changed palsu
- name: Dapatkan versi PostgreSQL terpasang
  command: psql --version
  register: postgres_version
  # ANTI-PATTERN: Membiarkan status changed kuning menyala untuk perintah baca murni
  # (Tanpa changed_when)
  
  # BENAR: Paksa status tetap hijau (ok) karena tidak ada modifikasi sistem
  changed_when: false

Kita juga dapat menggunakan ekspresi kondisional. Tandai changed hanya jika ada baris keluaran tertentu:

- name: Jalankan migrasi database
  command: bundle exec rake db:migrate
  register: db_migrate_output
  # Hanya tandai changed jika output tidak berisi kalimat 'no migrations'
  changed_when: "'No migrations to apply' not in db_migrate_output.stdout"

2. failed_when (Manipulasi Status Kegagalan) #

Kadang-kala sebuah perintah mengembalikan error code non-zero yang sebenarnya merupakan hal normal dan bukan kegagalan sistem. Sebaliknya, adakalanya perintah berjalan sukses (exit code 0) tetapi output-nya berisi teks error kritis. Kita dapat memanipulasi kriteria kegagalan ini:

- name: Memeriksa utilitas kapasitas penyimpanan partisi root
  command: df -h /
  register: root_disk_usage
  # Definisikan kriteria kegagalan sendiri:
  # Gagal jika exit code non-zero ATAU jika kapasitas penggunaan mencapai 95%
  failed_when:
    - root_disk_usage.rc != 0
    - "'95%' in root_disk_usage.stdout"

Pola Penantian State (until, retries, delay) #

Dalam otomatisasi sistem, adakalanya kita harus memicu proses dan menunggu sampai layanan tersebut benar-benar siap menerima koneksi sebelum melanjutkan ke tugas berikutnya (misalnya setelah merestart database server, kita harus menunggu port database 5432 merespon koneksi TCP).

Ansible menyediakan mekanisme perulangan bersyarat menggunakan kombinasi parameter until, retries, dan delay:

# Alur penantian port database aktif
- name: Menunggu database PostgreSQL siap menerima koneksi
  wait_for:
    port: 5432
    state: started
    timeout: 5
  register: port_status
  # Ulangi tugas ini...
  until: port_status is succeeded
  # Coba maksimal 12 kali (Total penantian: 12 * 5s = 60 detik)
  retries: 12
  # Beri jeda 5 detik di antara setiap percobaan kueri port
  delay: 5

any_errors_fatal #

Secara default, jika kita menjalankan playbook di 10 server, dan 2 server mengalami kegagalan eksekusi (failed task), Ansible akan menghentikan eksekusi khusus untuk 2 server tersebut, sedangkan 8 server lainnya tetap melanjutkan tugas berikutnya.

Namun, dalam skenario deployment rilis aplikasi multi-host yang bersifat atomik (seperti di belakang cluster Load Balancer), membiarkan sebagian server memperbarui aplikasi sementara sebagian server gagal dapat memicu kondisi inkonsistensi data yang merusak sistem (split-brain scenario).

Kita dapat mengatur parameter any_errors_fatal: true di level Play untuk memastikan bahwa jika ada satu saja server target yang gagal memproses tugas, seluruh eksekusi playbook untuk seluruh server lainnya akan langsung dihentikan seketika.

# File: playbooks/deploy-atomic.yml
---
- name: Rilis Pembaruan Aplikasi Skala Besar (Atomik)
  hosts: webservers
  become: true
  # Keamanan Kritis: Kegagalan satu server menghentikan seluruh proses secara global
  any_errors_fatal: true

  tasks:
    - name: Download paket rilis aplikasi terbaru
      get_url:
        url: "https://artifacts.unisbadri.com/apps/release-v2.tar.gz"
        dest: /tmp/release-v2.tar.gz

    - name: Ekstrak kode aplikasi ke direktori produksi
      unarchive:
        src: /tmp/release-v2.tar.gz
        dest: /var/www/html/
        remote_src: true

Ringkasan #

  • Siklus Zip Payload — Eksekusi modul Ansible bekerja dengan membungkus skrip Python ke dalam arsip Zip, mengirimkannya via SSH ke folder /tmp, mengekstraknya secara lokal, dan mengembalikan laporan respon JSON sebelum dibersihkan.
  • Konsistensi Linear Strategy — Metode default penjamin sinkronisasi state di mana satu tugas wajib diselesaikan di seluruh host target sebelum melangkah ke tugas berikutnya.
  • Tuning Forks Konkurensi — Sesuaikan nilai forks pada ansible.cfg untuk mempercepat pemrosesan batch paralel pada infrastruktur berskala besar sesuai kapasitas mesin control node.
  • Pemanfaatan Free Strategy — Gunakan strategy: free jika tugas-tugas antar host tidak memiliki ketergantungan urutan kerja demi meminimalkan durasi total eksekusi.
  • Pencabangan Logika via register — Tangkap dan simpan respon eksekusi modul ke dalam variabel register untuk mengevaluasi data properti JSON di dalam tugas kondisional berikutnya.
  • Penegakan Idempotensi — Gunakan changed_when: false untuk mematikan status perubahan palsu pada tugas pembacaan murni agar statistik eksekusi tetap akurat.
  • Kondisi Gagal Kustom — Gunakan failed_when untuk mendefinisikan batas kegagalan operasional sendiri, terlepas dari kode keluaran exit code default modul.
  • Penantian Port database — Kombinasikan parameter until, retries, dan delay untuk memandu proses pooling status kesiapan port layanan target setelah proses restart.
  • Deploy Proteksi Atomik — Aktifkan any_errors_fatal: true pada play deployment rilis aplikasi guna mencegah kondisi sistem inkonsisten akibat kegagalan sebagian server.

← Sebelumnya: Struktur   Berikutnya: Handler →

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