Standarisasi kualitas kode Ansible dengan ansible-lint serta otomasi pengujian role secara terisolasi menggunakan Molecule, mulai dari driver Docker/Podman hingga verifikasi otomatis dengan Testinfra.

Setelah di episode 17 sebelumnya kita membahas bagaimana menulis Custom Modules dan Custom Filters dengan Python, kalian sekarang tahu bahwa kode Ansible bisa kita perpanjang sesuai kebutuhan. Namun, ada satu konsekuensi penting dari kemampuan tersebut: semakin banyak kode yang kita tulis sendiri, semakin besar juga tanggung jawab kita untuk menjaga kualitasnya.
Bayangkan skenario ini: kalian sedang mengerjakan playbook yang bertugas membersihkan log lama di ratusan server produksi. Playbook itu lolos uji coba di mesin lokal kalian, lalu dijalankan di produksi dan ternyata menghapus direktori yang salah. Dampaknya bukan hanya tampilan yang buruk di layar, tapi layanan yang down, data yang hilang, dan laporan insiden yang harus kalian tulis. Inilah kenapa kode infrastruktur membutuhkan jaring pengaman yang sama ketatnya dengan kode aplikasi.
Di episode 18 ini, kita akan membahas dua alat yang menjadi tulang punggung kualitas kode Ansible di dunia nyata: ansible-lint untuk menegakkan best practice, gaya penulisan, dan keamanan; serta Molecule untuk menguji role secara otomatis di lingkungan yang terisolasi menggunakan Docker/Podman. Setelah episode ini, kalian akan memahami alur kerja yang diharapkan di tim profesional: lint dulu, test dulu, baru deploy.
Di episode-episode sebelumnya kita sudah membahas idempotency, handlers, roles, hingga collections. Semua itu adalah fondasi agar automasi aman. Tapi fondasi saja tidak cukup. Sebuah playbook bisa saja idempotent, namun tetap saja berbahaya: misalnya menulis password hardcoded ke dalam kode, menggunakan shell untuk hal yang seharusnya memakai modul khusus, atau menjalankan task tanpa name sehingga log eksekusi sulit dibaca saat insiden.
Coba analogikan begini: idempotency itu seperti mobil yang tidak pernah mogok. Tapi kalian tetap butuh rambu lalu lintas agar tidak menabrak (ansible-lint), dan butuh simulator berkendara agar bisa berlatih tanpa merusak jalanan nyata (Molecule). Keduanya adalah bagian dari budaya engineering yang sehat: menuliskan kualitas ke dalam proses, bukan hanya ke dalam niat.
Dua masalah nyata yang paling sering ditemui di lapangan:
command: apt install nginx, yang lain menulis ansible.builtin.apt. Playbook tetap jalan, tapi sulit di-maintain dan rawan error.ansible-lint dan Molecule menjawab kedua masalah ini secara berurutan: yang pertama memastikan kode benar dan konsisten secara statis, yang kedua memastikan kode benar-benar bekerja di lingkungan nyata.
ansible-lint adalah linter resmi dari proyek Ansible yang memeriksa playbook, role, dan inventory dari tiga sudut pandang:
| Sudut Pandang | Contoh yang Diperiksa |
|---|---|
| Syntax & Format | YAML valid, playbook terstruktur dengan benar, tidak ada name yang hilang pada task |
| Best Practice & Style | Wajib menggunakan FQCN (misal ansible.builtin.copy), tidak memakai command/shell jika ada modul khusus, penggunaan handler yang benar |
| Keamanan | Tidak ada password/API key hardcoded, permission file eksplisit (mode), tidak ada konten sensitif yang tidak di-no_log |
Yang membedakan ansible-lint dari sekadar YAML parser adalah ia memahami semantik Ansible. Ia tahu bahwa ansible.builtin.apt lebih tepat daripada shell: apt-get install, dan ia bisa mendeteksi pola berbahaya yang secara syntax sah-sah saja.
Cara instalasi yang direkomendasikan adalah menggunakan pipx agar terisolasi dari environment Python sistem, persis seperti cara kita menginstal Ansible di episode 0:
pipx install ansible-lintNote
ansible-lint membutuhkan ansible-core sebagai dependensi. Jika kalian menginstalnya lewat pipx, versi ansible-core yang sesuai akan otomatis ikut terpasang. Pastikan versinya kompatibel dengan playbook yang kalian tulis.
Cara termudah adalah menjalankannya dari root direktori proyek Ansible. ansible-lint secara otomatis akan mencari playbook, role, dan file ansible.cfg di sekitarnya:
ansible-lintJika ada pelanggaran, output yang muncul kira-kira seperti ini:
Perhatikan tiga hal penting dari output di atas:
name[missing]) dan lokasi file:baris yang spesifik. Ini memudahkan kalian langsung menuju ke baris yang bermasalah.error akan membuat perintah berhenti dengan exit code non-zero, yang sangat berguna ketika dipasang di pipeline CI/CD (akan kita bahas di episode 19).command-instead-of-module muncul karena penulis playbook memakai shell padahal ada modul ansible.builtin.apt. Ini contoh klasik "benar secara syntax, salah secara best practice".Tip
Jalankan ansible-lint dari root proyek, bukan dari subdirektori, supaya ia membaca ansible.cfg dan file konfigurasi lint kalian. Ini juga memastikan semua role dan collections ikut diperiksa secara konsisten.
Salah satu pelanggaran paling umum adalah task tanpa name. Perhatikan perbaikan dengan diff berikut:
tasks:
- ansible.builtin.package:
name: nginx
state: present
- name: Install Nginx
ansible.builtin.package:
name: nginx
state: presentDengan menambahkan name yang deskriptif, output playbook menjadi mudah dibaca saat debugging dan ansible-lint tidak lagi mengeluh. Hal kecil seperti ini berdampak besar ketika kalian membaca ribuan baris log eksekusi di tengah insiden.
.ansible-lintTidak semua tim menerapkan aturan yang sama. File konfigurasi .ansible-lint (YAML) memungkinkan kalian mengecualikan path, melewati aturan tertentu, atau mengubah sebuah aturan menjadi sekadar peringatan:
---
exclude_paths:
- .git/
- .github/
- molecule/
skip_list:
- name[casing]
warn_list:
- experimental
- risky-file-permissions
secrets: trueWarning
Hati-hati dengan skip_list. Me-lewati aturan memang bisa dilakukan, tapi itu juga berarti kalian secara sadar menerima risiko yang dilindungi aturan tersebut. Sebagai praktik yang baik, tulis komentar alasan di file konfigurasi setiap kali kalian me-nonaktifkan sebuah rule, supaya engineer lain (dan diri kalian sendiri di masa depan) mengerti mengapa.
Setelah kode kalian lulus lint, pertanyaan berikutnya adalah: apakah kode ini benar-benar bekerja? Di sinilah Molecule berperan.
Molecule adalah framework testing untuk Ansible roles. Ia bekerja dengan cara yang sangat masuk akal: membangun instance target di lingkungan yang terisolasi (misalnya container Docker atau Podman, atau VM cloud), lalu menjalankan role kalian di sana, memverifikasi hasilnya, dan membersihkan semua jejaknya.
Mengapa ini penting? Karena menguji role langsung di server produksi itu seperti menguji parasut saat sudah di atas pesawat. Molecule memungkinkan kalian melakukan "uji parasut" ribuan kali di laboratorium tanpa risiko sedikit pun terhadap infrastruktur yang sedang berjalan.
Arsitektur Molecule terdiri dari beberapa komponen:
| Komponen | Peran |
|---|---|
| Scenario | Satu set konfigurasi & file yang mendefinisikan satu skenario pengujian (misal default, centos, debian) |
| Driver | "Kendaraan" yang membuat instance, contohnya docker, podman, ec2, gcp, openstack |
| Provisioner | Bagian yang menjalankan role, yaitu Ansible itu sendiri |
| Verifier | Alat yang memverifikasi hasil, secara default ansible (playbook verify.yml) atau testinfra (test Python) |
Molecule diinstal terpisah dari Ansible, beserta driver yang ingin kalian pakai:
pipx install "molecule[docker]"Important
Kalian harus menginstal driver sesuai dengan container runtime yang tersedia di mesin kalian. Driver docker membutuhkan daemon Docker yang berjalan, sedangkan driver podman membutuhkan Podman (misalnya di Fedora/RHEL atau WSL2). Tidak ada salahnya menginstal keduanya, tapi ingat bahwa Molecule memilih driver berdasarkan konfigurasi di molecule.yml.
Molecule bekerja bersama role Ansible. Pertama, buat role terlebih dahulu menggunakan ansible-galaxy role init (yang sudah kita bahas di episode 12), lalu inisialisasi skenario Molecule di dalamnya:
ansible-galaxy role init my_nginx_role
cd my_nginx_role
molecule init scenario --driver-name dockerStruktur direktori yang dihasilkan kira-kira seperti ini:
my_nginx_role/
├── molecule/
│ └── default/
│ ├── converge.yml # Playbook untuk menjalankan role
│ ├── create.yml # Membuat instance (di-generate driver)
│ ├── destroy.yml # Menghapus instance (di-generate driver)
│ ├── molecule.yml # Konfigurasi utama skenario
│ └── verify.yml # Playbook verifikasi (verifier ansible)
├── defaults/
│ └── main.yml
├── tasks/
│ └── main.yml
└── meta/
└── main.ymlFile converge.yml adalah playbook yang "menyuntikkan" role kalian ke dalam instance pengujian. Ia mirip dengan playbook biasa, hanya saja role dipanggil langsung:
---
- name: Converge
hosts: all
become: true
gather_facts: true
tasks:
- name: "Include my_nginx_role"
ansible.builtin.include_role:
name: my_nginx_rolemolecule.ymlInti dari skenario adalah file molecule.yml. Di sinilah kalian menentukan image, platform, provisioner, dan verifier:
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: nginx-ubuntu-2204
image: geerlingguy/docker-ubuntu2204-ansible:latest
pre_build_image: true
privileged: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
provisioner:
name: ansible
playbooks:
converge: converge.yml
verifier:
name: ansibleMari kita bedah bagian pentingnya:
platforms mendefinisikan instance yang akan dibuat. Kalian bisa mendefinisikan beberapa platform sekaligus, misalnya satu Ubuntu dan satu Debian, untuk memastikan role bekerja di banyak sistem operasi.image menentukan image container dasar. Image dari geerlingguy sudah mengandung Ansible dan Python, sehingga pengujian lebih cepat.privileged: true dan volume cgroup dibutuhkan jika role kalian menjalankan service dengan systemd di dalam container.Note
Jika kalian menguji role yang cukup ringan (tanpa systemd), kalian bisa memakai image biasa seperti ubuntu:22.04 dengan pre_build_image: true tanpa privileged. Aturan praktisnya: butuh systemd → pakai image systemd + privileged; tidak butuh systemd → container biasa sudah cukup.
Molecule bekerja berdasarkan lifecycle. Setiap fase memiliki perintahnya sendiri, dan ini adalah bagian yang paling sering dipakai engineer sehari-hari:
molecule createPenjelasan setiap fase:
molecule create — Membuat instance dari image yang didefinisikan di molecule.yml. Kalian bisa memeriksa container yang dibuat dengan docker ps.molecule converge — Menjalankan role ke instance yang sudah ada. Ini fase di mana logika playbook benar-benar dieksekusi.molecule verify — Menjalankan verifier untuk memastikan hasilnya sesuai ekspektasi. Di sini kalian bisa cek apakah service berjalan, paket terinstall, port terbuka, dan sebagainya.molecule destroy — Menghapus instance. Penting dilakukan agar tidak ada container "hantu" yang membuang resource.molecule test — Menjalankan seluruh siklus secara otomatis: create → converge → verify → destroy. Ini perintah yang biasanya dipasang di pipeline CI/CD.Tip
Di proses debugging sehari-hari, jangan langsung pakai molecule test. Lebih efisien menjalankan molecule create lalu molecule converge berulang kali sambil memperbaiki kode, karena instance tetap hidup dan pengujian berulang jadi sangat cepat. Setelah selesai, baru molecule destroy untuk membersihkan.
Rangkuman lifecycle Molecule dalam tabel:
| Fase | Perintah | Fungsi |
|---|---|---|
| Setup | molecule create | Membuat instance dari image |
| Apply | molecule converge | Menjalankan role ke instance |
| Check | molecule verify | Memverifikasi hasil dengan verifier |
| Teardown | molecule destroy | Menghapus instance |
| Full | molecule test | Menjalankan seluruh siklus sekaligus |
Salah satu kekuatan terbesar Molecule adalah idempotency check. Jalankan molecule converge dua kali berturut-turut: pada eksekusi kedua, seluruh task harus melaporkan status ok (bukan changed). Jika ada task yang berubah di eksekusi kedua, berarti role kalian belum idempotent — persis seperti prinsip yang kita pelajari di episode 5, tapi kali ini diuji secara otomatis.
Verifier default Molecule adalah ansible, yang menjalankan playbook verify.yml. Contoh verify.yml yang memeriksa apakah NGINX terinstall dan berjalan:
---
- name: Verify
hosts: all
become: true
gather_facts: false
tasks:
- name: Nginx terinstall
ansible.builtin.package:
name: nginx
state: present
check_mode: true
- name: Service nginx aktif dan enabled
ansible.builtin.service:
name: nginx
state: started
enabled: true
check_mode: trueImportant
Perhatikan check_mode: true pada task verifikasi. Ini adalah pola penting di verify.yml: kita hanya ingin memeriksa, bukan mengubah sesuatu. Dengan check_mode, task hanya melaporkan apakah kondisinya sudah benar tanpa mengeksekusi perubahan apa pun.
Selain verifier Ansible, Molecule juga mendukung Testinfra, framework testing Python yang menulis assertion dalam bentuk fungsi Python yang sangat ekspresif. Untuk mengaktifkannya, ubah bagian verifier di molecule.yml:
verifier:
name: ansible
name: testinfraKemudian tulis test dalam file Python di direktori molecule/default/tests/:
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner.get_hosts(
"all"
)
def test_nginx_is_installed(host):
nginx = host.package("nginx")
assert nginx.is_installed
def test_nginx_service_running_and_enabled(host):
service = host.service("nginx")
assert service.is_running
assert service.is_enabled
def test_nginx_listening_on_port_80(host):
socket = host.socket("tcp://0.0.0.0:80")
assert socket.is_listeningTestinfra lebih populer untuk tim yang nyaman menulis assertion dalam Python, karena fleksibilitasnya jauh lebih tinggi: kalian bisa melakukan manipulasi data, perulangan, dan logika yang rumit tanpa terbentur sintaks YAML. Ini juga selaras dengan kemampuan Python yang sudah kalian pelajari di episode 17.
Tip
Baik verifier ansible maupun testinfra punya tempatnya masing-masing. Untuk assertion sederhana, verify.yml lebih ringkas. Untuk assertion yang kompleks atau berulang (misal memvalidasi daftar 50 package), Testinfra jauh lebih nyaman. Banyak tim senior bahkan memakai keduanya dalam skenario berbeda.
Berikut jebakan yang paling sering ditemui saat pertama kali menggunakan Molecule:
1. Container tanpa systemd. Image container standar tidak menjalankan init system. Jika role kalian memanggil ansible.builtin.systemd_service dengan state: started, task bisa gagal atau tidak berdampak. Solusinya: gunakan image yang menyediakan systemd (seperti geerlingguy/docker-ubuntu2204-ansible) dengan privileged: true dan volume cgroup.
2. Lupa molecule destroy. Instance yang tidak dibersihkan akan menumpuk dan menghabiskan disk/RAM. Biasakan molecule test yang otomatis menghapus instance, atau jalankan molecule destroy secara eksplisit.
3. Perbedaan versi Ansible antara lokal dan CI. Role yang lulus di mesin kalian bisa gagal di CI karena versi ansible-core berbeda. Solusi nyata akan kita bahas di episode 19 dan 20, tapi mulai sekarang biasakan mem-pin versi dependensi.
4. Verifier yang tidak memeriksa apa pun. verify.yml kosong atau assertion yang terlalu longgar memberikan false confidence. Pastikan setiap fitur role memiliki assertion yang sesuai.
5. Menjalankan molecule test di direktori yang salah. Perintah Molecule harus dijalankan dari dalam direktori role (di mana folder molecule/ berada), bukan dari root proyek.
Dengan ansible-lint dan Molecule, workflow pengembangan role kalian menjadi jauh lebih disiplin:
ansible-lint — perbaiki semua pelanggaran error.molecule converge + molecule verify untuk iterasi cepat.molecule test sebagai pengujian lengkap sebelum commit.Alur ini bisa diotomasi penuh dengan pre-commit untuk langkah lint, dan dengan pipeline CI/CD untuk langkah test — persis seperti yang akan kita bahas di episode 19.
Pada episode 18 ini, kita telah belajar bahwa kualitas kode Ansible bukanlah hal yang bisa dibiarkan mengalir begitu saja. Dengan ansible-lint, kalian bisa menegakkan best practice, gaya penulisan, dan keamanan secara otomatis — mulai dari FQCN, task bernama, hingga deteksi password hardcoded. Dengan Molecule, kalian bisa menguji role di lingkungan terisolasi menggunakan Docker/Podman, mengikuti alur create → converge → verify → destroy, dan memverifikasi hasilnya menggunakan playbook Ansible atau test Python Testinfra.
Poin kunci yang perlu kalian bawa pulang:
ansible-lint memeriksa syntax, style, dan keamanan secara statis; ia wajib lolos sebelum kode dikirim.molecule test menjalankan seluruh siklus otomatis dan sangat cocok untuk dipasang di CI/CD.ansible dan testinfra sama-sama valid, pilih sesuai kompleksitas assertion.Di episode 19 selanjutnya kita akan membawa kedua alat ini ke level berikutnya dengan topik Integrasi CI/CD Pipeline & IaC Paradigm. Kita akan membuat pipeline GitHub Actions dan GitLab CI yang otomatis menjalankan linting, testing, dan deployment, serta belajar bagaimana Ansible berkolaborasi dengan Terraform dalam alur provisioning dan configuration management. Pastikan tetap semangat!