Bab 35 menutup dengan janji yang sengaja digantung: script backup-config.sh, cleanup-temp.sh, dan health-check.sh yang sudah kita tulis dan jadwalkan lewat cron maupun systemd timer semuanya hanya berlaku untuk satu server. Begitu jaringan bertambah dari satu server jadi tiga seperti skema 192.168.1.0/24 yang sudah kita bangun sejak Bab 9 (DNS di 192.168.1.10) dan Bab 12 (Nginx di 192.168.1.20), menyalin script yang sama satu per satu ke setiap server lewat scp lalu menjalankannya manual lewat SSH mulai terasa tidak masuk akal. Sepuluh server berarti sepuluh kali proses manual yang identik, dan human error tinggal menunggu waktu saat salah satu server terlewat atau mendapat versi script yang sedikit berbeda dari yang lain. Bab ini membuka Ansible sebagai jawaban langsung atas masalah tersebut, sekaligus konsep Infrastructure as Code yang jadi payung besar di baliknya.
Skenario ini sangat familier bagi Sysadmin yang mengelola lebih dari satu server. Developer meminta paket Node.js versi tertentu terpasang di lima server aplikasi sekaligus. Tim keamanan meminta konfigurasi firewall yang sama diterapkan ke seluruh server produksi setelah insiden di satu server ketahuan. Tanpa alat bantu, permintaan semacam ini berubah jadi checklist manual yang dikerjakan berulang, rawan lupa satu langkah, dan nyaris mustahil diaudit siapa mengubah apa. Ansible menjawabnya dengan pendekatan yang berbeda dari script bash biasa, yaitu mendeskripsikan kondisi akhir yang diinginkan lewat file konfigurasi terstruktur, lalu membiarkan Ansible sendiri yang menghitung langkah apa saja yang perlu dijalankan agar kondisi itu tercapai di setiap server tujuan.
Kita akan mulai bab ini dari konsep Infrastructure as Code dan cara kerja Ansible secara singkat, dilanjutkan instalasi Ansible di control node, lalu inventory dan anatomi playbook dasar, dan ditutup dengan menjalankan playbook pertama yang benar-benar memasang Nginx di server target. Bab 37 nanti melengkapi sisi provisioning awal lewat cloud-init, sementara bab ini fokus pada konfigurasi server yang sudah berjalan.
36.1 Konsep Infrastructure as Code
Infrastructure as Code (IaC) adalah praktik mengelola dan menyediakan infrastruktur, mulai dari konfigurasi server, jaringan, sampai provisioning VM, lewat file definisi yang bisa dibaca mesin, bukan lewat langkah manual di terminal atau klik di dashboard. File definisi ini sifatnya deklaratif: kita menuliskan kondisi akhir yang diinginkan (misalnya "paket nginx harus terpasang dan service-nya harus berjalan"), bukan urutan perintah imperatif langkah demi langkah seperti pada script bash di Bab 35.
36.1.1 Masalah Configuration Drift yang Diselesaikan IaC
Configuration drift adalah istilah untuk kondisi ketika konfigurasi aktual di sebuah server pelan-pelan menyimpang dari konfigurasi yang seharusnya, biasanya karena perubahan manual yang tidak tercatat di mana pun. Satu server mendapat patch keamanan lebih dulu karena Sysadmin sempat login langsung untuk memperbaiki masalah mendesak, sementara empat server lain di belakang load balancer yang sama tetap berjalan dengan versi lama karena perubahan itu tidak pernah direplikasi. Ketika insiden terjadi, drift semacam ini membuat troubleshooting jadi lebih sulit karena server yang "identik" ternyata punya riwayat konfigurasi berbeda-beda.
IaC menutup celah ini dengan menjadikan file definisi sebagai satu-satunya sumber kebenaran. Perubahan konfigurasi dilakukan dengan mengubah file tersebut, lalu menjalankannya ulang ke seluruh server target, bukan login manual ke satu server tertentu. File ini juga bisa disimpan di Git seperti kode aplikasi pada umumnya, sehingga setiap perubahan konfigurasi server punya riwayat, bisa di-review lewat pull request, dan bisa di-rollback persis seperti kode. Konsep version control untuk infrastruktur inilah yang membedakan IaC dari sekadar "script otomasi" biasa.
36.1.2 Cara Kerja Ansible: Agentless, Push-Based, dan Idempotent
Ansible bukan satu-satunya tool IaC, tetapi karakteristiknya membuatnya jadi pilihan yang cocok sebagai titik masuk. Tiga sifat berikut yang paling membedakannya dari tool sejenis seperti Puppet dan Chef.
- Ansible bersifat agentless, artinya tidak perlu ada software daemon khusus terpasang dan berjalan terus-menerus di server yang dikelola. Ansible cukup terhubung lewat SSH, protokol yang sudah kita konfigurasi sejak Bab 3, sehingga tidak ada proses tambahan yang perlu dipelihara atau jadi celah keamanan baru di setiap server target.
- Ansible bekerja dengan model push-based: satu mesin yang disebut control node mendorong konfigurasi ke sejumlah managed node (server target) begitu perintah dijalankan. Model ini berbeda dari tool pull-based seperti Puppet, yang mengandalkan agent di tiap server target untuk secara berkala menarik konfigurasi terbaru sendiri dari server pusat.
- Setiap operasi Ansible dirancang idempotent, artinya menjalankan playbook yang sama berkali-kali ke server yang sama akan menghasilkan kondisi akhir yang sama persis, tanpa efek samping tambahan pada eksekusi kedua dan seterusnya. Jika paket nginx sudah terpasang, menjalankan ulang playbook instalasi nginx tidak akan memasangnya lagi atau menimbulkan error, cukup melaporkan bahwa kondisi yang diminta sudah tercapai. Sifat inilah yang membuat playbook aman dijalankan berulang sebagai bagian dari proses deployment rutin, berbeda dari script bash naif yang bisa gagal atau berperilaku aneh kalau dijalankan dua kali ke server yang sama.
Perlu dibedakan juga antara tool configuration management seperti Ansible, Puppet, dan Chef dengan tool provisioning seperti Terraform yang disinggung lagi di Bab 44 sebagai area pendalaman lanjutan. Configuration management berurusan dengan "bagaimana mengonfigurasi server yang sudah ada", sedangkan provisioning berurusan dengan "bagaimana membuat server/instance itu sendiri" di cloud maupun on-premise. Cloud-init yang dibahas di Bab 37 mengisi celah di antara keduanya, yaitu provisioning awal saat VM pertama kali menyala, sebelum Ansible sempat masuk untuk konfigurasi lanjutan.
| Aspek | Ansible | Puppet/Chef | Terraform |
|---|---|---|---|
| Arsitektur | Agentless, push-based lewat SSH | Butuh agent di tiap node, umumnya pull-based | Agentless, memanggil API provider cloud |
| Fokus utama | Configuration management | Configuration management | Provisioning infrastruktur (VM, network, storage) |
| Bahasa konfigurasi | YAML (playbook) | DSL khusus (Puppet) / Ruby (Chef) | HCL (HashiCorp Configuration Language) |
| Kurva belajar awal | Relatif landai | Lebih curam, perlu belajar DSL/Ruby | Landai untuk provisioning, konsep state terpisah |
Ringkasnya, Ansible dipilih di bab ini karena arsitekturnya yang agentless membuat instalasi awal jauh lebih sederhana dibanding Puppet atau Chef, sementara fokusnya di configuration management melengkapi shell script di Bab 35, bukan menggantikannya sepenuhnya.
36.2 Instalasi Ansible di Control Node
Control node adalah mesin tempat Ansible terpasang dan dari mana seluruh perintah dijalankan, sedangkan managed node adalah server target yang dikonfigurasi. Ansible mensyaratkan control node berjalan di sistem mirip Unix (Linux, macOS, atau BSD) dengan Python terpasang; Windows tidak didukung sebagai control node, meski tetap bisa jadi managed node lewat protokol WinRM (di luar cakupan bab ini). Managed node juga umumnya perlu Python 3 terpasang, karena sebagian besar module Ansible dieksekusi sebagai script Python di sisi managed node; Ubuntu Server pada dasarnya sudah menyertakan Python 3, kecuali pada image cloud minimal tertentu yang sengaja dipangkas.
36.2.1 Instalasi Ansible via APT
Ubuntu menyediakan package ansible di repository universe sejak beberapa rilis LTS terakhir. Package ini adalah metapackage yang membundel ansible-core (mesin inti Ansible) bersama kumpulan collection resmi yang sudah dikurasi, cukup untuk kebutuhan belajar dan penggunaan sehari-hari tanpa instalasi tambahan.
Langkah Praktik
- Pastikan repository universe aktif, lalu perbarui daftar paket dan pasang Ansible di control node.
sudo apt update sudo apt install ansible - Periksa versi Ansible yang terpasang.
ansible --version
Verifikasi dan Troubleshooting
- Baris pertama output
ansible --versionberformatansible [core X.Y.Z], diikuti barisconfig file,python version, dan sejenisnya. AngkaX.Y.Zmengikuti versi yang sedang dikemas di repository Ubuntu Server 26.04 LTS saat paket ini dipasang, jadi jangan heran kalau berbeda dari contoh versi mana pun yang pernah dilihat di internet; selalu jadikan output di server ini sebagai acuan, bukan angka di tutorial manapun termasuk bab ini. - Jika butuh versi Ansible paling baru dari upstream yang belum sempat masuk ke repository Ubuntu, dokumentasi resmi Ansible merekomendasikan instalasi lewat
pipxalih-alih PPA pihak ketiga yang dukungannya sudah berkurang untuk rilis-rilis terbaru. Untuk kebutuhan belajar di bab ini, packageansibledari APT sudah lebih dari cukup. - Error
Unable to locate package ansiblebiasanya berarti repository universe belum aktif. Aktifkan lewatsudo add-apt-repository universelalu ulangiapt update.
36.2.2 Menyiapkan Akses SSH ke Managed Node
Sebab Ansible terhubung ke managed node lewat SSH, autentikasi berbasis key yang sudah kita konfigurasi di Bagian 3.2 adalah prasyarat mutlak sebelum lanjut ke bagian berikutnya. Tanpa itu, Ansible akan berhenti di setiap host menunggu input password yang tidak pernah bisa diketik karena dijalankan otomatis. Bagian ini mengasumsikan sudah ada satu server tambahan yang disiapkan khusus untuk latihan bab ini, kita beri nama app01 dengan IP 192.168.1.40 mengikuti skema jaringan 192.168.1.0/24 yang sudah dipakai sejak Bab 9; sesuaikan IP dan hostname ini dengan server yang benar-benar tersedia.
Langkah Praktik
- Dari control node, salin public key yang sudah dibuat di Bagian 3.2.1 ke
app01.ssh-copy-id [email protected] - Login manual satu kali lewat SSH biasa untuk mengonfirmasi koneksi berjalan sekaligus menerima host key
app01keknown_hostsmilik control node.ssh [email protected] exit - Pastikan user tersebut punya akses
sudodiapp01, karena instalasi paket pada Bagian 36.4 memerlukan privilege escalation lewat directivebecomeyang akan kita pakai di playbook.ssh [email protected] sudo whoami
Verifikasi dan Troubleshooting
- Perintah
sudo whoamipada langkah ketiga semestinya mencetakroottanpa diminta password jika sudoersapp01sudah dikonfigurasiNOPASSWD, atau meminta password sekali jika belum. Kedua kondisi ini valid; jika diminta password, catat karena nanti perlu ditambahkan flag--ask-become-passsaat menjalankan playbook di Bagian 36.4.2. - Langkah login manual pada urutan kedua bukan formalitas. Tanpa host key
app01lebih dulu tersimpan diknown_hosts, percobaan koneksi pertama Ansible bisa menggantung menunggu konfirmasi interaktifAre you sure you want to continue connecting (yes/no)?yang tidak pernah muncul di konteks non-interaktif, sebuah jebakan yang sering luput disadari Sysadmin yang baru pertama kali memakai Ansible. - Error
Permission denied (publickey)di langkah mana pun berarti kembali dulu ke troubleshooting Bagian 3.2, karena masalahnya ada di lapisan SSH, bukan di Ansible.
36.3 Inventory dan Playbook Dasar
Dua file jadi fondasi setiap pekerjaan Ansible: inventory yang mendaftar server mana saja yang dikelola, dan playbook yang mendefinisikan apa yang harus dilakukan terhadap server-server itu. Kita akan membuat direktori kerja khusus, lalu menyusun keduanya secara bertahap.
36.3.1 Membuat Inventory Statis
Inventory adalah daftar host dan group host yang jadi target Ansible, ditulis dalam format INI atau YAML. Bagian ini memakai format INI karena lebih ringkas untuk inventory kecil.
Langkah Praktik
- Buat direktori kerja untuk seluruh file Ansible di bab ini.
mkdir -p ~/ansible-project cd ~/ansible-project - Buat file inventory.
nano inventory.ini - Isi dengan definisi group
webserversberisiapp01.[webservers] app01 ansible_host=192.168.1.40 ansible_user=sysadmin
Verifikasi dan Troubleshooting
- Tampilkan daftar host yang terbaca dari inventory untuk memastikan formatnya valid.
Output berupa JSON, dan bagian yang perlu diperiksa adalah blokansible-inventory -i inventory.ini --list"webservers": {"hosts": ["app01"]}yang menandakan Ansible berhasil membaca group dan host sesuai isiinventory.ini. - Untuk inventory yang lebih besar, host bisa dikelompokkan ke banyak group sekaligus (misalnya
[webservers],[dbservers]), dan group-of-groups lewat sintaks[production:children]. Bab ini sengaja memakai satu group saja agar fokus ke alur dasarnya dulu. - Ansible sebenarnya juga mendukung inventory dinamis yang mengambil daftar host langsung dari API cloud provider atau sistem inventaris internal, alih-alih file statis seperti
inventory.ini. Topik ini berguna begitu jumlah server sudah puluhan dan berubah-ubah, tetapi berada di luar cakupan bab pengenalan ini.
36.3.2 Menguji Konektivitas dengan Ad-Hoc Command
Ad-hoc command adalah perintah Ansible satu baris yang langsung dieksekusi tanpa perlu menulis playbook, cocok untuk pengecekan cepat seperti menguji konektivitas sebelum menulis playbook sungguhan.
Langkah Praktik
- Jalankan module
pingbawaan Ansible ke seluruh host di groupwebservers.ansible webservers -i inventory.ini -m ping
Verifikasi dan Troubleshooting
- Output yang berhasil menampilkan
app01 | SUCCESS =>diikuti blok JSON berisi"ping": "pong". Modulepingdi sini murni menguji jalur SSH dan interpreter Python di managed node, bukan ICMP ping seperti perintahpingbiasa di shell. - Status
UNREACHABLEumumnya berarti masalah koneksi SSH, seperti IP atau port salah, atau host key belum diterima seperti dibahas di Bagian 36.2.2. - Status
FAILEDdengan pesan menyinggungpythonberarti interpreter Python3 tidak ditemukan di path standar pada managed node. Tambahkan variableansible_python_interpreter=/usr/bin/python3di baris host padainventory.inijika kasus ini terjadi.
36.3.3 Anatomi Playbook YAML
Playbook adalah file YAML yang mendeskripsikan satu atau lebih play, dan setiap play berisi daftar task yang dijalankan berurutan terhadap host tertentu. Setiap task pada dasarnya memanggil satu module, yaitu unit kerja Ansible yang sudah dibungkus agar idempotent, misalnya module ansible.builtin.apt untuk manajemen paket atau ansible.builtin.service untuk mengelola service systemd.
Langkah Praktik
- Buat playbook percobaan pertama untuk memahami strukturnya sebelum masuk ke playbook nginx yang sesungguhnya.
nano hello.yml - Isi dengan struktur minimal berikut.
Baris--- - name: Playbook percobaan pertama hosts: webservers tasks: - name: Tampilkan pesan sederhana ansible.builtin.debug: msg: "Ansible berhasil terhubung ke {{ inventory_hostname }}"namedi level play dan di level task hanya label deskriptif yang tercetak saat eksekusi, tidak memengaruhi logika.hosts: webserversmenunjuk ke group yang didefinisikan diinventory.inipada Bagian 36.3.1. Variable{{ inventory_hostname }}memakai sintaks Jinja2, template engine yang dipakai Ansible untuk menyisipkan nilai dinamis, dalam hal ini nama host yang sedang diproses. - Jalankan playbook.
ansible-playbook -i inventory.ini hello.yml
Verifikasi dan Troubleshooting
- Output semestinya menampilkan blok
TASK [Gathering Facts]lebih dulu, sebuah task otomatis yang selalu berjalan di awal play untuk mengumpulkan informasi sistem managed node, disusulTASK [Tampilkan pesan sederhana]yang mencetak pesan berisiapp01, lalu ditutup barisPLAY RECAPberisi ringkasanapp01 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0. - Error YAML seperti
mapping values are not allowed in this contexthampir selalu berarti masalah indentasi. YAML sangat sensitif terhadap spasi, dan Ansible mensyaratkan indentasi konsisten memakai spasi, bukan karakter tab. - Task
Gathering Factsbisa dinonaktifkan lewatgather_facts: falsedi level play untuk mempercepat eksekusi playbook yang tidak butuh informasi sistem managed node, meski untuk playbook nginx di bagian berikutnya kita biarkan aktif seperti default.
36.4 Menjalankan Playbook Sederhana: Instalasi Nginx
Bab 12 memasang Nginx di 192.168.1.20 secara manual lewat apt install nginx yang diketik langsung di terminal server tersebut. Bagian ini menuliskan ulang proses yang sama dalam bentuk playbook, lalu menjalankannya ke app01. Perbedaannya baru terasa penuh begitu proses yang sama perlu diulang ke server kedua, ketiga, atau kesepuluh: cukup jalankan playbook yang sama, tanpa mengetik ulang urutan perintah manual di setiap server.
36.4.1 Menulis Playbook install-nginx.yml
Langkah Praktik
- Buat file playbook baru.
nano install-nginx.yml - Isi dengan dua task: memasang paket nginx, lalu memastikan service-nya aktif dan enabled.
--- - name: Instalasi dan menjalankan Nginx hosts: webservers become: true tasks: - name: Pasang paket nginx ansible.builtin.apt: name: nginx state: present update_cache: true - name: Pastikan service nginx aktif dan enabled saat boot ansible.builtin.service: name: nginx state: started enabled: truebecome: truedi level play membuat seluruh task di bawahnya dijalankan lewat privilege escalation (setarasudo) di managed node, sejalan dengan pengecekan akses sudo yang sudah dilakukan di Bagian 36.2.2. Parameterupdate_cache: truepada moduleansible.builtin.aptsetara menjalankanapt updatesebelum instalasi, sedangkanstate: presentberarti "pastikan paket ini terpasang", tidak masalah apakah sudah ada sebelumnya atau belum, konsisten dengan prinsip idempotent di Bagian 36.1.2. Moduleansible.builtin.servicepada task kedua setara kombinasisystemctl start nginxdansystemctl enable nginxyang sudah dipraktikkan manual di Bab 5. - Periksa sintaks playbook sebelum benar-benar dijalankan.
ansible-playbook -i inventory.ini install-nginx.yml --syntax-check
Verifikasi dan Troubleshooting
- Perintah
--syntax-checkhanya memvalidasi struktur YAML dan nama module, tidak benar-benar menghubungi managed node maupun mengubah apa pun. Outputplaybook: install-nginx.ymltanpa pesan error berarti sintaksnya sudah valid. - Nama module ditulis lengkap dengan prefix
ansible.builtin., mengikuti konvensi Fully Qualified Collection Name (FQCN) yang direkomendasikan Ansible sejak versi 2.10 ke atas untuk menghindari ambiguitas ketika ada collection pihak ketiga yang kebetulan memakai nama module sama.
36.4.2 Menjalankan dan Memverifikasi Idempotency Playbook
Langkah Praktik
- Jalankan playbook untuk pertama kalinya. Tambahkan flag
--ask-become-pass(bisa disingkat-K) jika user diapp01masih diminta password saatsudopada pengecekan Bagian 36.2.2.ansible-playbook -i inventory.ini install-nginx.yml - Jalankan ulang playbook yang persis sama tanpa mengubah apa pun, untuk membuktikan sifat idempotent yang dibahas di Bagian 36.1.2.
ansible-playbook -i inventory.ini install-nginx.yml
Verifikasi dan Troubleshooting
- Pada eksekusi pertama, baris
PLAY RECAPsemestinya menampilkanapp01 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0, denganchanged=2menandakan task pasang paket dan task aktifkan service sama-sama benar-benar mengubah kondisiapp01. - Pada eksekusi kedua, angka
changedsemestinya turun jadi0sementaraoktetap3, karena Ansible mendeteksi paket nginx sudah terpasang dan service sudah berjalan, sehingga tidak ada tindakan yang perlu diulang. Inilah bukti nyata sifat idempotent yang secara teori sudah dijelaskan di Bagian 36.1.2. Parameterupdate_cache: truetidak mengubah kesimpulan ini; Ansible tetap menjalankanapt updatedi balik layar pada kedua eksekusi, tetapi statuschangedpada moduleansible.builtin.aptmurni mengikuti ada tidaknya perubahan pada paket itu sendiri, bukan dari pembaruan cache-nya. - Konfirmasikan dari luar bahwa Nginx benar-benar berjalan di
app01, memakai polacurlyang sama seperti Bagian 12.1.
Halaman Welcome to nginx! bawaan yang muncul menandakan instalasi lewat playbook sudah berhasil menghasilkan kondisi akhir yang sama seperti instalasi manual di Bab 12.curl http://192.168.1.40 - Kegagalan dengan pesan menyinggung
Missing sudo passwordberarti flag--ask-become-passpada langkah pertama terlewat, padahal user di managed node belum dikonfigurasiNOPASSWDdi sudoers. - Untuk audit cepat sebelum benar-benar mengubah kondisi server produksi, flag
--checkmenjalankan playbook dalam mode simulasi mirip dry-run yang sudah dikenal dari Bagian 35.2.2, melaporkan task mana saja yang akanchangedtanpa benar-benar mengeksekusinya. Kebiasaan ini layak dijadikan standar sebelum menjalankan playbook baru ke server produksi yang belum pernah diuji.
Sampai di sini, kita sudah punya alur kerja Ansible yang utuh meski masih sederhana: satu control node, satu managed node terdaftar di inventory, dan satu playbook yang terbukti idempotent saat dijalankan berulang. Playbook install-nginx.yml ini juga bisa langsung dijalankan ke lebih dari satu host sekaligus hanya dengan menambah baris baru di inventory.ini, tanpa mengubah satu baris pun di playbook itu sendiri, persis kemampuan yang tidak dimiliki script bash manual di Bab 35. Bab 37 melangkah ke tahap sebelumnya dalam siklus hidup server, yaitu cloud-init, yang mengotomasi provisioning awal seperti pembuatan user dan pemasangan SSH key sejak detik pertama VM menyala, sebelum Ansible sempat masuk mengambil alih konfigurasi lanjutan seperti yang sudah kita praktikkan di bab ini.

