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.

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.
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:
---
- 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:
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 }}.
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.
Cara paling sederhana adalah mendefinisikan variable langsung di dalam play menggunakan keyword vars:. Variable ini berlaku untuk semua task dalam play tersebut.
---
- 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: presentVariable 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.
---
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_ed25519Cara 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.
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.
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.ymlSekarang mari kita isi file-file tersebut dengan contoh nyata:
---
ntp_server: pool.ntp.org
timezone: Asia/Jakarta
ansible_user: devops
ansible_ssh_private_key_file: ~/.ssh/id_ed25519---
nginx_port: 8080
docroot: /var/www/html
enable_https: true---
domain: web01.example.com
server_ip: 10.10.10.11
nginx_worker_processes: 4Keuntungannya 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.
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):
| Tingkat | Sumber Variable | Contoh Penggunaan |
|---|---|---|
| 1 (terlemah) | Command line values | Parameter CLI biasa (-u user), bukan -e |
| 2 | Role defaults | roles/<nama_role>/defaults/main.yml |
| 3 | Inventory group vars | [webservers:vars] di dalam file inventory |
| 4 | group_vars/all | Default global di lingkungan tersebut |
| 5 | group_vars/<nama_group> | Konfigurasi spesifik group |
| 6 | host_vars/<nama_host> | Pengecualian spesifik satu host |
| 7 | Host facts / cached set_facts | Data yang dikumpulkan dari server |
| 8 | Play vars | Keyword vars: di level play |
| 9 | Play vars_files & vars_prompt | File variabel & prompt interaktif |
| 10 | Role vars | roles/<nama_role>/vars/main.yml |
| 11 | Block vars & task vars | Variable yang scopenya hanya task tersebut |
| 12 | include_vars & set_fact | Variable yang dimuat/di-set saat runtime |
| 13 | Role/Include params | Parameter 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:
# 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=:
tasks:
- name: Periksa nilai app_port yang terpakai
ansible.builtin.debug:
var: app_portWarning
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".
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:
ansible -i inventory.yml webservers -m setupOutput-nya sangat panjang, tapi ini contoh potongan yang relevan untuk kita:
Untuk mempersempit output yang sangat besar, kalian bisa menggunakan parameter filter:
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:
| Fact | Nilai Contoh | Kegunaan |
|---|---|---|
ansible_facts['os_family'] | Debian, RedHat | Memilih package manager / konfigurasi per OS |
ansible_facts['distribution'] | Ubuntu, Rocky | Deteksi distribusi spesifik |
ansible_facts['default_ipv4']['address'] | 10.10.10.11 | IP utama server untuk konfigurasi |
ansible_facts['memtotal_mb'] | 2048 | Menentukan ukuran tuning service |
ansible_facts['processor_vcpus'] | 2 | Menentukan jumlah worker process |
ansible_facts['architecture'] | x86_64 | Memilih paket arsitektur tertentu |
ansible_facts['hostname'] | web01 | Mengisi 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.
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:
---
- 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.
gather_facts: falseMengumpulkan 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:
---
- 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:
---
- 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.
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:
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.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!