Variable Anti Pattern

Variable Anti Pattern #

Sistem variabel Ansible sangat fleksibel — ada lebih dari 20 tingkat precedence, variabel bisa didefinisikan di hampir semua tempat, dan template Jinja2 bisa menggunakan hampir semua expression. Fleksibilitas ini adalah kekuatan sekaligus risiko. Variabel yang didefinisikan di tempat yang salah akan di-override oleh variabel lain tanpa pesan error. Variabel yang namanya tidak jelas menyebabkan kebingungan saat debugging. Artikel ini membahas pola manajemen variabel yang sering menyebabkan masalah tersembunyi.

Anti Pattern 1: Tidak Memahami Precedence #

Ansible memiliki 22 tingkat precedence variabel. Tidak memahaminya menyebabkan variabel yang “seharusnya” digunakan ternyata di-override oleh variabel lain:

# MASALAH: Kenapa nilai ini tidak berubah?
#
# roles/nginx/defaults/main.yml
nginx_port: 80                  # Precedence rendah

# group_vars/all.yml
nginx_port: 8080                # Override defaults — ini yang aktif

# inventory/production/group_vars/webservers.yml
nginx_port: 443                 # Override group_vars/all.yml

# playbook.yml
- hosts: webservers
  vars:
    nginx_port: 9000            # Override semua di atas — ini yang aktif!

# HASIL: nginx_port = 9000
# Tapi penulis playbook.yml mungkin tidak sadar ada nilai di defaults/main.yml
Aturan praktis: Tingkat precedence dari RENDAH ke TINGGI:
  1. defaults/main.yml           ← Nilai default yang bisa di-override
  2. inventory vars              ← Konfigurasi per host/group
  3. group_vars / host_vars      ← Konfigurasi per group/host
  4. play vars                   ← vars: di level play
  5. task vars                   ← vars: di level task
  6. set_fact                    ← Variabel runtime
  7. Extra vars (-e)             ← Selalu menang, untuk override darurat

Gunakan defaults/ untuk default yang bisa di-override.
Gunakan vars/ (role vars) hanya untuk nilai yang TIDAK BOLEH di-override.

Anti Pattern 2: set_fact Berlebihan #

# ANTI-PATTERN: menggunakan set_fact untuk setiap kalkulasi kecil
- name: Kalkulasi worker processes
  set_fact:
    half_cpu: "{{ ansible_processor_vcpus // 2 }}"

- name: Hitung worker connections
  set_fact:
    workers: "{{ half_cpu | int + 1 }}"

- name: Set max connections
  set_fact:
    max_conn: "{{ workers | int * 1000 }}"

- name: Hitung buffer size
  set_fact:
    buf_size: "{{ (ansible_memtotal_mb / 4) | int }}M"
# BENAR: kalkulasi langsung dalam template atau vars:
# defaults/main.yml
nginx_worker_processes: "{{ ansible_processor_vcpus }}"
nginx_worker_connections: 1024
nginx_max_connections: "{{ nginx_worker_processes | int * nginx_worker_connections | int }}"

# Atau kalkulasi kompleks di vars: sekali saja
- name: Hitung semua konfigurasi nginx sekaligus
  set_fact:
    nginx_config:
      worker_processes: "{{ ansible_processor_vcpus }}"
      worker_connections: 1024
      buffer_size: "{{ (ansible_memtotal_mb // 4) }}M"

# Gunakan: {{ nginx_config.worker_processes }}

set_fact membuat variabel yang persisten di seluruh play dan sulit dilacak asalnya. Batasi penggunaannya untuk hasil register yang perlu di-transformasi, bukan kalkulasi yang bisa dilakukan inline.


Anti Pattern 3: Nama Variabel yang Ambigu #

# ANTI-PATTERN: nama variabel yang tidak jelas
- set_fact:
    port: 8080         # Port apa? Nginx? App? Database?
    host: db-01        # Host apa?
    version: "2.1.0"   # Versi software apa?
    enabled: true      # Apa yang enabled?
    config: {}         # Konfigurasi apa?
# BENAR: nama variabel yang deskriptif dan namespaced
- set_fact:
    app_http_port: 8080
    postgresql_primary_host: db-01.internal
    nginx_version: "1.24.0"
    ssl_enabled: true
    nginx_config:
      worker_processes: 4
      worker_connections: 1024

# Konvensi untuk role: prefix dengan nama role
# nginx_port, nginx_user, nginx_conf_dir
# postgresql_port, postgresql_data_dir

Anti Pattern 4: Mengekspos Secret dalam Debug #

# ANTI-PATTERN: debug yang menampilkan secret
- name: Debug konfigurasi database
  debug:
    var: db_config
  # db_config mungkin berisi db_password!

- name: Debug semua variabel
  debug:
    var: hostvars[inventory_hostname]
  # Ini menampilkan SEMUA variabel termasuk yang dari vault!

- name: Debug connection string
  debug:
    msg: "Connecting to postgresql://{{ db_user }}:{{ db_password }}@{{ db_host }}"
  # Password muncul di output!
# BENAR: debug tanpa mengekspos secret
- name: Debug konfigurasi (tanpa secret)
  debug:
    msg:
      - "db_host: {{ db_host }}"
      - "db_port: {{ db_port }}"
      - "db_user: {{ db_user }}"
      - "db_password: [REDACTED]"   # Jangan tampilkan!

- name: Verifikasi koneksi tanpa expose credential
  command: pg_isready -h {{ db_host }} -p {{ db_port }} -U {{ db_user }}
  changed_when: false
  no_log: true   # Sembunyikan command dan output dari log

Anti Pattern 5: Extra Vars untuk Konfigurasi Rutin #

# ANTI-PATTERN: mengandalkan -e untuk konfigurasi yang seharusnya di inventory
# Pipeline selalu dijalankan dengan:
# ansible-playbook site.yml -e "db_host=10.0.2.5" -e "db_port=5432" -e "app_name=myapp"
# ...dan masih 10 -e parameter lagi
#
# Masalah: -e sulit di-audit, mudah lupa, tidak terdokumentasi,
# dan tidak bisa di-review di Git

# BENAR: konfigurasi di tempat yang tepat
# inventory/production/group_vars/all.yml
db_host: 10.0.2.5
db_port: 5432
app_name: myapp

# -e hanya untuk override ad-hoc atau parameter yang benar-benar dinamis per-run:
# ansible-playbook deploy.yml -e "deploy_version=2.1.0"

Anti Pattern 6: Menggunakan vars_files Padahal Seharusnya group_vars #

# ANTI-PATTERN: load variabel secara manual padahal ada mekanisme otomatis
- name: Deploy ke production
  hosts: production
  vars_files:
    - vars/production.yml
    - vars/database.yml
    - vars/secrets.yml
  # Setiap playbook harus tahu file mana yang harus di-load
  # Mudah lupa, mudah salah, tidak ada konsistensi antar playbook
# BENAR: Ansible otomatis memuat group_vars dan host_vars
# Letakkan di:
# inventory/production/group_vars/all.yml      — semua host di production
# inventory/production/group_vars/dbservers.yml — hanya database servers
# inventory/production/host_vars/db-01.yml      — hanya db-01

# vars_files masih berguna untuk:
# - File yang dibagikan antar project (di luar inventory)
# - File konfigurasi yang tidak terkait dengan grup inventory

Ringkasan #

  • Pahami hierarki precedence: defaults/ untuk nilai yang bisa di-override, vars/ role untuk nilai yang tidak boleh, group_vars/ untuk konfigurasi per-lingkungan, -e hanya untuk override darurat.
  • Batasi set_fact — gunakan hanya untuk transformasi hasil register, bukan kalkulasi yang bisa dilakukan inline di template atau vars.
  • Nama variabel yang deskriptif dengan namespace role sebagai prefix: nginx_port, postgresql_data_dir, bukan port atau dir.
  • no_log: true untuk semua task yang melibatkan secret — jangan pernah biarkan password atau token muncul di output Ansible.
  • Jangan debug hostvars langsung — ia berisi semua variabel termasuk yang dari vault. Debug hanya field yang diperlukan secara eksplisit.
  • group_vars dan host_vars lebih baik dari vars_files untuk konfigurasi environment — dimuat otomatis, konsisten di semua playbook, mudah di-audit.

← Sebelumnya: Role Anti Pattern   Berikutnya: Security Anti Pattern →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact