Struktur #
Dalam dunia otomasi infrastruktur dengan Ansible, konsistensi adalah kunci utama untuk skalabilitas. Salah satu keindahan Ansible adalah bagaimana ia memaksa kita untuk mengorganisasi kode melalui konvensi struktur direktori yang terstandarisasi. Ketika kita pertama kali melihat struktur folder sebuah role, kita mungkin merasa bahwa ada terlalu banyak subfolder yang harus dikelola. Namun, setiap subfolder tersebut dirancang dengan tujuan yang sangat spesifik untuk mendukung prinsip pemisahan tanggung jawab (separation of concerns).
Memahami anatomi struktur direktori role bukan hanya tentang menghafal nama-nama foldernya, melainkan tentang memahami bagaimana Ansible memanfaatkan folder-folder ini untuk mencari aset secara otomatis saat playbook dijalankan. Dengan struktur yang terorganisasi dengan baik, tim pengembang yang berbeda dapat berkolaborasi pada repositori otomasi yang sama tanpa kebingungan, karena semua orang tahu persis di mana harus mencari task, template, file statis, atau variabel.
Eksplorasi Subdirektori Role Secara Mendalam #
Mari kita telusuri setiap subdirektori standar yang ada di dalam sebuah role. Kita akan membahas secara mendalam apa yang harus disimpan di sana, aturan penamaan yang direkomendasikan, serta bagaimana komponen-komponen ini berinteraksi satu sama lain.
1. Direktori tasks/
#
Direktori tasks/ adalah otak dari setiap role. Di sinilah kita mendefinisikan langkah-langkah konkret yang harus dijalankan pada managed node.
main.yml: Ini adalah file wajib di dalam direktoritasks/. File ini bertindak sebagai pintu masuk (entry point) eksekusi.- Sub-file (Modularisasi Task): Untuk role yang kompleks, sangat disarankan untuk memecah task menjadi beberapa file terpisah seperti
install.ymluntuk instalasi package,configure.ymluntuk pengaturan file konfigurasi, danservice.ymluntuk mengelola siklus hidup service. Kita menggabungkan file-file ini ke dalammain.ymlmenggunakanimport_tasksatauinclude_tasks. - Praktik Terbaik: Hindari menulis task yang terlalu generik. Berikan nama yang deskriptif pada setiap task agar log eksekusi Ansible mudah dibaca.
2. Direktori handlers/
#
Handler adalah task khusus yang hanya berjalan ketika dipicu (notified) oleh task lain yang mengalami status perubahan (changed). Direktori handlers/ menyimpan file main.yml yang berisi definisi semua handler untuk role tersebut.
- Fungsi Utama: Paling sering digunakan untuk me-restart atau me-reload service sistem setelah file konfigurasi diperbarui.
- Mekanisme Penggabungan: Kita menggunakan fitur
listenpada handler modern untuk mengelompokkan beberapa tindakan di bawah satu event generator, sehingga kode kita menjadi lebih bersih dan modular.
3. Direktori templates/
#
Direktori templates/ menampung file-file template yang menggunakan mesin templating Jinja2. File di dalam direktori ini biasanya memiliki ekstensi .j2.
- Prinsip Kerja: Ansible akan membaca file template, mengevaluasi variabel-variabel Jinja2 yang ada di dalamnya berdasarkan context environment saat runtime, lalu menghasilkan file output yang telah dirender pada managed node melalui module
template. - Aturan Penamaan: Selalu namai file template Anda sesuai dengan nama file target di managed node dengan akhiran
.j2. Contoh:nginx.conf.j2untuk menghasilkan file/etc/nginx/nginx.conf.
4. Direktori files/
#
Berbeda dengan templates/, direktori files/ digunakan untuk menyimpan file statis yang ingin kita salin langsung ke managed node tanpa modifikasi apa pun.
- Kapan Menggunakannya: Gunakan untuk menyalin gambar, aset web statis (seperti
favicon.icoatau file HTML statis), file konfigurasi biner, atau skrip pembantu (seperti skrip bash) yang tidak memerlukan pengisian variabel dinamis. - Keuntungan Performa: Proses transfer file statis menggunakan module
copylebih cepat daripada memproses template Jinja2 karena tidak memerlukan parsing overhead pada control node.
5. Direktori defaults/
#
Direktori defaults/ berisi file main.yml yang mendefinisikan variabel-variabel dengan prioritas paling rendah.
- Peran Kunci: Ini adalah “API” dari role kita. Kita mendokumentasikan semua variabel yang dapat dikustomisasi oleh pengguna role di sini, lengkap dengan nilai bawaan yang aman.
- Tingkat Override: Karena prioritasnya yang rendah, variabel di sini dapat dengan mudah ditimpa melalui inventory, group vars, atau langsung di dalam playbook.
6. Direktori vars/
#
Direktori vars/ berisi file main.yml yang mendefinisikan variabel-variabel internal role dengan prioritas tinggi.
- Peran Kunci: Digunakan untuk menyimpan data internal yang bersifat konstan untuk role tersebut, seperti path instalasi default untuk sistem operasi yang berbeda atau daftar dependensi paket yang spesifik.
- Keamanan: Menjaga agar pengguna role tidak menimpa variabel penting secara tidak sengaja dari luar role.
7. Direktori meta/
#
Direktori meta/ berisi file main.yml yang mendefinisikan metadata dari role.
- Informasi Metadata: Berisi nama pembuat (author), lisensi (misalnya BSD atau MIT), platform sistem operasi yang didukung (misalnya Ubuntu, CentOS), dan kategori Galaxy.
- Dependensi: Di sini kita juga mendeklarasikan role lain yang menjadi prasyarat (dependency) bagi role ini agar berjalan dengan benar.
8. Direktori tests/
#
Direktori tests/ digunakan untuk menyimpan file-file pengujian guna memastikan role kita bekerja seperti yang diharapkan.
- Isi Standar: Biasanya berisi file
inventorytiruan dan playbook pengujiantest.ymlyang memanggil role tersebut dengan parameter default. - Molecule: Dalam pengembangan modern, direktori ini sering digunakan oleh framework Molecule untuk melakukan pengujian otomatis (automated testing) menggunakan container Docker.
Aturan Lookup Otomatis Relatif Terhadap Path Role #
Salah satu fitur paling cerdas dari Ansible saat menjalankan role adalah mekanisme pencarian path otomatis (automatic path lookup). Ketika kita menggunakan modul seperti copy, template, script, include_tasks, atau bahkan vars_files di dalam sebuah task role, kita tidak perlu menulis path absolut atau path relatif yang panjang dari root proyek. Ansible secara cerdas akan mencari aset tersebut di dalam subdirektori role yang sesuai.
Mari kita pelajari aturan resolusi pencarian path Ansible secara berurutan:
-
Pencarian Lokal di Dalam Role:
- Jika kita memanggil modul
copydengan parametersrc: website.html, Ansible pertama-tama akan mencari file tersebut di folderfiles/milik role yang sedang berjalan (roles/<role_name>/files/website.html). - Jika kita memanggil modul
templatedengan parametersrc: vhost.conf.j2, Ansible akan mencari diroles/<role_name>/templates/vhost.conf.j2.
- Jika kita memanggil modul
-
Pencarian Relatif Terhadap Playbook:
- Jika file tidak ditemukan di dalam direktori internal role, Ansible akan naik satu tingkat dan mencari di direktori yang relatif terhadap lokasi file playbook utama (
.yml) yang sedang kita jalankan.
- Jika file tidak ditemukan di dalam direktori internal role, Ansible akan naik satu tingkat dan mencari di direktori yang relatif terhadap lokasi file playbook utama (
-
Pencarian di Jalur Pencarian Global:
- Jika masih tidak ditemukan, Ansible akan memeriksa jalur pencarian global yang didefinisikan dalam konfigurasi
ansible.cfgkita.
- Jika masih tidak ditemukan, Ansible akan memeriksa jalur pencarian global yang didefinisikan dalam konfigurasi
Untuk memperjelas alur kerja resolusi pencarian ini, mari kita perhatikan diagram flowchart berikut:
flowchart TD
A["Mulai: Task memanggil aset (misal: src: 'config.conf')"] --> B{"Apakah task dijalankan di dalam sebuah Role?"}
B -- "Ya" --> C{"Apakah aset ada di folder internal Role? (files/ atau templates/)"}
C -- "Ya" --> D["Gunakan aset dari direktori internal Role"]
C -- "Tidak" --> E{"Apakah aset ada di direktori relatif terhadap Playbook utama?"}
B -- "Tidak" --> E
E -- "Ya" --> F["Gunakan aset dari direktori relatif Playbook"]
E -- "Tidak" --> G{"Apakah aset ada di jalur global (ansible.cfg)?"}
G -- "Ya" --> H["Gunakan aset dari jalur global"]
G -- "Tidak" --> I["Error: Aset tidak ditemukan! Eksekusi dihentikan."]
Konsekuensi Praktis dari Aturan Lookup #
Mekanisme lookup otomatis ini membawa konsekuensi penting dalam penulisan kode kita:
- Portabilitas Tinggi: Kita dapat memindahkan direktori role ke repositori lain atau mengubah lokasi penempatan role di sistem file tanpa perlu memperbarui path pencarian aset di dalam task role kita.
- Namespace Terisolasi: File statis dengan nama yang sama (misalnya
nginx.conf) di dalam rolenginxtidak akan bentrok dengan filenginx.confdi dalam rolecommonkarena Ansible akan memprioritaskan folder lokal role masing-masing terlebih dahulu.
Strategi Penggunaan Templates dan Files Secara Optimal #
Salah satu keputusan desain yang paling sering kita hadapi saat membangun role adalah: apakah kita harus menyimpan konfigurasi di files/ atau di templates/?
Meskipun keduanya tampak serupa karena sama-sama berfungsi untuk mengirimkan berkas ke managed node, pencampuran penggunaan keduanya tanpa strategi yang jelas dapat membuat role kita menjadi kaku atau sulit dikustomisasi.
Mari kita bahas strategi penentuan yang optimal:
1. Kapan Menggunakan files/ (File Statis)
#
Gunakan direktori files/ hanya jika berkas yang dikirimkan bersifat 100% statis dan identik di seluruh host target, tanpa memandang variasi sistem operasi, environment (development vs production), atau variabel kustom.
- Contoh Kasus:
- Berkas kunci publik SSH (
authorized_keys). - Logo perusahaan atau aset gambar untuk halaman web default.
- Skrip pemeliharaan sistem (skrip bash/python) yang parameternya dikontrol via argumen command-line, bukan hardcoded di dalam skrip.
- Sertifikat SSL root CA publik yang didistribusikan ke seluruh server.
- Berkas kunci publik SSH (
2. Kapan Menggunakan templates/ (File Dinamis Jinja2)
#
Gunakan direktori templates/ jika isi berkas perlu beradaptasi secara dinamis berdasarkan kondisi mesin target atau preferensi konfigurasi pengguna.
- Contoh Kasus:
- File konfigurasi layanan (seperti
nginx.confataupostgresql.conf) yang perlu menyesuaikan jumlahworker_processesdengan jumlah CPU fisik managed node (ansible_processor_vcpus). - Konfigurasi aplikasi yang memuat kredensial database unik untuk setiap environment.
- File konfigurasi virtual host yang nama domainnya diambil dari variabel playbook.
- File konfigurasi layanan (seperti
Mari kita bandingkan perbedaan implementasi taktis dari kedua pendekatan ini melalui contoh kode berikut:
# roles/webserver/tasks/configure.yml
# BENAR: Menggunakan file statis untuk aset statis dan template untuk konfigurasi dinamis
---
- name: Salin file HTML statis default ke server web
copy:
src: welcome_page.html # Diambil otomatis dari roles/webserver/files/welcome_page.html
dest: /var/www/html/index.html
owner: www-data
mode: '0644'
- name: Render template konfigurasi Nginx secara dinamis
template:
src: nginx.conf.j2 # Diambil otomatis dari roles/webserver/templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
mode: '0644'
Custom Plugins: Memperluas Fungsionalitas dengan Library dan Lookup Plugins #
Kadang-kadang, modul bawaan Ansible tidak cukup untuk menangani tugas otomasi kita yang sangat spesifik. Misalnya, kita mungkin perlu berinteraksi dengan API internal perusahaan yang rumit atau melakukan transformasi data kustom yang sulit dilakukan hanya menggunakan filter Jinja2 bawaan.
Ansible memungkinkan kita untuk menyertakan custom plugin dan custom module langsung di dalam role kita. Ini adalah fitur yang sangat luar biasa karena membuat role kita menjadi benar-benar mandiri (self-contained).
1. Custom Module di Direktori library/
#
Jika kita menempatkan file Python di dalam direktori library/ milik role kita (misalnya library/my_custom_api.py), Ansible secara otomatis akan memuat file tersebut sebagai modul yang dapat dipanggil langsung di dalam task role kita.
# roles/myapp/tasks/main.yml
# BENAR: Memanggil custom module yang ada di direktori library/ role kita
---
- name: Berinteraksi dengan API internal menggunakan custom module
my_custom_api:
api_endpoint: "https://api.internal.corp"
action: "register_node"
node_name: "{{ ansible_hostname }}"
register: api_result
2. Custom Lookup Plugins di Direktori lookup_plugins/
#
Lookup plugin digunakan untuk mengambil data dari sumber eksternal pada control node. Dengan menempatkan plugin kustom di direktori lookup_plugins/, kita bisa menulis logika pencarian data kustom kita sendiri menggunakan python, seperti membaca rahasia dari password manager internal.
# roles/myapp/tasks/main.yml
# BENAR: Menggunakan custom lookup plugin dari direktori lookup_plugins/
---
- name: Ambil kredensial database dari password manager internal
set_fact:
db_password: "{{ lookup('internal_vault', 'db_prod_password_key') }}"
Dengan memanfaatkan direktori-direktori kustom ini, kita tidak perlu meminta administrator sistem untuk menginstal modul tambahan secara global di control node. Cukup distribusikan role kita, dan semua fungsionalitas kustom akan langsung berfungsi.
Studi Kasus: Anti-Pattern vs Best Practice Struktur Direktori #
Untuk memperdalam pemahaman kita tentang bagaimana mengorganisasi struktur direktori role secara profesional, mari kita pelajari perbandingan studi kasus nyata antara struktur direktori yang buruk (anti-pattern) dengan struktur direktori yang baik (best practice).
1. Struktur Direktori yang Buruk (Anti-Pattern) #
Bayangkan sebuah tim yang terburu-buru membuat role untuk aplikasi web mereka. Mereka tidak membagi file dengan benar dan mencampuradukkan berbagai jenis aset di tempat yang salah.
# ANTI-PATTERN: Struktur direktori role yang berantakan dan sulit dipelihara
roles/bad_app/
├── tasks/
│ └── main.yml # Berisi 300 baris task campuran (install, config, deploy)
├── files/
│ ├── app.conf # Hardcoded file konfigurasi (sulit dikustomisasi)
│ ├── setup_script.sh # Skrip bash raksasa yang seharusnya bisa ditulis pakai module Ansible
│ └── db_password.txt # File rahasia disimpan mentah (security vulnerability)
└── defaults/
└── main.yml # Kosong (tidak ada API variabel untuk pengguna role)
Mengapa ini buruk?
- Monolitik: Semua task ditumpuk di satu file
tasks/main.yml. Sangat sulit melacak kegagalan atau melakukan debugging. - Kaku: Menggunakan file statis
app.confdi folderfiles/yang berisi parameter yang di-hardcode. Jika kita ingin mengubah nama database di server production, kita harus mengedit file ini langsung, merusak konsistensi kode. - Bahaya Keamanan: Menyimpan file rahasia seperti
db_password.txtsecara polos tanpa enkripsi Ansible Vault. - Logika Tersembunyi: Mengandalkan skrip bash luar (
setup_script.sh) untuk melakukan konfigurasi daripada memanfaatkan keunggulan deklaratif modul bawaan Ansible.
2. Struktur Direktori yang Baik (Best Practice) #
Sekarang, mari kita lihat bagaimana kita mendesain ulang role di atas dengan menerapkan semua prinsip struktur direktori Ansible yang baik.
# BENAR: Struktur direktori role yang rapi, tersegregasi, dan fleksibel
roles/good_app/
├── tasks/
│ ├── main.yml # Orkestrator utama yang bersih
│ ├── install.yml # Khusus instalasi package apt/yum
│ ├── configure.yml # Khusus rendering template konfigurasi
│ └── deploy.yml # Khusus penarikan repositori kode git
├── templates/
│ └── app.conf.j2 # Konfigurasi dinamis berbasis variabel Jinja2
├── defaults/
│ └── main.yml # Berisi daftar lengkap variabel default & dokumentasi
├── vars/
│ └── main.yml # Menyimpan konstanta internal sistem (seperti path direktori)
├── handlers/
│ └── main.yml # Handler terpusat untuk restart service aplikasi
└── README.md # Panduan lengkap parameter variabel dan cara pakai
Mengapa ini sangat baik?
- Pemisahan Logika yang Jelas: Setiap file task di folder
tasks/hanya bertanggung jawab pada satu fase siklus hidup aplikasi. - Sangat Fleksibel: File konfigurasi didefinisikan sebagai template
app.conf.j2. Semua parameter sensitif seperti database host atau port dibaca dari variabel yang nilainya bisa kita atur melalui inventory atau Ansible Vault secara aman. - Dokumentasi Hidup: File
defaults/main.ymlmendokumentasikan dengan jelas apa saja variabel yang bisa di-override oleh pengguna role. - Kemudahan Integrasi: Dilengkapi dengan handler terpusat untuk memastikan restart service hanya terjadi ketika file konfigurasi mengalami perubahan nyata, menghemat waktu downtime server.
Ringkasan #
- Setiap subdirektori role memiliki peran spesifik:
tasks/untuk otomasi logika,handlers/untuk aksi reaktif,templates/untuk konfigurasi dinamis, danfiles/untuk berkas statis.- Ansible menerapkan automatic path lookup yang memprioritaskan direktori internal role (
files/dantemplates/) sebelum mencari ke direktori relatif playbook utama.- Gunakan
templates/dengan Jinja2.j2untuk file yang membutuhkan parameter dinamis, dan batasifiles/hanya untuk aset yang benar-benar statis di semua lingkungan.- Kita dapat memperluas kapabilitas role secara mandiri dengan menyertakan custom python module di direktori
library/dan custom lookup plugin di direktorilookup_plugins/.- Hindari anti-pattern seperti menumpuk semua task di satu file raksasa atau menyimpan kredensial sensitif secara polos di folder files role.