Performance

Performance #

Dalam mengelola infrastruktur teknologi berskala besar, performa eksekusi automasi adalah faktor krusial yang menentukan efisiensi kerja tim operasi. Playbook Ansible yang berjalan lambat — misalnya memakan waktu 40 menit hanya untuk melakukan pembaruan konfigurasi minor pada 100 server — akan menghambat pipeline CI/CD, memperpanjang waktu pemulihan insiden (MTTR), dan merusak produktivitas developer karena loop umpan balik (feedback loop) yang terlalu panjang. Banyak developer yang mengira bahwa kelambatan ini adalah sifat bawaan dari Ansible yang berbasis agen-less (agentless). Padahal, sebagian besar playbook dapat dipercepat secara drastis hingga 50% hingga 80% hanya dengan menerapkan konfigurasi eksekusi yang tepat tanpa mengubah logika task kita sama sekali.

Mengoptimalkan Paralelisme dengan forks #

Secara default, Ansible dikonfigurasi dengan nilai forks = 5. Ini berarti Ansible hanya akan memproses task secara paralel pada maksimal 5 server managed node sekaligus. Jika kita memiliki 100 server di dalam inventory kita, Ansible terpaksa membagi eksekusi menjadi 20 gelombang antrian (batch) yang berjalan secara berurutan. Ini adalah penyebab nomor satu mengapa eksekusi playbook terasa sangat lambat di lingkungan multi-server.

Mengubah Konfigurasi Forks #

Kita bisa menaikkan batas konkurensi ini dengan mengubah pengaturan forks di dalam file ansible.cfg kita, atau dengan mengirimkan argumen -f atau --forks secara dinamis saat menjalankan playbook dari terminal cli.

# ansible.cfg
[defaults]
# Mengatur batas eksekusi paralel menjadi 30 host sekaligus
forks = 30

Jika kita ingin mengubahnya hanya untuk sesi eksekusi tertentu, gunakan perintah cli berikut:

# BENAR: Menjalankan playbook dengan konkurensi tinggi via runtime parameter
ansible-playbook -i inventory.ini site.yml -f 50

Menentukan Nilai Forks yang Optimal #

Menentukan nilai forks yang tepat bukanlah tentang mengatur angka sebesar mungkin. Setiap fork yang dibuat oleh Ansible akan melahirkan proses Python baru (child process) di mesin control node kita. Oleh karena itu, konsumsi memori RAM dan penggunaan CPU di control node akan meningkat secara linear seiring bertambahnya jumlah forks.

  • Sizing Rule: Sebagai aturan praktis (rule of thumb), sediakan memori RAM sekitar 50MB hingga 100MB untuk setiap fork, dan pastikan CPU control node memiliki resource core yang cukup. Untuk control node dengan 4 Core CPU dan RAM 8GB, nilai forks antara 25 hingga 40 biasanya sangat aman dan optimal.
  • Network Bandwidth: Perhatikan juga kapasitas bandwidth jaringan dan batas koneksi SSH maksimal (MaxStartups) pada server target agar tidak memicu kegagalan jabat tangan SSH akibat banjir koneksi simultan.

SSH Pipelining untuk Mengurangi Round-trip Koneksi #

Untuk memahami mengapa SSH Pipelining sangat penting, kita perlu melihat bagaimana Ansible mengeksekusi modul Python pada managed node secara default:

  1. Ansible membuat direktori sementara di server target (di bawah folder ~/.ansible/tmp/).
  2. Ansible menyalin kode Python modul kita ke direktori tersebut menggunakan protokol SFTP atau SCP.
  3. Ansible membuka sesi koneksi SSH baru untuk mengeksekusi file Python tersebut dengan hak akses sudo jika dikonfigurasi.
  4. Ansible menghapus kembali file Python sementara tersebut setelah eksekusi selesai.
  5. Ansible membuka sesi SSH baru untuk task berikutnya.

Proses salin-eksekusi-hapus ini menghasilkan overhead jaringan yang sangat besar karena membutuhkan beberapa kali operasi bolak-balik (round-trip) koneksi SSH untuk setiap task tunggal.

Cara Kerja SSH Pipelining #

Dengan mengaktifkan SSH Pipelining (pipelining = true), Ansible tidak akan lagi menyalin file modul secara fisik ke disk server target. Sebagai gantinya, Ansible akan mengirimkan seluruh kode Python modul langsung ke interpreter Python managed node melalui input standar (stdin) dari koneksi SSH yang sedang berjalan. Ini memotong proses pemindahan file dan menghemat banyak waktu eksekusi, terutama pada playbook yang memiliki puluhan task pendek.

Berikut adalah perbandingan skema transmisi data tanpa pipelining vs dengan pipelining:

flowchart TD
    subgraph Default ["Tanpa Pipelining (Banyak Round-Trip)"]
        D1["1. Buat folder temp via SSH"] --> D2["2. Salin zip modul via SFTP"]
        D2 --> D3["3. Eksekusi modul via SSH"]
        D3 --> D4["4. Hapus folder temp via SSH"]
    end

    subgraph Pipeline ["Dengan Pipelining (Satu Koneksi Stdin)"]
        P1["Kirim & Jalankan Modul via Stdin Koneksi SSH Tunggal"]
    end

Mengaktifkan Pipelining di ansible.cfg #

Kita bisa mengaktifkan fitur ini melalui bagian [ssh_connection] di konfigurasi Ansible:

# ansible.cfg
[ssh_connection]
# Mengaktifkan SSH pipelining secara global
pipelining = True

Mengatasi Kendala sudoers requiretty #

Tantangan utama saat mengaktifkan pipelining adalah kompatibilitas dengan konfigurasi keamanan sistem operasi target. Di beberapa distro Linux lama (seperti CentOS/RHEL default), file /etc/sudoers mengaktifkan opsi Defaults requiretty. Opsi ini mewajibkan adanya terminal TTY fisik untuk setiap eksekusi perintah sudo, yang mana hal ini akan memblokir eksekusi modul via stdin tanpa TTY yang digunakan oleh SSH Pipelining.

Jika kita menghadapi error "sudo: a terminal is required to read the password", kita harus menonaktifkan aturan tersebut di server target.

# BENAR: Mempersiapkan server target agar kompatibel dengan SSH Pipelining
- name: Pastikan sudoers tidak membutuhkan TTY fisik
  hosts: all
  become: true
  tasks:
    - name: Nonaktifkan requiretty secara aman di sudoers
      ansible.builtin.lineinfile:
        path: /etc/sudoers
        state: absent
        regexp: '^Defaults\s+requiretty'
        validate: '/usr/sbin/visudo -cf %s'

SSH Multiplexing dengan ControlMaster #

Meskipun pipelining mengurangi transfer file, Ansible secara default tetap harus membuka koneksi SSH baru untuk setiap task. Membuka koneksi SSH baru membutuhkan proses jabat tangan (TCP handshake), negosiasi algoritma enkripsi key exchange, dan proses otentikasi user yang memakan waktu sekitar 0.5 hingga 2 detik per koneksi. Jika playbook kita memiliki 40 task, maka overhead jabat tangan koneksi ini saja bisa menghabiskan waktu hingga 1 menit per server target!

Menggunakan ControlMaster #

Untuk menghilangkan overhead ini, kita bisa menggunakan fitur SSH Multiplexing yang disediakan oleh OpenSSH melalui parameter ControlMaster. Fitur ini memungkinkan beberapa sesi SSH baru ke server target yang sama untuk berbagi (reuse) satu koneksi soket jaringan TCP yang sudah terbuka sebelumnya.

Kita bisa mengonfigurasi multiplexing ini di dalam ansible.cfg di bawah bagian [ssh_connection]:

# ansible.cfg
[ssh_connection]
pipelining = True
# Konfigurasi multiplexing koneksi SSH
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ControlPath=/tmp/ansible-ssh-%h-%p-%r

Penjelasan Parameter SSH: #

  • ControlMaster=auto: Memberi tahu OpenSSH untuk mencoba menggunakan kembali koneksi socket yang sudah ada. Jika socket belum ada, OpenSSH akan membuat koneksi master baru secara otomatis.
  • ControlPersist=60s: Menjaga koneksi socket TCP master tetap aktif di background selama 60 detik setelah sesi task terakhir selesai. Jika ada task baru dalam kurun waktu 60 detik tersebut, koneksi akan langsung tersambung secara instan tanpa proses handshake ulang.
  • ControlPath=/tmp/ansible-ssh-%h-%p-%r: Menentukan lokasi file socket kontrol temporary unik di control node kita yang diidentifikasi berdasarkan host %h, port %p, dan username %r.

Dengan mengaktifkan kombinasi pipelining = True dan ControlMaster, kita bisa memotong waktu eksekusi playbook secara dramatis hingga lebih dari 60%.


Strategi Eksekusi: linear, free, dan Mitogen #

Secara default, Ansible mengeksekusi playbook menggunakan strategi bernama linear. Strategi linear bertindak seperti barikade sinkronisasi: Ansible akan mengeksekusi Task 1 pada seluruh host target, menunggu hingga semua host tersebut selesai memproses Task 1, baru kemudian melangkah bersama-sama ke Task 2.

Kapan Menggunakan Strategy: free? #

Jika kita memiliki satu server yang lambat (misalnya karena jaringan lambat atau spek hardware rendah), server tersebut akan menahan (block) semua server lain yang sudah selesai lebih cepat untuk tidak bisa melanjutkan ke task berikutnya. Untuk menghindari antrean sinkron ini, kita bisa beralih ke strategi free.

Dalam strategi free, setiap server akan mengeksekusi seluruh daftar task secepat mungkin secara independen tanpa memedulikan status penyelesaian server lain.

Berikut perbandingan visual eksekusi strategi Linear vs Free:

flowchart TD
    subgraph Linear ["Strategi Linear (Menunggu Sinkronisasi)"]
        direction TB
        L1_H1["Host 1: Task A (Selesai)"] & L1_H2["Host 2: Task A (Lambat...)"] --> Barrier["Barikade Sinkronisasi (Menunggu Semua)"]
        Barrier --> L2_H1["Host 1: Task B"] & L2_H2["Host 2: Task B"]
    end

    subgraph Free ["Strategi Free (Independen & Cepat)"]
        direction TB
        F1_H1["Host 1: Task A (Selesai)"] --> F2_H1["Host 1: Task B"]
        F1_H2["Host 2: Task A (Lambat...)"] --> F2_H2["Host 2: Task B (Jauh Tertinggal)"]
    end

Kita bisa mendeklarasikan strategi eksekusi ini langsung di tingkat playbook kita:

# BENAR: Menggunakan strategi free untuk pembaruan mandiri yang tidak saling bergantung
- name: Lakukan update package darurat secara cepat
  hosts: web_servers
  strategy: free  # ✓ Setiap host memproses task secepat kemampuannya sendiri
  tasks:
    - name: Jalankan apt-get update
      ansible.builtin.apt:
        update_cache: true

    - name: Install update paket curl
      ansible.builtin.apt:
        name: curl
        state: latest
Jangan gunakan strategi free jika task-task di dalam playbook kita membutuhkan koordinasi multi-host. Misalnya, jika Task 2 adalah migrasi database terpusat yang membutuhkan data dari Task 1 di server web, menggunakan strategi free akan memicu error fatal karena server web bisa langsung lompat ke Task 2 sebelum database siap.

Mitogen untuk Ansible #

Bagi organisasi yang menginginkan performa eksekusi maksimal tanpa mengubah baris kode playbook apa pun, Mitogen for Ansible adalah solusi terbaik. Mitogen adalah plugin pihak ketiga yang menggantikan arsitektur pemanggilan modul Python Ansible bawaan yang berat dengan protokol multiplexing kustom yang jauh lebih efisien.

Mitogen bekerja dengan cara mempertahankan satu interpreter Python yang terus menyala di server target (persistent daemon), lalu mengirimkan instruksi terkompresi lewat satu koneksi pipa (pipe) persisten.

  • Keuntungan: Mengurangi konsumsi CPU di control node hingga 300%, memangkas traffic jaringan hingga 70%, dan mempercepat waktu eksekusi playbook secara keseluruhan antara 1.5x hingga 3x lipat.
  • Instalasi: Unduh library Mitogen, lalu daftarkan path plugin tersebut di dalam file ansible.cfg kita:
# ansible.cfg
[defaults]
# Mengganti strategi eksekusi bawaan dengan plugin Mitogen
strategy_plugins = /path/to/mitogen/ansible_mitogen/plugins/strategy
strategy = mitogen_linear

Optimasi Loop Dinamis dengan loop vs with_items #

Dalam versi Ansible modern (2.5 ke atas), penggunaan konstruksi loop lebih direkomendasikan dibandingkan dengan konstruksi loop gaya lama seperti with_items, with_dict, atau with_subelements. Meskipun with_items secara implisit melakukan perataan list (flattening), ia memiliki overhead kompilasi variabel internal yang lebih lambat dibandingkan dengan loop yang memproses data secara langsung (native list evaluation).

Menghindari Loop Task yang Berulang #

Masalah performa terbesar terkait loop bukanlah tentang memilih antara loop atau with_items, melainkan kecenderungan developer untuk membuat loop task yang memanggil modul sistem berkali-kali untuk operasi yang sebenarnya bisa dikirimkan secara massal (bulk/batch operation).

Mari kita pelajari contoh anti-pattern pemanggilan paket satu per satu vs solusi instalasi massal yang efisien:

# ANTI-PATTERN: Menginstal paket satu per satu di dalam loop.
# Ini memaksa modul apt berjalan sebanyak 4 kali, membuka kunci database apt berulang kali.
- name: Instalasi tools dasar (Lambat)
  ansible.builtin.apt:
    name: "{{ item }}"
    state: present
  with_items:
    - git
    - tmux
    - htop
    - vim
  # JANGAN: Operasi ini memakan waktu 4x lebih lama karena overhead inisialisasi apt berjalan 4x.


# BENAR: Mengirimkan seluruh list langsung ke modul apt sekali jalan.
- name: Instalasi tools dasar secara massal (Cepat)
  ansible.builtin.apt:
    name:
      - git
      - tmux
      - htop
      - vim
    state: present
  # ✓ Modul apt mendeteksi argumen berupa list dan melakukan instalasi dalam satu transaksi tunggal.

Pola pengiriman massal ini juga harus diterapkan pada operasi penyalinan file atau pembuatan konfigurasi dengan memanfaatkan template tunggal atau modul template pintar daripada melakukan loop menyalin file satu per satu.


Menghindari Overhead Menggunakan Fact Caching dan Smart Gathering #

Langkah awal dari setiap eksekusi playbook Ansible adalah mengumpulkan informasi sistem target yang dinamakan “Gathering Facts” menggunakan modul setup. Langkah ini mengumpulkan detail lengkap hardware, alamat IP jaringan, informasi OS, hingga mount disk. Proses ini memakan waktu sekitar 2 hingga 5 detik per host.

Menggunakan gather_facts: false #

Jika playbook kita hanya melakukan tugas-tugas sederhana seperti merestart service atau menarik repositori Git yang tidak menggunakan variabel bawaan Ansible (ansible_*), matikan pengumpulan fakta ini sepenuhnya untuk menghemat waktu secara instan:

# BENAR: Menonaktifkan gathering facts untuk tugas non-conditional
- name: Restart Nginx Cluster
  hosts: web_servers
  gather_facts: false  # ✓ Menghemat 2-5 detik per server secara instan
  tasks:
    - name: Reload konfigurasi nginx
      ansible.builtin.systemd:
        name: nginx
        state: reloaded

Mengaktifkan Fact Caching di ansible.cfg #

Jika playbook kita tetap membutuhkan facts, kita bisa menggunakan teknik Fact Caching. Dengan fact caching, Ansible hanya akan mengambil facts secara langsung dari server target pada eksekusi pertama, lalu menyimpan data tersebut ke sistem penyimpanan lokal (seperti file JSON atau database Redis) selama batas waktu tertentu (TTL). Pada eksekusi berikutnya, Ansible akan langsung membaca data dari cache lokal tanpa perlu melakukan SSH setup ke server target.

Berikut konfigurasi cache berbasis file JSON di ansible.cfg:

# ansible.cfg
[defaults]
# Mengatur pengambilan facts pintar (hanya jika cache kedaluwarsa)
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
# Menyimpan cache facts selama 24 jam (86400 detik)
fact_caching_timeout = 86400

Melakukan Profiling Playbook dengan Callback Plugins #

Kita tidak bisa mengoptimasi performa secara efektif jika kita tidak mengetahui di task mana waktu eksekusi paling banyak terbuang. Untuk menganalisis kinerja playbook kita secara ilmiah, kita harus mengaktifkan callback plugin bawaan Ansible bernama profile_tasks.

Mengaktifkan Plugin Profiling #

Tambahkan konfigurasi berikut ke file ansible.cfg kita untuk mengaktifkan statistik pencatatan waktu:

# ansible.cfg
[defaults]
# Mengaktifkan plugin pencatat waktu tugas dan total waktu play
callbacks_enabled = profile_tasks, timer

Membaca Hasil Profiling #

Setelah plugin diaktifkan, setiap kali kita menjalankan playbook, Ansible akan mencatat durasi waktu untuk setiap task dan menyajikan rangkuman daftar 20 task terlambat di akhir log terminal:

Thursday 17 June 2026  16:20:00 +0700 (0:00:01.050)  0:05:30.120 ****
===============================================================================
ansible.builtin.apt (instalasi paket postgresql) ------------------ 85.20s
ansible.builtin.git (pull source code) -------------------------- 32.40s
ansible.builtin.setup (Gathering Facts) -------------------------- 22.15s
ansible.builtin.template (generate config app) ------------------- 5.12s
...

Dengan menganalisis laporan ini, kita bisa langsung mengidentifikasi bottleneck utama:

  1. Jika Gathering Facts memakan waktu terlalu lama, kita perlu mengaktifkan Fact Caching atau menyetel gathering = smart.
  2. Jika ansible.builtin.apt lambat, kita harus menggabungkan tugas-tugas instalasi menjadi massal (bulk).
  3. Jika modul git lambat, kita perlu memeriksa latensi koneksi internet atau menggunakan parameter depth: 1 untuk melakukan shallow clone yang lebih ringan.

Perbandingan Strategi dan Pengaturan Performa #

Berikut adalah tabel matriks ringkasan optimasi performa yang dapat kita gunakan sebagai acuan cepat dalam memilih teknik yang tepat sesuai skenario infrastruktur kita:

Teknik Optimasi Dampak Performa Tingkat Kesulitan Trade-off / Risiko
Menaikkan forks Tinggi (2x - 5x) Sangat Mudah Mengonsumsi CPU/RAM control node lebih tinggi
SSH Pipelining Tinggi (1.5x - 2x) Mudah Membutuhkan penonaktifan requiretty di target
ControlMaster SSH Sedang (1.3x - 1.5x) Mudah Membutuhkan OpenSSH di control node
Fact Caching Sedang (Menghemat setup) Mudah Data facts bisa kedaluwarsa (out of date)
Free Strategy Sangat Tinggi Sedang Menghilangkan sinkronisasi alur antar-host
Plugin Mitogen Sangat Tinggi (3x - 5x) Sedang Beberapa modul kustom mungkin tidak kompatibel

Ringkasan #

  • forks menentukan jumlah konkurensi eksekusi paralel Ansible; naikkan dari nilai bawaan 5 ke 25-50 sesuai dengan kapasitas CPU dan memori control node kita.
  • SSH Pipelining meminimalkan overhead jaringan dengan cara mentransmisikan kode modul secara langsung ke stdin Python interpreter target tanpa menyalin berkas temporary ke disk.
  • SSH Multiplexing (ControlMaster) mengeliminasi overhead jabat tangan koneksi TCP/SSH berulang dengan cara berbagi satu koneksi socket persisten yang sudah terbuka sebelumnya.
  • Strategi free membebaskan setiap host untuk menyelesaikan task secepat-cepatnya secara mandiri, mengabaikan hambatan antrean server lain yang lambat.
  • Mitogen adalah plugin pihak ketiga yang sangat powerful untuk menggantikan backend pemrosesan Ansible, menawarkan percepatan eksekusi hingga beberapa kali lipat secara instan.
  • Optimasi loop kita dengan cara mengirimkan data secara massal (bulk list) ke modul (seperti modul apt) daripada memanggil modul berulang kali dalam satu perulangan loop.
  • Gunakan Fact Caching (gathering = smart dengan backend jsonfile) untuk menghindari pengumpulan facts berulang pada setiap jalannya playbook.
  • Aktifkan plugin profile_tasks untuk mendapatkan data analitik objektif tentang task mana yang menjadi bottleneck utama dari playbook kita.

← Sebelumnya: Delegation & Local Action   Berikutnya: Testing →

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