Reusability #
Membuat role yang benar-benar reusable lebih sulit dari yang terlihat. Mudah membuat role yang bekerja di satu proyek — tapi membuat role yang bisa dengan mudah digunakan di proyek lain, oleh tim lain, dengan infrastruktur yang berbeda, membutuhkan pertimbangan desain yang lebih dalam. Artikel ini membahas prinsip-prinsip yang membuat role benar-benar reusable, bukan hanya “bisa dipakai ulang kalau situasinya persis sama”.
Masalah Role yang Tidak Reusable #
Role yang tidak reusable biasanya punya satu atau lebih dari ciri berikut:
# ANTI-PATTERN: role yang hardcode nilai spesifik
# roles/nginx/tasks/configure.yml
- name: Deploy konfigurasi nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
# roles/nginx/templates/nginx.conf.j2
server {
listen 80;
server_name app.mycompany.com; # ← Hardcoded! Tidak bisa digunakan proyek lain
root /opt/myapp/public; # ← Hardcoded! Path spesifik proyek ini
location / {
proxy_pass http://127.0.0.1:8080; # ← Hardcoded! Port spesifik
}
}
Role ini hanya bekerja untuk app.mycompany.com dengan path /opt/myapp/public. Untuk proyek lain, kamu harus memodifikasi role — yang berarti versi role di proyek pertama dan kedua bisa berbeda dan tidak sync.
Prinsip 1: Semua Nilai Spesifik Harus Jadi Variabel #
Setiap nilai yang mungkin berbeda antar proyek harus bisa dikustomisasi melalui variabel:
# BENAR: semua nilai spesifik jadi variabel
# roles/nginx/defaults/main.yml
nginx_server_name: "{{ inventory_hostname }}" # Default: hostname server
nginx_document_root: /var/www/html
nginx_upstream_host: 127.0.0.1
nginx_upstream_port: 8080
{# roles/nginx/templates/nginx.conf.j2 #}
server {
listen {{ nginx_port }};
server_name {{ nginx_server_name }};
root {{ nginx_document_root }};
location / {
proxy_pass http://{{ nginx_upstream_host }}:{{ nginx_upstream_port }};
}
}
Sekarang role ini bisa digunakan untuk app.proyek1.com, api.proyek2.id, atau domain apapun — cukup override variabelnya.
Prinsip 2: Beri Default yang Masuk Akal #
Default yang baik membuat role langsung bisa digunakan tanpa harus mengatur semua variabel:
# roles/nginx/defaults/main.yml
# Default yang masuk akal untuk hampir semua skenario
nginx_port: 80
nginx_worker_processes: "auto" # Otomatis sesuai jumlah CPU
nginx_worker_connections: 1024
nginx_client_max_body_size: "10m"
nginx_keepalive_timeout: 65
nginx_ssl_enabled: false # SSL tidak aktif secara default
nginx_document_root: /var/www/html
# Default yang bergantung pada konteks host
nginx_server_name: "{{ ansible_fqdn }}" # Gunakan FQDN host sebagai default
Dengan default seperti ini, kamu bisa menggunakan role tanpa mengatur satu variabel pun — dan hasilnya tetap masuk akal.
Prinsip 3: Pisahkan Concern dengan Jelas #
Role yang reusable punya batas tanggung jawab yang jelas. Role nginx seharusnya hanya mengurus nginx — tidak mengurus konfigurasi firewall, tidak mengurus monitoring, tidak mengurus deployment aplikasi.
Role nginx:
✓ Install nginx
✓ Konfigurasi nginx (worker, timeout, dll.)
✓ Setup virtual host
✓ Setup SSL/TLS
✗ Setup firewall — itu tanggung jawab role firewall
✗ Deploy kode aplikasi — itu tanggung jawab role myapp
✗ Setup monitoring — itu tanggung jawab role monitoring
Role yang mencoba melakukan terlalu banyak hal sulit digunakan kembali karena pengguna mungkin hanya butuh sebagian dari fungsinya.
Prinsip 4: Idempoten di Semua Kondisi #
Role yang reusable harus idempoten — aman dijalankan berulang kali di sistem yang sudah dikonfigurasi maupun yang baru:
# ANTI-PATTERN: tidak idempoten
- name: Buat direktori dan set permission
command: mkdir -p /opt/app && chmod 755 /opt/app
# Ini akan selalu "changed" meski direktori sudah ada dengan permission yang benar
# BENAR: idempoten
- name: Pastikan direktori aplikasi ada
file:
path: /opt/app
state: directory
mode: '0755'
owner: "{{ app_user }}"
# Ansible cek kondisi — hanya berubah jika memang perlu berubah
Prinsip 5: Kemampuan Kustomisasi melalui Task Tags #
Role yang reusable memberi pengguna kemampuan untuk menjalankan hanya subset dari task-nya:
# roles/nginx/tasks/main.yml
---
- import_tasks: install.yml
tags:
- nginx
- nginx:install
- import_tasks: configure.yml
tags:
- nginx
- nginx:configure
- import_tasks: ssl.yml
when: nginx_ssl_enabled
tags:
- nginx
- nginx:ssl
Pengguna role bisa menjalankan hanya bagian yang diperlukan:
# Hanya install, skip konfigurasi
ansible-playbook site.yml --tags nginx:install
# Hanya update konfigurasi tanpa install ulang
ansible-playbook site.yml --tags nginx:configure
# Semua task nginx
ansible-playbook site.yml --tags nginx
Menguji Role Secara Independen #
Role yang bisa diuji secara independen jauh lebih mudah di-maintain dan di-share. Buat direktori pengujian sederhana di dalam role:
roles/nginx/
├── tasks/
├── defaults/
├── ...
└── tests/
├── inventory.ini # Inventory minimal untuk testing
└── test.yml # Playbook untuk menguji role ini
# roles/nginx/tests/test.yml
---
- name: Test role nginx
hosts: all
become: true
pre_tasks:
- name: Perbarui apt cache
apt:
update_cache: true
when: ansible_os_family == "Debian"
roles:
- role: "{{ playbook_dir }}/../" # Gunakan role dari direktori parent
vars:
nginx_port: 8080
nginx_server_name: "test.local"
post_tasks:
- name: Verifikasi nginx berjalan
systemd:
name: nginx
register: nginx_status
- name: Assert nginx aktif
assert:
that:
- nginx_status.status.ActiveState == "active"
fail_msg: "Nginx tidak berjalan setelah role dieksekusi"
Ringkasan #
- Semua nilai spesifik harus jadi variabel — tidak boleh ada hardcode path, domain, port, atau nilai lain yang berbeda antar proyek.
- Beri default yang masuk akal sehingga role bisa langsung digunakan tanpa mengatur semua variabel — tapi tetap fleksibel.
- Pisahkan concern — role nginx hanya mengurus nginx, tidak mencampuri tanggung jawab role lain.
- Idempoten di semua kondisi — aman dijalankan di sistem baru maupun yang sudah dikonfigurasi.
- Task tags memungkinkan pengguna menjalankan hanya subset dari role — install saja, atau konfigurasi saja, tanpa harus modifikasi role.
- Uji role secara independen dengan direktori
tests/di dalam role — memastikan role bekerja sebelum di-share atau diintegrasikan.