Struktur File Ansible untuk Instalasi Zimbra Single Server

Beberapa waktu terakhir saya iseng oprek oprek menyusun ulang project Ansible saya untuk instalasi Zimbra single server. Alasannya sederhana, dulu saya buat struktur seadanya, satu playbook panjang, variabel campur aduk, dan ujungnya susah dipakai ulang. Jadi di postingan ini saya bagikan bagaimana saya menata file dan folder Ansible untuk instalasi Zimbra single server, lengkap dengan alasan kenapa tiap bagian saya pisah seperti itu.

Postingan ini fokus pada strukturnya saja, bukan isi task detail per file. Kalau ada bagian yang mau saya dalami di tulisan terpisah, silakan kasih tahu saya di komentar.

Gambaran umum

Begini struktur akhir yang saya pakai sekarang.

tree zimbra-ansible

Output tree zimbra-ansible/ di terminal saya jadikan screenshot pertama, biar pembaca punya gambaran utuh sebelum saya bahas tiap bagian.

ansible.cfg

File ansible.cfg di root project saya pakai untuk override default Ansible khusus project ini, jadi tidak perlu menyentuh konfigurasi global di /etc/ansible/ansible.cfg. Isinya kira-kira soal lokasi inventory, path role, retry files yang saya matikan, dan beberapa setting koneksi SSH seperti host_key_checking = False saat lab.

Manfaat utama menempatkan ansible.cfg di project, ketika ada anggota tim lain yang clone repository, mereka langsung dapat konfigurasi yang sama tanpa perlu setup global. Praktik komunitas memang menyarankan project-level config untuk repository yang akan dibagi.

Inventory dan group_vars

Folder inventory/ di project saya isinya hanya hosts.ini. File ini berisi daftar host beserta pengelompokannya. Untuk Zimbra single server isinya simpel, hanya satu host di bawah grup zimbra_servers. Walaupun cuma satu mesin, saya tetap pakai grup karena memudahkan kalau suatu hari struktur berkembang ke multi-server.

Folder group_vars/ saya letakkan di root project, bukan di dalam inventory/. Ansible mendukung dua pola ini, dan saya pilih yang di root karena lebih mudah dilihat dan diedit. File all.yml di dalamnya menyimpan variabel yang berlaku untuk semua host, misalnya zimbra_version, zimbra_domain, zimbra_admin_email, timezone, dan URL download installer.

Untuk skala single server, satu file all.yml sudah cukup. Kalau nanti host bertambah dan tiap grup butuh variabel berbeda, tinggal saya tambah file seperti group_vars/zimbra_servers.yml. Begitu pula host_vars/ belum saya buat karena belum perlu, tapi sudah siap saya tambahkan ketika ada host yang butuh override individual.

site.yml

File site.yml di root saya pakai sebagai entry point utama playbook. Isinya menarget grup zimbra_servers dan memanggil role zimbra_single. Di sini saya juga set become: true dan gather_facts: true, ditambah beberapa pre_tasks ringan seperti update cache paket atau memastikan Python tersedia di target.

Untuk project sekecil ini saya belum pecah site.yml ke beberapa playbook di folder playbooks/. Kalau besok saya tambah role lain misalnya monitoring atau backup, baru saya pisah jadi playbooks/zimbra.yml, playbooks/monitoring.yml, lalu site.yml saya jadikan orchestrator yang berisi import_playbook saja.

Role zimbra_single

Bagian paling besar adalah role itu sendiri. Saya namai zimbra_single karena memang khusus untuk topologi single server. Nantinya kalau saya bikin multi-server, saya buat role terpisah seperti zimbra_mta atau zimbra_proxy agar tanggung jawab masing-masing role jelas, ketimbang memaksa satu role besar mengakomodasi semuanya dengan logika percabangan yang ribet.

defaults dan vars

Saya isi defaults/main.yml dengan variabel yang user role boleh ubah, misalnya path download installer, nama user dan grup Zimbra, dan daftar komponen yang diinstall. Sifatnya nilai default, prioritas paling rendah, jadi mudah di-override dari group_vars atau host_vars.

Folder vars/ saya sengaja biarkan kosong untuk sekarang. Bedanya dengan defaults, isi vars/main.yml itu prioritasnya tinggi dan susah di-override tanpa sengaja. Saya rencanakan untuk konstanta internal role yang tidak boleh diutak-atik user, misalnya nama service Zimbra atau path binary zmcontrol. Karena belum ada kebutuhan konkret, saya biarkan dulu agar tidak menambah noise.

tasks

Folder tasks/ adalah jantung role. File main.yml saya buat tipis, isinya hanya include_tasks ke file-file lain berdasarkan urutan eksekusi. Pemecahan ini disengaja agar tiap file punya tanggung jawab tunggal dan mudah dibaca.

File preflight.yml melakukan pengecekan awal sebelum instalasi dimulai. Cek RAM minimum, free disk space, versi OS yang didukung, dan resolusi DNS hostname. Kalau salah satu gagal, playbook stop di sini sebelum sempat merusak apa-apa. Ini investasi waktu yang worth it karena Zimbra cukup picky soal prasyarat.

File os_debian.yml dan os_redhat.yml saya pecah berdasarkan OS family. Isinya menangani persiapan OS yang spesifik per distro, mulai dari instalasi paket dependensi, disable service yang bentrok seperti postfix atau sendmail bawaan, sampai konfigurasi firewall (UFW untuk Debian, firewalld untuk RHEL). Saya pilih pendekatan pecah file per OS ketimbang satu file panjang dengan when: ansible_os_family == "...", karena lebih bersih saat dibaca dan lebih mudah dirawat kalau salah satu distro butuh logika kompleks. Pemanggilannya tetap pakai when di main.yml, jadi hanya satu file yang dijalankan tergantung OS target.

File download.yml mengunduh tarball installer Zimbra dari URL yang didefinisikan di variabel, lalu mengekstraknya ke direktori kerja. Saya tambahkan verifikasi checksum SHA256 di sini untuk memastikan file tidak korup atau diganti di tengah jalan.

File install.yml menjalankan installer dalam mode unattended menggunakan config file. Ini bagian paling tricky karena installer Zimbra interaktif by default. Saya pakai template zinstall.cfg.j2 untuk menghasilkan config file yang dibaca oleh install.sh -s -c. Nilai yang ditemplate antara lain admin password, domain, time zone, hostname, dan komponen yang dipilih.

File config.yml berisi konfigurasi setelah instalasi selesai. Set admin password lewat zmprov, atur quota default, konfigurasi domain dan COS, restart service kalau perlu, dan hal-hal lain yang sifatnya tuning post-install. Saya konsolidasikan ke satu file karena task-nya saling terkait dan dijalankan beruntun. Kalau ke depan jumlahnya membengkak, baru saya pecah lagi misalnya jadi ssl.yml, quota.yml, dan seterusnya.

handlers

Folder handlers/main.yml menyimpan handler yang dipanggil pakai notify. Untuk Zimbra saya punya handler restart zimbra yang menjalankan zmcontrol restart sebagai user zimbra, dan reload firewall. Pemakaian handler ini menjaga idempotensi, restart hanya terjadi kalau memang ada perubahan, bukan setiap kali playbook dijalankan.

templates

File-file di templates/ berekstensi .j2 karena diproses Jinja2. Saya punya dua, hosts.j2 untuk men-generate /etc/hosts yang Zimbra-friendly (urutan baris dan FQDN harus benar, kalau tidak installer langsung gagal), dan zinstall.cfg.j2 untuk config file installer Zimbra yang dipakai mode unattended.

Praktik yang saya ikuti, semua nilai yang berubah-ubah saya parameterkan lewat variabel, bukan hardcode di template. Misalnya {{ zimbra_admin_password }}, {{ zimbra_domain }}, {{ ansible_fqdn }}. Dengan begini template bisa dipakai ulang untuk berbagai environment tanpa harus diedit ulang.

requirements.yml

Di root project saya punya requirements.yml yang mendaftarkan collection eksternal yang dipakai role, misalnya community.general dan ansible.posix. Anggota tim lain yang clone repo cukup jalankan ansible-galaxy collection install -r requirements.yml dan dependency mereka beres.

Praktik ini termasuk yang sering dilupakan tapi sangat penting. Tanpa requirements.yml, playbook bisa error karena modul tidak ketemu, dan debugging-nya menyebalkan, apalagi kalau yang menjalankan bukan kalian sendiri.

Hal yang belum saya tambahkan

Ada beberapa hal yang belum ada di struktur sekarang tapi sebenarnya layak dipertimbangkan ke depan.

Pertama, Ansible Vault untuk menyimpan secret seperti password admin Zimbra dan password LDAP. Pola yang biasa dipakai, buat file group_vars/vault.yml yang dienkripsi pakai ansible-vault encrypt, lalu variabel di dalamnya diberi prefix vault_. Di file plaintext tinggal dibuat alias seperti zimbra_admin_password: "{{ vault_zimbra_admin_password }}". Untuk lab saya masih pakai variabel plaintext, tapi sebelum dipakai di environment yang lebih serius, vault wajib ditambahkan.

Kedua, folder meta/ di dalam role yang berisi metadata seperti author, license, dan platform yang didukung. Kalau suatu hari role ini saya publish ke Ansible Galaxy atau saya share ke tim lain, file ini yang dibaca pertama kali. Tidak urgent untuk lab, tapi bagus dimiliki.

Ketiga, README di dalam role itu sendiri. README di root sudah ada, tapi role yang reusable idealnya punya README sendiri yang mendokumentasikan variabel yang tersedia dan contoh pemakaian.

Penutup

Struktur di atas bukan satu-satunya cara, tapi cukup mewakili praktik yang umum dipakai komunitas Ansible. Inti yang saya pegang ada empat, pisahkan inventory dari role agar reusable, taruh variabel sesuai hierarki, pecah tasks per tanggung jawab, dan jangan pernah commit secret tanpa enkripsi.

Kalau kalian baru mulai dengan Ansible, saran saya jangan langsung niru struktur lengkap. Mulai dari yang sederhana, satu playbook, satu inventory, satu role. Begitu kompleksitas naik, baru pelan-pelan pecah sesuai kebutuhan. Struktur yang baik itu yang menjawab masalah nyata, bukan struktur yang rapi tapi over-engineered untuk skala kecil.

Kalau ada bagian yang ingin saya bahas lebih detail, misalnya isi zinstall.cfg.j2 lengkap atau task instalasi Zimbra step by step, kasih tahu saya di komentar.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *