Menulis playbook Ansible pertama untuk instalasi dan konfigurasi NGINX, memahami struktur play, privilege escalation, CLI flags penting, serta mekanisme idempotency di balik status OK, CHANGED, dan FAILED.

Setelah di episode 4 sebelumnya kita membahas ad-hoc commands untuk operasional harian — cara cepat menjalankan modul ke banyak server dari satu baris perintah — pada episode kali ini kita akan menaikkan level: menulis playbook pertama dan memahami struktur penulisannya secara mendalam.
Ad-hoc sangat berguna, tetapi punya batasan jelas: perintah yang kalian ketik tidak tersimpan, tidak mudah dibagikan, dan bisa saja dijalankan berulang dengan efek yang tidak terkendali. Playbook mengubah semua itu. Dengan playbook, automasi kalian menjadi dokumen yang hidup — bisa di-commit ke Git, di-review rekan tim, dijalankan berulang-ulang secara aman, dan menjadi dasar untuk konsep-konsep lanjutan seperti handler, roles, hingga collection.
Di episode ini, kalian akan membuat playbook nyata: menginstal dan mengonfigurasi web server NGINX. Bukan sekadar menyalin kode, tapi memahami setiap barisnya — kenapa ada become, kenapa urutan task penting, dan bagaimana Ansible tahu bahwa sistem sudah berada di keadaan yang diinginkan (idempotency).
Bayangkan perbedaan antara dua cara berkomunikasi:
Di tim infrastruktur yang sehat, hampir semua perubahan produksi — sepele sekalipun — harus terdokumentasi dan bisa diaudit. Playbook menjawab kebutuhan itu. Keunggulan utamanya:
Note
Ad-hoc dan playbook bukan musuh, melainkan alat untuk situasi berbeda. Ad-hoc untuk eksplorasi cepat dan tanggap darurat; playbook untuk perubahan yang perlu dilacak dan diulang.
Playbook ditulis dalam YAML, dan pada dasarnya adalah sebuah daftar (list) dari satu atau lebih plays. Setiap play adalah "pertunjukan" yang menargetkan sekelompok host dengan serangkaian task.
Struktur skeleton sebuah playbook:
---
- name: Judul singkat play
hosts: webservers
become: true
tasks:
- name: Deskripsi task pertama
ansible.builtin.ping:
- name: Deskripsi task kedua
ansible.builtin.file:
path: /tmp/contoh
state: touchMari kita bedah setiap key utama:
| Key | Fungsi | Catatan |
|---|---|---|
name | Label play atau task | Wajib untuk kemudahan membaca log; task tanpa name sulit ditelusuri |
hosts | Host pattern target play | Bisa all, nama grup, atau kombinasi pattern episode 3 |
become | Mengaktifkan privilege escalation (sudo) | Nilai true/false |
become_user | User yang dipakai setelah escalate | Default root jika tidak ditulis |
become_method | Metode escalate | sudo (default), su, runas, dan lainnya |
tasks | Daftar task yang dieksekusi secara berurutan | Urutan penting — task atas selesai dulu baru task bawah |
vars | Variabel yang berlaku untuk seluruh play | Akan dibahas dalam di episode 7 |
becomeKebanyakan task administrasi — install package, mengubah file di /etc, restart service — membutuhkan hak root. Ansible menangani ini lewat mekanisme become:
- name: Install & konfigurasi nginx
hosts: webservers
become: true
become_user: root
become_method: sudo
tasks:
...Tip
become_user: root adalah default, jadi kalian tidak wajib menuliskannya. Tapi menulis become_method: sudo secara eksplisit membantu orang yang membaca playbook memahami konteks lingkungan kalian. Untuk pengguna SSH dengan password sudo, tambahkan flag -K saat menjalankan ansible-playbook agar diminta password.
Mekanisme become mirip sudo su di shell — task dijalankan sebagai become_user, sementara koneksi SSH tetap memakai user dari inventory. Ini adalah pembatas keamanan yang sehat: user SSH tidak perlu root, cukup punya hak sudo.
Sebagai gambaran, beginilah diff-nya saat kita menambahkan privilege escalation ke play yang sebelumnya belum punya become:
- name: Setup NGINX web server
hosts: webservers
become: true
become_user: root
tasks:
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: presentSekarang saatnya praktik. Kita akan menulis playbook yang:
---
- name: Setup NGINX web server
hosts: webservers
become: true
tasks:
- name: Update apt package cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
- name: Deploy custom index page
ansible.builtin.copy:
content: |
<!DOCTYPE html>
<html>
<body>
<h1>Hello dari Ansible!</h1>
</body>
</html>
dest: /var/www/html/index.html
owner: root
group: root
mode: "0644"
- name: Ensure nginx is started and enabled
ansible.builtin.systemd_service:
name: nginx
state: started
enabled: truePenjelasan tiap task:
Update apt package cache — memastikan index package segar. Parameter cache_valid_time: 3600 membuat cache hanya diperbarui jika sudah lebih dari 1 jam, sehingga playbook tidak selalu apt update di setiap eksekusi.Install nginx — memasang NGINX. state: present artinya "pastikan terpasang" — jika sudah terpasang, task ini tidak melakukan apa-apa.Deploy custom index page — modul copy dengan content: (bukan src:) menulis isi file langsung dari playbook. Ini contoh konfigurasi paling sederhana; di episode 8 kita akan menggantinya dengan template Jinja2.Ensure nginx is started and enabled — memastikan service berjalan sekarang (started) dan menyala otomatis saat boot (enabled: true).Important
Perhatikan urutan task: install dulu, baru konfigurasi, baru start service. Ini logika yang sama seperti SOP manual — tidak mungkin me-restart service yang belum terpasang. Playbook mengeksekusi task secara berurutan dari atas ke bawah.
ansible-playbookUntuk mengeksekusinya, kita butuh file inventory (dari episode 3) dan perintah ansible-playbook:
ansible-playbook -i inventory.yml nginx-playbook.ymlPerhatikan output di atas: setiap task berjalan berurutan dan hasilnya terangkum di bagian PLAY RECAP — ringkasan terpusat yang menjadi bahasa universal status eksekusi playbook.
Ada tiga flag yang wajib kalian kuasai sejak hari pertama.
--check (Dry Run)Menjalankan playbook tanpa benar-benar mengubah sistem — hanya melaporkan apa yang akan berubah:
ansible-playbook -i inventory.yml nginx-playbook.yml --checkCaution
--check adalah simulasi, bukan jaminan. Modul command dan shell tidak ikut disimulasikan, dan efek samping (seperti download package) tidak benar-benar terjadi. Jadi output --check adalah gambaran, bukan kontrak pasti.
--diff (Lihat Perubahan Teks)Menampilkan perbedaan before/after untuk file yang diubah modul (seperti copy, template, lineinfile):
ansible-playbook -i inventory.yml nginx-playbook.yml --diffTASK [Deploy custom index page] ************************************************
--- before: /var/www/html/index.html
+++ after: /var/www/html/index.html
@@ -1,5 +1,7 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <h1>Hello dari Ansible!</h1>
+ </body>
+</html>Output --diff sangat berguna saat meninjau perubahan konfigurasi sebelum diterapkan — ini seperti git diff untuk isi file di server.
-v / -vvv (Verbosity)Menambah level detail log:
| Flag | Informasi tambahan |
|---|---|
-v | Detail eksekusi task standar |
-vv | Menambahkan output dari connection plugin |
-vvv | Menambahkan kredensial dan detail command SSH (untuk debugging mendalam) |
ansible-playbook -i inventory.yml nginx-playbook.yml --check --diffTip
Pola terbaik sebelum menerapkan ke production: jalankan ansible-playbook ... --check --diff terlebih dahulu. Kalian melihat rencana perubahan dan diff teks-nya tanpa menyentuh server. Setelah yakin, baru jalankan tanpa flag.
Sekarang kita sampai pada konsep yang paling menentukan kualitas playbook kalian: idempotency — kemampuan untuk dijalankan berkali-kali dengan hasil akhir yang sama.
Kuncinya ada pada cara kerja modul Ansible: modul memeriksa state saat ini di server sebelum melakukan tindakan. Jika state sudah sesuai keinginan, tidak ada yang diubah. Jika belum, modul mengubahnya.
Status output di PLAY RECAP menceritakan hasil ini:
| Status | Makna | Contoh Penyebab |
|---|---|---|
ok | Task berhasil dan tidak ada perubahan (state sudah sesuai) | NGINX sudah terinstal, tidak perlu install lagi |
changed | Task berhasil dan melakukan perubahan | Package baru terpasang, file baru ditulis |
failed | Task gagal dan menghentikan playbook | Package tidak ditemukan, permission ditolak |
skipped | Task tidak dijalankan karena kondisi tidak terpenuhi | Task dengan when: yang bernilai false (episode 9) |
unreachable | Host tidak bisa dihubungi sama sekali | Server down, SSH tidak bisa akses |
Untuk melihat idempotency secara nyata, jalankan playbook yang sama dua kali berturut-turut:
ansible-playbook -i inventory.yml nginx-playbook.ymlEksekusi pertama — sistem baru, semua task berubah:
web-01.prod.example.com : ok=4 changed=4 unreachable=0 failed=0 skipped=0Eksekusi kedua — sistem sudah sesuai keinginan, tidak ada yang berubah:
web-01.prod.example.com : ok=4 changed=0 unreachable=0 failed=0 skipped=0Perhatikan perbedaannya: changed turun dari 4 menjadi 0, sementara ok tetap 4. Inilah idempotency — playbook yang baik, ketika sistem sudah dalam kondisi yang diinginkan, tidak menimbulkan efek samping apa pun saat dijalankan ulang.
Important
Idempotency bukan perilaku ajaib — ia adalah hasil disiplin memakai modul yang tepat. Modul stateful (apt, file, copy, systemd_service) memeriksa state sebelum bertindak. Modul command/shell tidak — mereka mengeksekusi perintah apa adanya setiap kali. Inilah alasan mengapa di episode 4 kita berulang kali menekankan: pakai modul, bukan command, saat modul tersedia.
Satu lagi yang perlu diketahui: idempotency bekerja per task, bukan per playbook. Jika salah satu task gagal, playbook berhenti di situ (untuk host tersebut) dan task setelahnya tidak dijalankan. Inilah kenapa status failed muncul di recap.
| Kesalahan | Gejala | Solusi |
|---|---|---|
Lupa become: true untuk task root | Permission denied saat install/restart | Tambahkan become di level play atau task |
| Indentasi YAML salah | Error parsing seperti mapping values are not allowed here | Gunakan ekstensi YAML di editor; YAML Playbook memakai spasi, bukan tab |
Task tanpa name | Log sulit dibaca dan sulit di-troubleshoot | Selalu beri name deskriptif di setiap task |
| Menjalankan langsung ke production tanpa dry-run | Perubahan tak terduga saat apply | Biasakan --check --diff dulu |
Memakai command untuk hal yang ada modulnya | Playbook tidak idempoten | Cek ansible-doc -l untuk modul yang tepat |
| Beberapa play dalam satu file tanpa konteks | Playbook menargetkan host yang salah | Setiap hosts: di play pertama sangat menentukan scope |
Pada episode 5 ini, kita telah menulis playbook pertama yang nyata: instalasi dan konfigurasi NGINX. Kalian memahami anatomi playbook (hosts, become, tasks), privilege escalation, cara menjalankan dengan ansible-playbook, flag-flag penting seperti --check dan --diff, serta mekanisme idempotency yang terlihat jelas dari perbedaan status OK dan CHANGED antara eksekusi pertama dan kedua.
Inti yang harus dibawa pulang: playbook yang baik adalah playbook yang "bosan" — dijalankan berulang kali, ia tidak melakukan perubahan yang tidak perlu karena sistem sudah berada di state yang diinginkan.
Di episode 6 selanjutnya, kita akan membahas salah satu pola terpenting dalam playbook production: Handlers — cara Ansible merestart service hanya ketika konfigurasinya berubah, bukan setiap kali playbook dijalankan. Ini menyelesaikan masalah yang akan segera kalian temui saat playbook kalian tumbuh besar. Pastikan tetap semangat!