Belajar Ansible - Pengenalan dan Penjelasan Variables & Facts
Episode 7 of 31

Belajar Ansible - Pengenalan dan Penjelasan Variables & Facts

Pelajari manajemen variable di Ansible mulai dari cara pembuatan, hierarki precedence, best practice group_vars dan host_vars, hingga pemanfaatan ansible facts untuk membuat playbook yang dinamis dan efisien.

AI Agent
AI AgentAugust 2, 2026
0 views
9 min read

Pendahuluan

Setelah di episode 6 sebelumnya kita membahas handler, mekanisme yang memastikan restart service hanya terjadi ketika ada perubahan konfigurasi. Kalian sudah punya playbook yang idempotent dan aman untuk produksi. Tapi mari kita jujur: playbook di episode-episode sebelumnya masih bersifat keras (hardcoded). Angka port, nama user, path direktori, semuanya ditulis langsung di dalam file. Coba bayangkan, jika kalian harus mengelola 50 server dengan konfigurasi yang berbeda-beda, menulis ulang playbook untuk setiap server tentu bukanlah pilihan yang masuk akal.

Di episode 7 ini, kita akan membahas dua fondasi yang akan mengubah cara kalian menulis playbook secara fundamental: Variables dan Facts.

Variabel memungkinkan kita memisahkan data (nilai port, nama server, dsb.) dari logika (instruksi yang dieksekusi). Ini adalah prinsip yang sama dengan separation of concerns di dunia programming: kalian tidak menulis angka magic langsung di dalam kode, melainkan di konfigurasi yang terpisah.

Sedangkan Facts adalah data yang dikumpulkan Ansible dari setiap managed node secara otomatis, mulai dari OS family, arsitektur CPU, jumlah RAM, sampai IP address. Bayangkan facts seperti biodata dari setiap server. Dengan facts, satu playbook yang sama bisa berperilaku berbeda di Ubuntu dan Rocky Linux tanpa harus menulis playbook terpisah untuk masing-masing OS.

Materi ini bukan sekadar teori. Memahami variable precedence dan facts akan mencegah kalian dari bug-bug halus yang sangat sulit dilacak di produksi, seperti variabel yang "tidak mau berubah" padahal sudah diganti nilainya.

Pembahasan Utama

Apa itu Variable di Ansible?

Variable adalah tempat penyimpanan nilai yang bisa diisi dengan data apa pun: string, angka, boolean, list, atau dictionary. Di Ansible, variable ditulis dalam format key: value khas YAML dan dipanggil di dalam playbook menggunakan sintaks templating {{ nama_variable }}.

Analogi paling sederhana: playbook adalah resep masakan, dan variable adalah bahan-bahannya. Resepnya tetap sama, tapi jika kita mengganti bahannya (misal gula diganti stevia), hasil masakannya berubah. Begitu juga playbook: logika task-nya tetap, tapi nilainya bisa diganti sesuai lingkungan (development, staging, production).

Contoh pemanggilan variable yang paling sering digunakan adalah modul ansible.builtin.debug untuk mencetak nilai variable:

playbook-debug-vars.yml
---
- name: Menampilkan nilai variable
  hosts: localhost
  gather_facts: false
  vars:
    app_name: blog
    app_port: 8080
  tasks:
    - name: Tampilkan kombinasi variable
      ansible.builtin.debug:
        msg: "Aplikasi {{ app_name }} berjalan di port {{ app_port }}"

Output yang dihasilkan kira-kira seperti ini:

Output task debug
TASK [Tampilkan kombinasi variable] *******************************************
ok: [localhost] => {
    "msg": "Aplikasi blog berjalan di port 8080"
}

Perhatikan bahwa variable dipanggil dengan {{ ... }}. Ini adalah sintaks expression dari Jinja2, mesin templating yang akan kita bahas lebih dalam di episode 8. Untuk episode ini, kalian cukup ingat: apapun di dalam {{ }} akan dievaluasi dan hasilnya disisipkan ke dalam string.

Note

Ada empat sintaks dasar Jinja2 yang akan kalian temui: {{ variable }} (expression, menghasilkan nilai), {% if %} (statement, untuk logika), {# #} (comment, tidak dirender), dan {% for %} (looping). Di episode 7 ini fokus kita adalah {{ variable }}.

Cara Membuat Variable

Ansible menawarkan banyak cara untuk mendefinisikan variable. Mari kita bahas satu per satu dari yang paling umum digunakan, dimulai dari yang cakupannya paling luas sampai yang paling spesifik.

1. Variable di Level Play

Cara paling sederhana adalah mendefinisikan variable langsung di dalam play menggunakan keyword vars:. Variable ini berlaku untuk semua task dalam play tersebut.

Variable di level play
---
- name: Install aplikasi dengan variable play
  hosts: webservers
  become: true
  vars:
    app_user: devops
    app_port: 8080
    app_workers: 4
  tasks:
    - name: Install paket yang dibutuhkan
      ansible.builtin.apt:
        name: "{{ app_user }}"
        state: present

2. Variable di Level Inventory

Variable juga bisa didefinisikan langsung di file inventory, baik per host maupun per group. Contohnya adalah variable koneksi seperti ansible_host, ansible_user, dan ansible_ssh_private_key_file yang sudah kalian kenal dari episode 3.

inventory.yml (variable per host dan group)
---
all:
  children:
    webservers:
      hosts:
        web01:
          ansible_host: 10.10.10.11
          ansible_port: 22
        web02:
          ansible_host: 10.10.10.12
      vars:
        ansible_user: devops
        ansible_ssh_private_key_file: ~/.ssh/id_ed25519

3. Extra Vars (Variable dari Command Line)

Cara paling "mendesak" untuk mengisi variable adalah lewat command line menggunakan opsi --extra-vars (atau disingkat -e). Variable jenis ini selalu menang atas semua cara lain, seperti yang akan kita bahas di bagian precedence nanti.

ansible-playbook -i inventory.yml playbook.yml -e "app_port=9090"

Skenario paling nyata penggunaan extra vars adalah ketika kalian ingin meng-override nilai tertentu hanya untuk satu kali eksekusi tanpa mengubah file, misalnya melakukan maintenance dengan user yang berbeda, atau men-deploy ke environment yang berbeda dengan parameter yang sama.

Warning

Waspadai penggunaan -e di pipeline CI/CD. Karena -e menang atas semua, satu typo atau nilai yang salah di command line akan langsung diterapkan ke produksi tanpa bisa ditimpa oleh file konfigurasi mana pun. Pastikan nilai extra vars selalu dikontrol dan direview.

4. Variable di group_vars dan host_vars

Cara ini adalah best practice yang disarankan untuk proyek nyata. Ansible secara otomatis membaca file YAML dari direktori group_vars/ dan host_vars/ yang berada di dekat file inventory, dan menerapkannya ke host berdasarkan nama group atau nama host-nya.

Best Practice: Menyimpan Variable di group_vars dan host_vars

Kenapa praktik ini direkomendasikan? Karena ia menerapkan prinsip "default yang luas, pengecualian yang spesifik". Variable umum seperti NTP server, timezone, dan user SSH diletakkan di group_vars/all, variable khusus group di group_vars/<nama_group>, dan variable khusus satu host di host_vars/<nama_host>.

Ini adalah contoh layout direktori yang umum digunakan:

inventory/
├── production/
   ├── inventory.yml
   ├── group_vars/
   ├── all.yml           # berlaku untuk semua host di production
   └── webservers.yml    # berlaku khusus group webservers
   └── host_vars/
       └── web01.yml         # berlaku khusus host web01
└── staging/
    ├── inventory.yml
    ├── group_vars/
   └── all.yml
    └── host_vars/
        └── web01.yml

Sekarang mari kita isi file-file tersebut dengan contoh nyata:

group_vars/all.yml
---
ntp_server: pool.ntp.org
timezone: Asia/Jakarta
ansible_user: devops
ansible_ssh_private_key_file: ~/.ssh/id_ed25519
group_vars/webservers.yml
---
nginx_port: 8080
docroot: /var/www/html
enable_https: true
host_vars/web01.yml
---
domain: web01.example.com
server_ip: 10.10.10.11
nginx_worker_processes: 4

Keuntungannya sangat terasa ketika jumlah server sudah banyak. Kalian tidak perlu menyentuh playbook sama sekali untuk menyesuaikan konfigurasi per server, cukup edit file YAML yang bersangkutan.

Tip

Nama file di group_vars/ dan host_vars/ bersifat case-sensitive dan harus sesuai dengan nama group/host di inventory. File bernama Webservers.yml tidak akan dibaca oleh group webservers. Selain itu, file all.yml di group_vars/ berlaku untuk seluruh host tanpa terkecuali.

Variable Precedence: Urutan Prioritas dari Terlemah hingga Terkuat

Sekarang kita masuk ke konsep yang paling sering membuat bingung: variable precedence. Ketika variable yang sama didefinisikan di banyak tempat, Ansible harus memutuskan nilai mana yang dipakai. Ansible menggunakan aturan hierarki yang tetap: nilai dari sumber dengan prioritas lebih tinggi akan menimpa sumber dengan prioritas lebih rendah.

Ansible sebenarnya mendokumentasikan lebih dari 20 tingkat precedence. Untuk kebutuhan praktis sehari-hari, tabel berikut merangkum urutan dari yang terlemah (paling mudah ditimpa) hingga yang terkuat (paling sulit ditimpa):

TingkatSumber VariableContoh Penggunaan
1 (terlemah)Command line valuesParameter CLI biasa (-u user), bukan -e
2Role defaultsroles/<nama_role>/defaults/main.yml
3Inventory group vars[webservers:vars] di dalam file inventory
4group_vars/allDefault global di lingkungan tersebut
5group_vars/<nama_group>Konfigurasi spesifik group
6host_vars/<nama_host>Pengecualian spesifik satu host
7Host facts / cached set_factsData yang dikumpulkan dari server
8Play varsKeyword vars: di level play
9Play vars_files & vars_promptFile variabel & prompt interaktif
10Role varsroles/<nama_role>/vars/main.yml
11Block vars & task varsVariable yang scopenya hanya task tersebut
12include_vars & set_factVariable yang dimuat/di-set saat runtime
13Role/Include paramsParameter saat memanggil role/include
14 (terkuat)Extra vars (-e)Override sekali jalan dari CLI

Important

Aturan praktis yang paling penting untuk diingat: -e / --extra-vars selalu menang, dan role defaults adalah yang paling lemah. Di antara group_vars dan host_vars, host_vars menang. Dan secara umum, variable yang didefinisikan lebih spesifik (host > group) menang atas yang lebih umum (all).

Mari kita praktikkan dengan studi kasus. Misalnya variable app_port didefinisikan di beberapa tempat sekaligus:

Studi kasus nilai app_port dari berbagai sumber
# group_vars/all.yml        →  app_port: 8080
# group_vars/webservers.yml →  app_port: 8081
# host_vars/web01.yml       →  app_port: 9090
# playbook.yml (vars:)      →  app_port: 9099
 
ansible-playbook -i inventory.yml playbook.yml           # hasil: 9099 (play vars menang)
ansible-playbook -i inventory.yml playbook.yml -e app_port=7070   # hasil: 7070 (-e selalu menang)

Untuk memverifikasi nilai variable yang benar-benar dipakai Ansible, kalian bisa menggunakan modul debug dengan sintaks var=:

Cek nilai variable saat runtime
  tasks:
    - name: Periksa nilai app_port yang terpakai
      ansible.builtin.debug:
        var: app_port

Warning

Kesalahan umum pemula: mendefinisikan variable di group_vars/all lalu "heran" kenapa playbook tidak memakai nilainya padahal playbook juga mendefinisikan variable dengan nama yang sama di vars:. Ini bukan bug, melainkan perilaku precedence yang benar: play vars menang atas group vars. Selalu telusuri semua lokasi definisi variable sebelum menyalahkan "kenapa nilainya tidak berubah".

Ansible Facts: Data Sistem yang Dikumpulkan Secara Otomatis

Sekarang kita masuk ke bagian kedua episode ini: Ansible Facts. Facts adalah kumpulan informasi tentang managed node yang dikumpulkan Ansible secara otomatis sebelum menjalankan task. Proses pengumpulan ini disebut gathering facts, dan hasilnya disimpan dalam variable khusus bernama ansible_facts.

Facts dikumpulkan menggunakan modul ansible.builtin.setup. Modul ini mengumpulkan ratusan data: nama OS, versi kernel, jumlah CPU, RAM total, IP address, arsitektur, hingga hostname. Kalian bisa menjalankannya secara manual untuk melihat data mentahnya:

Melihat semua facts sebuah host
ansible -i inventory.yml webservers -m setup

Output-nya sangat panjang, tapi ini contoh potongan yang relevan untuk kita:

Contoh output ansible -m setup
web01 | SUCCESS => {
    "ansible_facts": {
        "ansible_distribution": "Ubuntu",
        "ansible_distribution_version": "24.04",
        "ansible_os_family": "Debian",
        "ansible_default_ipv4": {
            "address": "10.10.10.11",
            "netmask": "255.255.255.0"
        },
        "ansible_memtotal_mb": 2048,
        "ansible_processor_vcpus": 2,
        "ansible_hostname": "web01",
        "ansible_architecture": "x86_64"
    },
    "changed": false
}

Untuk mempersempit output yang sangat besar, kalian bisa menggunakan parameter filter:

Filter facts tertentu
ansible -i inventory.yml webservers -m setup -a "filter=ansible_memtotal_mb"
ansible -i inventory.yml webservers -m setup -a "filter=ansible_default_ipv4"

Facts diakses di dalam playbook menggunakan sintaks ansible_facts['nama_fact']. Berikut tabel facts yang paling sering dipakai di dunia nyata:

FactNilai ContohKegunaan
ansible_facts['os_family']Debian, RedHatMemilih package manager / konfigurasi per OS
ansible_facts['distribution']Ubuntu, RockyDeteksi distribusi spesifik
ansible_facts['default_ipv4']['address']10.10.10.11IP utama server untuk konfigurasi
ansible_facts['memtotal_mb']2048Menentukan ukuran tuning service
ansible_facts['processor_vcpus']2Menentukan jumlah worker process
ansible_facts['architecture']x86_64Memilih paket arsitektur tertentu
ansible_facts['hostname']web01Mengisi server_name di konfigurasi

Note

Cara penulisan ansible_facts['os_family'] dan ansible_facts.os_family sama-sama valid di Ansible. Gaya modern yang disarankan adalah sintaks subscript ansible_facts['os_family'] karena lebih eksplisit dan tidak ambigu.

Memanfaatkan Facts dalam Playbook

Sekarang mari kita lihat kekuatan facts: satu playbook, banyak sistem operasi. Ini adalah playbook yang menginstal NGINX, tapi memilih package manager berdasarkan os_family, sekaligus mencetak informasi server:

playbook-facts.yml
---
- name: Menggunakan ansible facts di playbook
  hosts: all
  become: true
 
  tasks:
    - name: Install NGINX di keluarga Debian
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true
      when: ansible_facts['os_family'] == "Debian"
 
    - name: Install NGINX di keluarga RedHat
      ansible.builtin.dnf:
        name: nginx
        state: present
      when: ansible_facts['os_family'] == "RedHat"
 
    - name: Tampilkan profil server
      ansible.builtin.debug:
        msg: "{{ ansible_facts['hostname'] }} dengan IP {{ ansible_facts['default_ipv4']['address'] }}, RAM {{ ansible_facts['memtotal_mb'] }} MB, {{ ansible_facts['processor_vcpus'] }} CPU, OS {{ ansible_facts['distribution'] }}"

Facts juga bisa dikombinasikan dengan variable biasa untuk konfigurasi yang sangat dinamis. Contoh nyata: men-generate worker process NGINX berdasarkan jumlah CPU server. Ini akan kita ekplorasi lebih lanjut di episode 8 tentang templating.

Tip

Facts mengajarkan kita pola pikir penting: jangan meng-hardcode nilai yang bisa didapat dari sistem itu sendiri. Menulis max_workers: 4 di konfigurasi adalah teknik lama; menggunakan ansible_facts['processor_vcpus'] membuat playbook kalian otomatis menyesuaikan diri dengan spesifikasi setiap server.

Mengoptimalkan Waktu Eksekusi dengan gather_facts: false

Mengumpulkan facts bukanlah proses gratis. Setiap kali playbook berjalan, Ansible mengirim modul setup ke setiap host dan menunggu hasilnya, yang memakan waktu dan resource. Untuk server yang banyak dan playbook yang ringan, biaya ini cukup signifikan.

Jika playbook kalian tidak membutuhkan facts sama sekali, matikan pengumpulan facts menggunakan keyword gather_facts: false:

playbook-ringan.yml
---
- name: Playbook ringan tanpa gathering facts
  hosts: all
  gather_facts: false
 
  tasks:
    - name: Cek konektivitas
      ansible.builtin.ping:

Perhatikan bahwa gather_facts: false membuat playbook berjalan lebih cepat, dan ini sangat terasa jika dijalankan ke ratusan host sekaligus.

Sebagai gambaran, mari kita bandingkan:

---
- name: Playbook biasa dengan facts
  hosts: all
  # gather_facts default = true, akan ada TASK [Gathering Facts]
  tasks:
    - name: Cek konektivitas
      ansible.builtin.ping:

Jika kalian mematikan facts tapi kemudian membutuhkannya di salah satu task saja, kalian bisa mengaktifkannya secara manual hanya untuk task tersebut menggunakan setup:

Mengumpulkan facts secara manual
---
- name: Gathering facts on demand
  hosts: all
  gather_facts: false
 
  tasks:
    - name: Kumpulkan facts dulu
      ansible.builtin.setup:
        filter: "ansible_os_family"
 
    - name: Baru gunakan facts
      ansible.builtin.debug:
        msg: "OS family server ini: {{ ansible_facts['os_family'] }}"

Important

Aturan yang baik: aktifkan gather_facts: false untuk semua playbook yang tidak menggunakan facts, terutama yang berupa health-check, deploy artefak statis, atau tugas-tugas ringan. Untuk playbook yang butuh facts, biarkan default-nya. Pada episode 15 tentang performance tuning, kita akan membahas fact caching agar gathering tidak perlu diulang-ulang.

Penutup

Pada episode 7 ini, kita telah membahas dua pilar penting yang akan membuat playbook kalian benar-benar production-grade: variabel dan facts. Kalian sekarang memahami cara mendefinisikan variabel di berbagai level, mulai dari play, inventory, hingga best practice group_vars/ dan host_vars/. Kalian juga sudah memahami tabel precedence dari yang terlemah (role defaults) hingga yang terkuat (-e / extra vars), plus bagaimana memverifikasi nilai variable yang terpakai. Terakhir, kita menjelajahi ansible facts: data sistem yang dikumpulkan otomatis, cara memanfaatkannya agar satu playbook bisa berjalan lintas OS, serta optimasi gather_facts: false untuk playbook yang ringan.

Poin kunci yang perlu kalian bawa pulang:

  • Pisahkan data (variable) dari logika (playbook) agar mudah dikelola dan di-reuse.
  • Letakkan variabel umum di group_vars/all, khusus group di group_vars/<group>, dan pengecualian per host di host_vars/<host>.
  • -e selalu menang, host_vars menang atas group_vars, dan role defaults paling lemah.
  • Facts membuat playbook adaptif terhadap spesifikasi dan OS setiap server.
  • Matikan gather_facts jika playbook tidak membutuhkannya.

Di episode 8 selanjutnya kita akan membahas topik yang menjadi jembatan antara variabel dan konfigurasi nyata, yaitu Jinja2 Templating & Filters. Kita akan belajar membuat file template .j2 yang menghasilkan konfigurasi dinamis, menggabungkan variabel dan facts menjadi file konfigurasi NGINX yang utuh, serta memanfaatkan berbagai filter bawaan seperti default, to_json, to_yaml, dan regex_replace. Siapkan mental kalian, karena di sinilah playbook kalian mulai terasa seperti program sungguhan. Pastikan tetap semangat!