Incident Response

Incident Response #

Alert sudah berbunyi. Seseorang di on-call duty harus bertindak. Tanpa persiapan, setiap insiden dimulai dari nol: buka terminal, SSH ke server, coba ingat perintah diagnostik yang relevan, baca log yang tersebar, coba beberapa solusi. Seringkali yang terjadi adalah salah langkah, eskalasi yang tidak perlu, dan jam-jam yang habis untuk hal yang sebenarnya sudah pernah di-debug sebelumnya. Dengan runbook yang bisa dieksekusi — playbook Ansible yang mendokumentasikan langkah-langkah respons sekaligus menjalankannya — tim bisa merespons lebih cepat, lebih konsisten, dan dengan lebih sedikit kesalahan. Artikel ini membahas cara membangun infrastruktur runbook yang efektif.

Alur Respons Insiden yang Lengkap #

Respons insiden bukan hanya soal “fix the thing yang rusak”. Ada alur yang terstruktur dari deteksi sampai post-mortem, dan setiap fase punya tujuan serta output yang berbeda:

sequenceDiagram
    participant Mon as Monitoring
    participant OnCall as On-Call Engineer
    participant Run as Runbook
    participant Sys as Affected System
    participant EsC as Escalation
    participant PM as Post-Mortem

    Mon->>OnCall: "Alert fired"
    Note over OnCall: "Fase 1: DETEKSI"
    OnCall->>Run: "Acknowledge alert, jalankan diagnose.yml"
    Run->>Sys: "Kumpulkan data"
    Sys-->>Run: "Logs, metrics, status"
    Run-->>OnCall: "Laporan diagnostik"
    Note over OnCall: "Fase 2: TRIAGE"
    OnCall->>OnCall: "Tentukan severity"
    OnCall->>Run: "Pilih runbook sesuai gejala"
    Note over OnCall: "Fase 3: MITIGASI"
    Run->>Sys: "Eksekusi langkah perbaikan"
    alt Berhasil
        Sys-->>Run: "Service pulih"
        OnCall->>Mon: "Konfirmasi resolved"
    else Gagal
        Run-->>OnCall: "Runbook tidak resolve"
        OnCall->>EsC: "Eskalasi (severity naik)"
    end
    Note over OnCall,PM: "Fase 4: POST-MORTEM"
    OnCall->>PM: "Tulis blameless post-mortem"
    PM->>PM: "Identifikasi root cause + action items"

Empat fase ini — Deteksi, Triage, Mitigasi, Post-Mortem — adalah fondasi respons insiden yang efektif. Ansible mengotomasi fase 1 dan 3 (diagnostik dan mitigasi), dan mendokumentasikan fase 2 dan 4 lewat komentar di playbook runbook.


Severity Level: Siapa yang Dipanggil Kapan #

Tidak semua insiden sama. Severity menentukan seberapa cepat respons dibutuhkan, siapa yang harus dipanggil, dan apakah perlu eskalasi. Tabel berikut adalah acuan yang umum dipakai:

Severity Dampak Response Time Eskalasi Contoh
SEV-1 (Critical) Total outage, data loss, security breach < 15 menit On-call → Lead → Director → VP Engineering Production down, customer-facing API return 500
SEV-2 (Major) Degraded sebagian user, ada workaround < 1 jam On-call → Lead Satu region down, payment processing lambat
SEV-3 (Minor) Edge case, tidak ada user impact langsung < 4 jam On-call handle sendiri Non-critical job gagal, satu metric alert palsu
SEV-4 (Cosmetic) Bug minor, no user impact Best effort Backlog Typo di UI, log noisy

Memiliki severity matrix yang jelas mencegah dua masalah umum: (1) engineer yang burn-out karena semua alert diperlakukan SEV-1, dan (2) insiden yangunder-escalated karena engineer tidak yakin apakah harus panggil lead.

State Diagram Severity Escalation #

Severity bisa naik turun tergantung situasi. Insiden yang dimulai SEV-3 bisa menjadi SEV-1 jika business impact ternyata lebih besar dari yang diasumsikan awal:

stateDiagram-v2
    [*] --> SEV3: "Alert diterima"
    SEV3 --> SEV2: "Impact meluas"
    SEV2 --> SEV1: "Total outage"
    SEV2 --> SEV3: "Impact terbatas"
    SEV1 --> SEV2: "Mitigasi parsial"
    SEV1 --> Resolved: "Service pulih"
    SEV2 --> Resolved: "Workaround aktif"
    SEV3 --> Resolved: "Fixed"
    Resolved --> PostMortem: "Severity apapun"
    PostMortem --> [*]
    SEV1 --> SEV1: "Self-escalate jika impact memburuk"

Decision Tree: Severity Awal yang Tepat #

flowchart TD
    A["Alert masuk"] --> B{"User-facing impact?"}
    B -- "Ya" --> C{"Semua user atau sebagian?"}
    C -- "Semua user" --> D["SEV-1: Total outage"]
    C -- "Sebagian" --> E{"Ada workaround?"}
    E -- "Tidak" --> F["SEV-1: Customer-facing down"]
    E -- "Ya" --> G["SEV-2: Degraded"]
    B -- "Tidak" --> H{"Data loss atau security?"}
    H -- "Ya" --> I["SEV-1: Data/Security"]
    H -- "Tidak" --> J{"Bisakah ditunda jam ini?"}
    J -- "Tidak" --> K["SEV-2: Internal blocker"]
    J -- "Ya" --> L["SEV-3: Minor"]

Playbook Diagnostik: Kumpulkan Dulu, Putuskan Kemudian #

Langkah pertama saat insiden bukanlah melakukan perubahan — melainkan mengumpulkan informasi untuk memahami situasi. Banyak insiden yang sebenarnya hanya butuh restart service, tapi tanpa diagnostik yang benar, engineer malah mengubah konfigurasi atau melakukan deploy ulang yang tidak perlu dan justru memperparah masalah:

# playbooks/diagnose.yml
---
- name: Kumpulkan informasi diagnostik saat insiden
  hosts: "{{ target_hosts | default('all') }}"
  gather_facts: true

  tasks:
    - name: Kumpulkan status semua service kritis
      systemd:
        name: "{{ item }}"
      register: service_status
      loop: "{{ critical_services }}"
      ignore_errors: true
      changed_when: false

    - name: Kumpulkan penggunaan resource
      command: "{{ item.cmd }}"
      register: "{{ item.name }}"
      loop:
        - { name: top_processes, cmd: "ps aux --sort=-%cpu | head -15" }
        - { name: disk_usage,    cmd: "df -h" }
        - { name: memory_usage,  cmd: "free -h" }
        - { name: network_conn,  cmd: "ss -tnp | head -30" }
        - { name: open_files,    cmd: "lsof | wc -l" }
      ignore_errors: true
      changed_when: false

    - name: Kumpulkan log error terbaru
      command: "journalctl -u {{ item }} --since '30 minutes ago' --no-pager -p err"
      register: recent_errors
      loop: "{{ critical_services }}"
      ignore_errors: true
      changed_when: false

    - name: Simpan semua diagnostik ke file lokal
      local_action:
        module: copy
        content: |
          ===== DIAGNOSTIK INSIDEN =====
          Host: {{ inventory_hostname }}
          Waktu: {{ ansible_date_time.iso8601 }}

          === STATUS SERVICE ===
          {% for result in service_status.results %}
          {{ result.item }}: {{ result.status.ActiveState | default('unknown') }}
          {% endfor %}

          === RESOURCE USAGE ===
          {{ disk_usage.stdout }}

          {{ memory_usage.stdout }}

          === TOP PROCESSES ===
          {{ top_processes.stdout }}

          === LOG ERRORS (30 MENIT TERAKHIR) ===
          {% for result in recent_errors.results %}
          --- {{ result.item }} ---
          {{ result.stdout | default('(tidak ada error)') }}
          {% endfor %}          
        dest: "/tmp/incident-diag-{{ inventory_hostname }}-{{ ansible_date_time.date }}.txt"
      delegate_to: localhost

- name: Tampilkan ringkasan diagnostik
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Lokasi file diagnostik
      debug:
        msg: "File diagnostik tersimpan di /tmp/incident-diag-*.txt"

Poin penting dari playbook ini: tidak ada task yang mengubah state sistem. Semua task hanya register data dan command dengan changed_when: false. Tujuannya murni mengumpulkan informasi. Engineer yang menjalankan playbook ini boleh tenang karena tidak ada risiko merusak sesuatu yang sebenarnya masih berfungsi.

Saat menjalankan playbook diagnostik, selalu simpan output ke file dengan timestamp, lalu commit ke Git atau upload ke internal wiki. File ini menjadi bukti untuk post-mortem dan referensi untuk insiden serupa di masa depan. Diagnostic yang tidak diarsipkan = pembelajaran yang hilang.

Self-Healing: Automasi untuk Kondisi Umum #

Beberapa kondisi insiden yang berulang dan terprediksi bisa diatasi secara otomatis tanpa intervensi manusia. Automasi self-healing mengurangi waktu pemulihan, mengurangi alert fatigue, dan membuat on-call engineer fokus pada insiden yang benar-benar butuh penalaran manusia:

# playbooks/self-heal.yml
---
- name: Self-healing untuk kondisi umum
  hosts: appservers
  become: true

  tasks:
    - name: Kumpulkan informasi kondisi saat ini
      gather_facts: true

    # Kondisi 1: Disk hampir penuh — bersihkan log lama dan Docker images
    - name: Cek penggunaan disk
      command: df / --output=pcent
      register: disk_pcent
      changed_when: false

    - name: Bersihkan log lama jika disk > 85%
      block:
        - name: Hapus log lama (lebih dari 7 hari)
          find:
            paths: /var/log
            age: 7d
            patterns: "*.log.*"
            recurse: true
          register: old_logs

        - name: Hapus file log lama
          file:
            path: "{{ item.path }}"
            state: absent
          loop: "{{ old_logs.files }}"

        - name: Bersihkan Docker images yang tidak digunakan
          community.docker.docker_prune:
            images: true
            volumes: true
            builder_cache: true
          ignore_errors: true

        - name: Catat tindakan self-healing
          debug:
            msg: "Self-heal: disk cleanup dilakukan (penggunaan disk > 85%)"
      when: disk_pcent.stdout | trim | replace('%', '') | int > 85

    # Kondisi 2: Service crash — restart otomatis
    - name: Cek status service kritis
      systemd:
        name: "{{ item }}"
      register: svc_check
      loop: "{{ critical_services }}"
      changed_when: false
      ignore_errors: true

    - name: Restart service yang tidak aktif
      systemd:
        name: "{{ item.item }}"
        state: restarted
      loop: "{{ svc_check.results }}"
      when:
        - item.status is defined
        - item.status.ActiveState != "active"
      loop_control:
        label: "{{ item.item }}"

    # Kondisi 3: Memory pressure — restart service dengan memory leak
    - name: Cek penggunaan memory
      command: free -m --output=used
      register: mem_used
      changed_when: false

    - name: Restart aplikasi jika memory sangat tinggi
      systemd:
        name: myapp
        state: restarted
      when:
        - ansible_memtotal_mb > 0
        - (mem_used.stdout_lines[-1] | trim | int) / ansible_memtotal_mb > 0.90

Kapan Self-Healing Tepat vs Berbahaya #

Self-healing adalah pedang bermata dua. Di satu sisi ia mengurangi noise dan MTTR; di sisi lain, ia bisa menutupi masalah sistemik yang seharusnya diinvestigasi:

SELF-HEAL COCOK untuk:
  ✓ Disk cleanup (log rotation, image prune) — reversible, low risk
  ✓ Restart service yang crash — biasanya pulih dengan sendirinya
  ✓ Kill process zombie — clear dan reversible
  ✓ Rotate log file besar — idempotent

SELF-HEAL BERBAHAYA untuk:
  ✗ Restart database prod tanpa threshold ketat — bisa trigger failover
  ✗ Auto-scale up resource tanpa analisis — menutupi memory leak
  ✗ Clear alert state — menyembunyikan masalah
  ✗ Modify konfigurasi service — bisa jadi salah konfigurasi makin parah
  ✗ Restart Kubernetes node — bisa trigger pod eviction cascade
Setiap self-heal action harus mengirim notifikasi ke channel on-call, bahkan jika berhasil. Engineer perlu tahu bahwa self-heal terjadi — bukan untuk bertindak saat itu, tapi untuk menganalisis pola: jika self-heal untuk service X terjadi 5x dalam sehari, ada masalah fundamental di service X (memory leak, config bug) yang harus diinvestigasi dan di-fix permanen. Self-heal yang sering adalah symptom, bukan solusi.

Runbook sebagai Playbook yang Terdokumentasi #

Runbook yang paling efektif adalah yang bisa dijalankan sekaligus menjelaskan apa yang dilakukannya. Dokumentasi yang terpisah dari eksekusi cepat basi — runbook yang ada di playbook Ansible selalu ter-update karena tested setiap kali dijalankan:

# playbooks/runbooks/database-connection-exhausted.yml
---
# RUNBOOK: Database Connection Pool Exhausted
#
# Gejala: Error "remaining connection slots are reserved for non-replication superuser connections"
#         atau aplikasi timeout saat konek ke database
# Penyebab umum: Connection leak di aplikasi, traffic spike, atau pool size terlalu kecil
# Eskalasi: Jika runbook ini tidak menyelesaikan masalah dalam 10 menit,
#           hubungi tim database dan naikkan severity ke SEV-2

- name: Runbook — Database Connection Pool Exhausted
  hosts: dbservers
  become: true

  tasks:
    - name: "[Diagnostik] Lihat semua koneksi aktif ke database"
      command: >
        psql -U postgres -c
        "SELECT client_addr, state, count(*) as count
         FROM pg_stat_activity
         GROUP BY client_addr, state
         ORDER BY count DESC"        
      register: db_connections
      changed_when: false
      become_user: postgres

    - name: "[Info] Tampilkan koneksi per client"
      debug:
        var: db_connections.stdout_lines

    - name: "[Cek] Berapa koneksi idle yang lama?"
      command: >
        psql -U postgres -c
        "SELECT count(*) FROM pg_stat_activity
         WHERE state = 'idle'
         AND state_change < now() - interval '10 minutes'"        
      register: idle_connections
      changed_when: false
      become_user: postgres

    - name: "[Aksi] Terminasi koneksi idle lama jika > 20"
      command: >
        psql -U postgres -c
        "SELECT pg_terminate_backend(pid)
         FROM pg_stat_activity
         WHERE state = 'idle'
         AND state_change < now() - interval '10 minutes'
         AND pid <> pg_backend_pid()"        
      become_user: postgres
      when: idle_connections.stdout | regex_search('\d+') | int > 20
      register: terminate_result

    - name: "[Hasil] Tampilkan jumlah koneksi yang diterminasi"
      debug:
        msg: "Koneksi yang diterminasi: {{ terminate_result.stdout | default('0 (tidak ada yang perlu diterminasi)') }}"

    - name: "[Verifikasi] Cek jumlah koneksi sekarang"
      command: >
        psql -U postgres -c
        "SELECT count(*) FROM pg_stat_activity"        
      register: current_connections
      changed_when: false
      become_user: postgres

    - name: "[Status] Kondisi koneksi saat ini"
      debug:
        var: current_connections.stdout_lines

Perhatikan struktur di playbook: setiap task punya label [Diagnostik], [Cek], [Aksi], [Verifikasi], [Status] di awal nama. Pola ini memudahkan on-call engineer yang membaca log output memahami fase mana yang sedang berjalan, dan juga mempercepat post-mortem — log langsung menunjukkan urutan langkah yang sudah dilakukan.

Anti-Pattern: Runbook Samar vs Runbook Konkret #

# ANTI-PATTERN: Runbook samar tanpa detail langkah
# playbooks/fix-database.yml
---
- name: Fix database issue
  hosts: dbservers
  tasks:
    - name: Restart postgres if needed
      systemd:
        name: postgresql
        state: restarted
      when: "something is wrong"
# Masalah:
# 1. "if needed" dan "something is wrong" tidak terdefinisi.
# 2. Engineer yang jalankan ini di jam 3 pagi harus menebak kapan harus restart.
# 3. Tidak ada verifikasi setelah restart.
# 4. Tidak ada catatan apa yang dicek.
# 5. Tidak reversible kalau restart justru memperparah masalah.

# BENAR: Runbook dengan step konkret, threshold jelas, dan verifikasi
# playbooks/runbooks/database-connection-exhausted.yml
- name: Runbook — Database Connection Pool Exhausted
  hosts: dbservers
  tasks:
    - name: "[Diagnostik] Hitung koneksi idle > 10 menit"
      command: "psql -c \"SELECT count(*) FROM pg_stat_activity WHERE state = 'idle' AND state_change < now() - interval '10 minutes'\""
      register: idle_count
      changed_when: false

    - name: "[Aksi] Terminasi jika > 20 koneksi idle"
      command: "psql -c \"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND state_change < now() - interval '10 minutes' AND pid <> pg_backend_pid()\""
      when: idle_count.stdout | regex_search('\d+') | int > 20

    - name: "[Verifikasi] Pastikan total koneksi turun di bawah threshold"
      command: "psql -c \"SELECT count(*) FROM pg_stat_activity\""
      register: total_count
      failed_when: total_count.stdout | regex_search('\d+') | int > 100
# Keuntungan:
# - Threshold eksplisit (> 20 koneksi idle, total < 100)
# - Ada fase verifikasi (failed_when untuk rollback indicator)
# - Tidak restart service — reversible, low-risk
# - Output jelas untuk post-mortem

Workflow Eskalasi #

Saat runbook tidak menyelesaikan masalah, eskalasi harus terjadi secara otomatis — engineer on-call tidak perlu membuang waktu untuk memutuskan siapa yang harus dipanggil. Sistem harus membuat keputusan eskalasi berdasarkan severity dan durasi insiden:

# playbooks/escalate.yml
---
- name: Kirim eskalasi insiden
  hosts: localhost
  vars:
    incident_id: "{{ lookup('pipe', 'date +%Y%m%d%H%M%S') }}"

  tasks:
    - name: Buat tiket insiden di sistem tiket
      uri:
        url: "{{ pagerduty_events_url }}"
        method: POST
        body_format: json
        body:
          routing_key: "{{ vault_pagerduty_routing_key }}"
          event_action: trigger
          dedup_key: "ansible-incident-{{ incident_id }}"
          payload:
            summary: "{{ incident_summary }}"
            severity: "{{ incident_severity | default('critical') }}"
            source: "ansible-runbook"
            custom_details:
              environment: "{{ env }}"
              affected_hosts: "{{ ansible_play_hosts | join(', ') }}"
              runbook: "{{ playbook_dir | basename }}"
        status_code: 202
      no_log: true

    - name: Kirim notifikasi Slack darurat
      uri:
        url: "{{ vault_slack_webhook_url }}"
        method: POST
        body_format: json
        body:
          text: ":rotating_light: *INSIDEN KRITIS*"
          attachments:
            - color: danger
              title: "{{ incident_summary }}"
              fields:
                - title: Environment
                  value: "{{ env }}"
                  short: true
                - title: Incident ID
                  value: "{{ incident_id }}"
                  short: true
                - title: Runbook
                  value: "{{ playbook_dir | basename }}"
                  short: true
                - title: Affected Hosts
                  value: "{{ ansible_play_hosts | join(', ') }}"
                  short: false
      no_log: true

Decision Tree: Siapa yang Dipanggil #

flowchart TD
    A[Incident triggered] --> B{Severity?}
    B -- SEV-1 --> C[On-Call: panggil<br/>+ lead engineer]
    C --> D{Ack < 5 min?}
    D -- Tidak --> E[Auto-escalate:<br/>engineering manager]
    E --> F{Ack < 10 min?}
    F -- Tidak --> G[VP Engineering +<br/>comms team]
    B -- SEV-2 --> H[On-Call handle<br/>+ notify lead di Slack]
    H --> I{Resolved < 1 jam?}
    I -- Tidak --> J[Eskalasi ke SEV-1]
    I -- Ya --> K[Close + post-mortem]
    B -- SEV-3 --> L[On-Call handle sendiri]
    L --> M[Resolved? ticket close]
    B -- SEV-4 --> N[Backlog, no escalation]

Workflow ini bisa diotomasi lewat timer di PagerDuty atau rule di alerting (lihat Alerting). Yang penting: eskalasi tidak pernah menjadi keputusan manual saat insiden terjadi — keputusan itu sudah dibuat sebelumnya dan tertulis di policy.


Post-Mortem: Blameless dan Actionable #

Post-mortem bukan dokumen untuk mencari siapa yang salah — itu adalah dokumen untuk mencari kondisi sistemik yang memungkinkan insiden terjadi. Budaya “blameless” krusial: kalau orang takut dituduh saat insiden, mereka akan menyembunyikan informasi, dan pembelajaran tidak pernah terjadi:

Anti-Pattern: Skip Post-Mortem vs Blameless Post-Mortem #

ANTI-PATTERN: Skip post-mortem untuk insiden "kecil"
  Setelah SEV-3 selesai, engineer kembali ke kerjaan biasa.
  Tidak ada post-mortem. Tidak ada root cause analysis.
  3 bulan kemudian, SEV-3 yang sama terjadi lagi, ke orang berbeda.
  Lagi-lagi tidak ada post-mortem. Pattern tidak pernah terlihat.
  
  Risk: Insiden yang sama berulang dengan biaya yang sama setiap kali.
  Tim tidak pernah belajar. Budaya "fix and forget" menang.

BENAR: Blameless post-mortem untuk semua insiden SEV-1 dan SEV-2
  Dalam 48 jam setelah resolved, schedule post-mortem meeting.
  Hadir: on-call engineer, lead, SRE, product manager.
  
  Format:
  1. Timeline (dari alert sampai resolved, dengan timestamp presisi)
  2. Root cause analysis (5-Whys atau fishbone diagram)
  3. Contributing factors (apa yang membuat insiden ini bisa terjadi)
  4. What went well (respon cepat, runbook efektif)
  5. What went wrong (alert lambat, dokumentasi kurang, runbook obsolete)
  6. Action items dengan owner dan deadline
  
  Output: Dokumen publik di internal wiki + action items di backlog.
  
  Risk: post-mortem makan 1-2 jam per insiden.
  Benefit: insiden yang sama tidak berulang → save 5-10 jam incident response
           di masa depan → ROI positif setelah insiden ke-2.

Template Post-Mortem via Ansible #

Untuk konsistensi, sediakan template post-mortem yang bisa di-generate dari playbook:

# playbooks/templates/post-mortem-template.md.j2
# Post-Mortem: {{ incident_summary }}

**Incident ID:** {{ incident_id }}
**Date:** {{ incident_date }}
**Severity:** {{ incident_severity }}
**Status:** Resolved
**Duration:** {{ incident_duration }}

## Timeline

| Time (UTC) | Event |
|---|---|
{% for event in timeline %}
| {{ event.time }} | {{ event.description }} |
{% endfor %}

## Root Cause

{{ root_cause }}

## Contributing Factors

{% for factor in contributing_factors %}
- {{ factor }}
{% endfor %}

## What Went Well

{% for item in went_well %}
- {{ item }}
{% endfor %}

## What Went Wrong

{% for item in went_wrong %}
- {{ item }}
{% endfor %}

## Action Items

| Action | Owner | Deadline | Status |
|---|---|---|---|
{% for item in action_items %}
| {{ item.action }} | {{ item.owner }} | {{ item.deadline }} | Open |
{% endfor %}

Anti-Pattern: Post-Mortem yang Menyalahkan Individu #

ANTI-PATTERN: Post-mortem yang fokus ke "siapa"
  "Andi salah hapus tabel production saat maintenance."
  "Toni telat acknowledge alert karena sedang meeting."
  "Siti deploy di jam sibuk tanpa approval."
  
  Efek:
  - Tim jadi defensive, takut diskusi terbuka di post-mortem
  - Informasi disembunyikan di post-mortem berikutnya
  - Fokus ke "orang X harus hati-hati" bukan ke sistem
  - Tidak ada perubahan sistemik → insiden berulang

BENAR: Post-mortem yang fokus ke kondisi sistemik
  "Proses deployment tidak punya pre-check yang memvalidasi environment."
  "Alert tidak punya escalation policy yang jelas untuk severity ini."
  "On-call schedule tidak overlap dengan meeting tim, jadi ada gap 30 menit."
  "Runbook untuk skenario X belum ditulis, dan engineer yang pertama respond
   belum pernah menghadapi skenario ini sebelumnya."
  
  Keuntungan:
  - Tidak ada yang merasa disalahkan secara personal
  - Fokus ke sistem, proses, dan tooling yang bisa diperbaiki
  - Action items berupa perubahan sistemik (bukan "Andi harus lebih hati-hati")
  - Budaya psychological safety → insiden dilaporkan lebih dini

Integrasi dengan Alerting dan SLO #

Respons insiden yang efektif dimulai dari alert yang baik dan SLO yang terdefinisi dengan jelas. Alert tanpa SLO = noise. SLO tanpa runbook = tujuan tanpa eksekusi. Lihat Alerting untuk setup alert yang actionable, dan SLO & SLA untuk mendefinisikan target layanan yang terukur:

flowchart LR
    A["Monitoring & SLO"] --> B{"Breach SLO?"}
    B -- "Ya" --> C["Alert fired"]
    C --> D["Severity assigned"]
    D --> E{"Eskalasi policy"}
    E --> SEV1["SEV-1: PagerDuty"]
    E --> SEV2["SEV-2: Slack + on-call"]
    E --> SEV3["SEV-3: Ticket"]
    SEV1 --> F["On-Call respond"]
    SEV2 --> F
    SEV3 --> F
    F --> G{"Runbook resolves?"}
    G -- "Ya" --> H["Service restored"]
    H --> I["Post-Mortem"]
    G -- "Tidak" --> J["Eskalasi naik"]
    J --> K["Lead/Manager panggil"]
    K --> F

Alert yang baik adalah alert yang punya runbook link di annotation-nya. Saat on-click alert di PagerDuty, engineer langsung dibawa ke runbook yang relevan. Ini menghemat lookup time dan mengurangi keputusan “apa yang harus dilakukan” saat insiden terjadi.


Kapan Incident Response Automation Tidak Cocok #

Tidak semua insiden cocok diotomasi. Ada kelas insiden yang butuh penalaran manusia dan keputusan situasional:

JANGAN automasi untuk:
  ✗ Security incident (data breach, unauthorized access) — butuh investigasi forensik
  ✗ Data corruption atau potential data loss — butuh assessment manual
  ✗ Deployment rollback keputusan — butuh judgement bisnis
  ✗ Communication ke customer tentang outage — butuh crafted message
  ✗ Root cause analysis — butuh manusia dengan konteks domain
  ✗ Action items post-mortem — butuh diskusi tim, bukan script

AUTOMASI COCOK untuk:
  ✓ Kumpulkan diagnostik (logs, metrics, status) saat alert
  ✓ Restart service yang crash dengan threshold jelas
  ✓ Cleanup resource (disk, memory) yang sudah penuh
  ✓ Terminasi koneksi database yang idle/over
  ✓ Kirim notifikasi eskalasi ke PagerDuty/Slack
  ✓ Generate template post-mortem dengan timeline
  ✓ Verifikasi kondisi setelah perbaikan

Batas yang jelas antara automasi dan judgement adalah kunci: automasi yang terlalu agresif bisa menutupi masalah; automasi yang terlalu minim tidak memberikan value.


Ringkasan #

  • Kumpulkan diagnostik sebelum bertindak — playbook diagnostik yang mengumpulkan status service, resource usage, dan log error memberikan gambaran situasi tanpa membuat perubahan.
  • Severity matrix yang jelas (SEV-1 sampai SEV-4) mencegah under-escalation dan over-escalation — tentukan threshold response time dan jalur eskalasi sebelum insiden terjadi.
  • Self-healing untuk kondisi yang berulang dan terprediksi (disk penuh, service crash, idle connection) — kurangi intervensi manual untuk hal-hal yang bisa diotomasi dengan risiko rendah.
  • Runbook sebagai playbook adalah cara terbaik mendokumentasikan prosedur insiden — dokumentasi dan eksekusi ada di tempat yang sama dan selalu sinkron.
  • Tambahkan komentar naratif di playbook runbook (Gejala, Penyebab, Eskalasi) — orang yang on-call jam 3 pagi butuh konteks, bukan hanya perintah.
  • Gunakan label task seperti [Diagnostik], [Aksi], [Verifikasi] untuk memudahkan baca log output dan analisis post-mortem.
  • Workflow eskalasi harus otomatis — jika runbook tidak menyelesaikan masalah, sistem harus mengirim alert eskalasi tanpa membutuhkan keputusan manual.
  • Blameless post-mortem fokus ke kondisi sistemik, bukan individu — ini menciptakan psychological safety dan mencegah insiden berulang.
  • Semua post-mortem SEV-1 dan SEV-2 harus menghasilkan action items dengan owner dan deadline — dokumentasi tanpa action items = pembelajaran tanpa implementasi.
  • Setiap self-heal action harus kirim notifikasi ke on-call, bahkan jika berhasil — pola self-heal yang sering adalah symptom dari masalah sistemik.
  • Simpan semua runbook di Git dan review secara berkala — runbook yang tidak di-update menjadi dokumen yang menyesatkan saat paling dibutuhkan.
  • Alert yang baik punya runbook link di annotation — satu klik dari PagerDuty langsung ke playbook yang relevan.
  • Lihat juga Alerting untuk setup alert yang actionable, dan SLO & SLA untuk mendefinisikan target layanan yang terukur.

← Sebelumnya: Health Check   Berikutnya: SLO & SLA →

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