Environment Management

Environment Management #

Hampir semua sistem produksi berjalan di beberapa lingkungan: development untuk eksperimen, staging untuk validasi, production untuk pengguna nyata. Tantangannya adalah memastikan konfigurasi yang tepat diterapkan ke lingkungan yang tepat — tanpa duplikasi, tanpa variasi yang tidak disengaja, dan tanpa risiko konfigurasi staging bocor ke production atau sebaliknya. Ansible menyediakan struktur yang memungkinkan ini dilakukan dengan bersih dan aman.

Struktur Inventory Per Environment #

Pola yang paling scalable adalah satu direktori inventory per environment:

inventory/
  ├── development/
  │   ├── hosts.ini
  │   └── group_vars/
  │       ├── all.yml           # Variabel untuk semua host di dev
  │       ├── webservers.yml
  │       └── databases.yml
  │
  ├── staging/
  │   ├── hosts.ini
  │   └── group_vars/
  │       ├── all.yml
  │       ├── webservers.yml
  │       └── databases.yml
  │
  └── production/
      ├── hosts.ini
      └── group_vars/
          ├── all.yml
          ├── webservers.yml
          └── vault.yml         # Variabel sensitif — dienkripsi

Setiap environment punya all.yml sendiri yang mendefinisikan nilai yang spesifik untuk environment tersebut:

# inventory/development/group_vars/all.yml
env: development
app_replicas: 1
app_debug: true
log_level: debug
db_pool_size: 5
enable_https: false

# inventory/staging/group_vars/all.yml
env: staging
app_replicas: 2
app_debug: false
log_level: info
db_pool_size: 10
enable_https: true

# inventory/production/group_vars/all.yml
env: production
app_replicas: 4
app_debug: false
log_level: warning
db_pool_size: 50
enable_https: true

Shared vs Environment-Specific Variables #

Banyak variabel sama di semua environment — tidak perlu diduplikasi:

group_vars/         ← Di luar direktori inventory (shared)
  └── all.yml       ← Berlaku untuk semua environment

inventory/
  ├── development/
  │   └── group_vars/
  │       └── all.yml     ← Override variabel shared untuk dev
  ├── staging/
  └── production/
# group_vars/all.yml (shared — berlaku semua environment)
app_port: 8080
app_name: myapp
app_user: deployer
app_dir: /opt/myapp
backup_retention_days: 30

# Nilai default yang akan di-override per environment:
app_replicas: 1
log_level: info
# inventory/production/group_vars/all.yml (override untuk production)
app_replicas: 4
log_level: warning
# Tidak perlu mendefinisikan ulang app_port, app_name, dll.
# Mereka di-inherit dari shared group_vars

Environment Promotion dengan Tag Git #

Pola yang umum: setiap environment di-deploy dari branch atau tag Git yang berbeda:

Git Flow untuk Deployment:
  feature/* → main (auto-deploy ke development)
  main      → release candidate (manual promote ke staging)
  tag v*.*.*→ production (setelah staging verified)
# playbooks/deploy.yml
---
- name: Deploy aplikasi
  hosts: appservers
  vars:
    # Di-override dari pipeline berdasarkan environment dan tag Git
    deploy_version: "{{ version | mandatory }}"
    deploy_env: "{{ env }}"   # Dari inventory group_vars

  pre_tasks:
    - name: Verifikasi kita deploy ke environment yang benar
      assert:
        that:
          - deploy_env == expected_env | default(deploy_env)
        fail_msg: >
          Mismatch environment! Inventory menunjuk ke '{{ deploy_env }}'
          tapi pipeline mengharapkan '{{ expected_env }}'.          

    - name: Verifikasi versi production hanya dari tag resmi
      assert:
        that:
          - deploy_version is match('^v?[0-9]+\.[0-9]+\.[0-9]+$')
        fail_msg: >
          Deployment production hanya boleh menggunakan tag semver.
          Versi '{{ deploy_version }}' tidak valid untuk production.          
      when: deploy_env == 'production'

Konfigurasi Berbeda tapi Playbook Sama #

Kekuatan utama pola ini: satu playbook yang bekerja di semua environment berdasarkan variabel:

# playbooks/setup-nginx.yml
---
- name: Setup nginx untuk semua environment
  hosts: webservers
  become: true
  tasks:
    - name: Install nginx
      apt:
        name: nginx
        state: present

    - name: Deploy konfigurasi nginx
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: Reload nginx

    # Konfigurasi SSL — hanya jika enable_https: true
    - name: Deploy SSL certificate
      copy:
        src: "files/ssl/{{ inventory_hostname }}.crt"
        dest: /etc/ssl/certs/app.crt
      when: enable_https | bool

    # Worker processes — berbeda per environment
    - name: Set nginx worker processes
      lineinfile:
        path: /etc/nginx/nginx.conf
        regexp: '^worker_processes'
        line: "worker_processes {{ ansible_processor_vcpus if env == 'production' else 1 }};"
      notify: Reload nginx
{# templates/nginx.conf.j2 — template yang aware lingkungan #}
worker_processes {{ ansible_processor_vcpus if env == 'production' else 1 }};

events {
    worker_connections {{ 1024 if env == 'production' else 256 }};
}

http {
    {% if enable_https | bool %}
    server {
        listen 443 ssl;
        ssl_certificate /etc/ssl/certs/app.crt;
        # ... SSL config
    }
    {% else %}
    server {
        listen 80;
        # Dev/staging: tidak pakai SSL
    }
    {% endif %}
}

Environment Lock: Cegah Deploy ke Environment yang Salah #

# roles/common/tasks/env-lock.yml
# Jalankan ini di awal setiap playbook deployment

- name: Cek environment lock file
  stat:
    path: /etc/ansible-env-lock
  register: env_lock

- name: Verifikasi environment sesuai jika lock file ada
  block:
    - name: Baca expected environment dari lock file
      slurp:
        src: /etc/ansible-env-lock
      register: lock_content

    - name: Validasi environment yang dikonfigurasi
      assert:
        that:
          - lock_content.content | b64decode | trim == env
        fail_msg: >
          ENVIRONMENT MISMATCH!
          Server ini dikonfigurasi untuk: {{ lock_content.content | b64decode | trim }}
          Inventory yang digunakan: {{ env }}
          Hentikan deployment untuk mencegah konfigurasi yang salah!          
  when: env_lock.stat.exists

- name: Tulis environment lock file jika belum ada
  copy:
    content: "{{ env }}\n"
    dest: /etc/ansible-env-lock
    mode: '0444'   # Read-only
  when: not env_lock.stat.exists

Ringkasan #

  • Satu direktori inventory per environment adalah pola paling scalable — setiap environment punya hosts, variabel, dan vault-nya sendiri yang terpisah.
  • Shared group_vars/ di luar direktori inventory untuk variabel yang sama di semua environment — override di inventory/<env>/group_vars/ hanya untuk yang berbeda.
  • Satu playbook untuk semua environment dengan variabel yang mengontrol perilaku — jangan duplikasi playbook hanya karena perbedaan konfigurasi kecil.
  • Validasi di pre_tasks: pastikan kita deploy ke environment yang benar dan versi yang valid sebelum melakukan perubahan apapun.
  • Environment lock file di /etc/ansible-env-lock mencegah kecelakaan fatal menjalankan inventory production ke server staging atau sebaliknya.
  • Deployment production hanya dari tag semver yang resmi — enforce ini di playbook dengan assert sehingga tidak ada yang bisa bypass.

← Sebelumnya: GitLab CI   Berikutnya: Rollback Strategy →

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