Template (Jinja2) #
Template adalah cara Ansible menghasilkan file konfigurasi yang berbeda per host atau per lingkungan dari satu sumber yang sama. Sebuah template nginx yang sama bisa menghasilkan konfigurasi untuk server staging (port 80, debug mode on) maupun production (port 443, SSL enabled, debug mode off) — hanya dengan mengubah nilai variabelnya. Ansible menggunakan Jinja2 sebagai mesin template, bahasa yang sama yang digunakan oleh Flask dan Django.
Sintaks Dasar Jinja2 #
Jinja2 menggunakan tiga jenis sintaks:
{# Ini adalah komentar — tidak muncul di output #}
{{ variabel }} {# Substitusi variabel #}
{% if kondisi %} {# Blok kontrol (if, for, dll.) #}
...
{% endif %}
Contoh template sederhana:
{# templates/app.conf.j2 #}
[server]
host = {{ ansible_default_ipv4.address }}
port = {{ app_port }}
workers = {{ app_workers | default(4) }}
environment = {{ env }}
[database]
host = {{ db_host }}
port = {{ db_port | default(5432) }}
name = {{ db_name }}
Filter Jinja2 yang Paling Berguna #
Filter mengubah nilai variabel sebelum ditampilkan. Format: {{ nilai | filter }}:
{# Default value jika variabel tidak terdefinisi #}
{{ app_port | default(8080) }}
{# Konversi tipe #}
{{ some_number | int }}
{{ some_value | string }}
{{ some_value | bool }}
{{ some_list | list }}
{# Manipulasi string #}
{{ app_name | upper }} {# APP_NAME #}
{{ app_name | lower }} {# app_name #}
{{ " nginx " | trim }} {# nginx #}
{{ "/etc/app" | basename }} {# app #}
{{ "/etc/app/config.conf" | dirname }} {# /etc/app #}
{# Manipulasi list #}
{{ packages | join(', ') }} {# package1, package2, package3 #}
{{ servers | length }} {# jumlah item #}
{{ servers | first }} {# item pertama #}
{{ servers | last }} {# item terakhir #}
{{ servers | sort }} {# diurutkan #}
{{ servers | unique }} {# hapus duplikat #}
{# Kondisi dengan filter #}
{{ value | ternary('yes', 'no') }} {# 'yes' jika value truthy, 'no' jika falsy #}
Kondisi dalam Template #
{# Kondisi sederhana #}
{% if nginx_ssl_enabled %}
listen 443 ssl;
ssl_certificate {{ nginx_ssl_cert }};
ssl_certificate_key {{ nginx_ssl_key }};
{% else %}
listen {{ nginx_port }};
{% endif %}
{# Kondisi dengan elif #}
{% if env == 'production' %}
log_level = warning
{% elif env == 'staging' %}
log_level = info
{% else %}
log_level = debug
{% endif %}
{# Kondisi untuk blok opsional #}
{% if nginx_access_log_enabled | default(true) %}
access_log {{ nginx_log_dir }}/access.log main;
{% else %}
access_log off;
{% endif %}
Loop dalam Template #
{# Iterasi list untuk generate konfigurasi #}
upstream backend {
{% for server in groups['appservers'] %}
server {{ hostvars[server]['ansible_default_ipv4']['address'] }}:{{ app_port }} weight=1;
{% endfor %}
}
{# Loop dengan index #}
{% for vhost in virtual_hosts %}
server {
listen {{ vhost.port }};
server_name {{ vhost.domain }};
location / {
proxy_pass http://{{ vhost.upstream }};
}
}
{% if not loop.last %}
{% endif %}
{% endfor %}
{# Iterasi dictionary #}
{% for key, value in app_config.items() %}
{{ key }} = {{ value }}
{% endfor %}
Template Multi-Host: Menggunakan hostvars #
Template yang paling powerful menggunakan informasi dari semua host, bukan hanya host yang sedang dikonfigurasi. Ini memungkinkan load balancer dikonfigurasi dengan otomatis menggunakan IP semua backend server:
{# templates/haproxy.cfg.j2 — konfigurasi HAProxy yang otomatis #}
global
maxconn 50000
defaults
timeout connect 5s
timeout client 30s
timeout server 30s
frontend http_front
bind *:80
default_backend http_back
backend http_back
balance roundrobin
{% for host in groups['webservers'] %}
server {{ host }} {{ hostvars[host]['ansible_default_ipv4']['address'] }}:{{ app_port }} check
{% endfor %}
Output yang dihasilkan (misalnya untuk 3 web server):
backend http_back
balance roundrobin
server web-01 10.0.1.10:8080 check
server web-02 10.0.1.11:8080 check
server web-03 10.0.1.12:8080 check
hostvarshanya berisi data untuk host yang sudah menjalankanGathering Factsdalam run playbook yang sama. Pastikan semua host yang direferensikan sudah dieksekusi sebelum template yang menggunakanhostvarsdi-render.
Template yang Aman: Hindari Nilai Kosong #
Template yang tidak menangani nilai kosong bisa menghasilkan konfigurasi yang rusak:
{# ANTI-PATTERN: tidak menangani nilai kosong #}
db_password = {{ db_password }}
{# Jika db_password tidak terdefinisi: db_password = (kosong) — konfigurasi rusak #}
{# BENAR: gunakan default atau mandatory check #}
db_password = {{ db_password | mandatory }}
{# mandatory akan gagal dengan error yang jelas jika variabel tidak terdefinisi #}
{# Atau berikan nilai default yang aman #}
max_connections = {{ db_max_connections | default(100) | int }}
{# BENAR: validasi dan bersihkan nilai #}
server_name {{ nginx_server_name | lower | trim }};
listen {{ nginx_port | int }};
worker_processes {{ nginx_worker_processes | default('auto') }};
Memvalidasi Template Sebelum Deploy #
Banyak aplikasi (nginx, Apache, PostgreSQL) menyediakan perintah untuk memvalidasi file konfigurasi sebelum digunakan. Manfaatkan ini dengan parameter validate:
- name: Deploy konfigurasi nginx dari template
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
validate: "nginx -t -c %s" # Validasi sebelum disimpan
notify: Reload nginx
- name: Deploy konfigurasi sudoers
template:
src: sudoers.j2
dest: /etc/sudoers.d/myapp
mode: '0440'
validate: "visudo -cf %s" # Validasi syntax sudoers
Jika validasi gagal, Ansible tidak akan menyimpan file dan task dianggap gagal — mencegah konfigurasi rusak masuk ke production.
Ringkasan #
- Jinja2 menggunakan tiga sintaks:
{{ }}untuk variabel,{% %}untuk kontrol flow,{# #}untuk komentar.- Filter seperti
default(),int,join(), danternary()adalah alat terpenting untuk template yang robust.- Gunakan
hostvarsdalam template untuk mengakses informasi dari host lain — powerful untuk menggenerate konfigurasi load balancer dan service discovery.| mandatoryuntuk variabel yang harus ada — template akan gagal dengan pesan yang jelas jika variabel tidak terdefinisi.- Gunakan parameter
validate:di moduletemplateuntuk memvalidasi file konfigurasi sebelum disimpan — mencegah konfigurasi rusak masuk ke server.- Untuk file konfigurasi yang kompleks, template selalu lebih baik dari
lineinfileataublockinfile— lebih mudah dibaca, lebih mudah di-maintain, dan lebih mudah di-review.