OOM Killer #

Di suatu malam, proses Node.js yang melayani API produksi tiba-tiba berhenti. Tidak ada exception, tidak ada graceful shutdown, tidak ada log perpisahan. Server restart, koneksi yang sedang aktif terputus, dan beberapa transaksi yang sedang diproses hilang begitu saja. Di log sistem, satu baris tersembunyi di antara ribuan log lain: Out of memory: Kill process 12345 (node) score 723 or sacrifice child.

OOM Killer — Out-of-Memory Killer — adalah mekanisme di kernel Linux yang aktif ketika sistem kehabisan memory dan tidak bisa mengalokasikan lebih banyak. Alih-alih membiarkan seluruh sistem freeze atau crash, kernel memilih satu atau beberapa proses untuk dibunuh agar memory bisa dibebaskan. Proses yang dipilih tidak mendapat kesempatan untuk cleanup — ia langsung diterminasi dengan SIGKILL.

Bagi engineer yang tidak memahaminya, OOM kill terasa seperti bug misterius atau ketidakstabilan yang tidak bisa dijelaskan. Bagi yang memahaminya, OOM kill adalah sinyal yang jelas: ada masalah dengan manajemen memory di sistem tersebut yang perlu diselesaikan.

Cara Kerja OOM Killer #

Ketika sebuah proses meminta memory dan kernel tidak bisa memenuhinya, kernel masuk ke proses OOM handling. Sebelum membunuh proses, kernel mencoba beberapa langkah:

flowchart TD
    A[Proses minta alokasi memory] --> B{Memory tersedia?}
    B --> |Ya| C[Alokasi berhasil]
    B --> |Tidak| D[Coba bebaskan page cache]
    D --> E{Cukup?}
    E --> |Ya| C
    E --> |Tidak| F[Coba swap ke disk]
    F --> G{Swap tersedia?}
    G --> |Ya| H[Swap dan alokasi]
    G --> |Tidak atau swap penuh| I[OOM condition]
    I --> J[Hitung oom_score untuk semua proses]
    J --> K[Pilih proses dengan skor tertinggi]
    K --> L[Kirim SIGKILL ke proses terpilih]
    L --> M[Memory dibebaskan]
    M --> N[Coba alokasi lagi]
    N --> B

    style I fill:#ffcccc
    style L fill:#ffcccc

OOM Killer memilih proses berdasarkan oom_score — nilai antara 0 sampai 1000 yang dihitung oleh kernel. Semakin tinggi skornya, semakin besar kemungkinan proses itu yang akan dibunuh.

# Lihat oom_score proses saat ini
cat /proc/<PID>/oom_score

# Lihat semua proses dengan oom_score-nya
for pid in /proc/[0-9]*; do
    pid_num=$(basename $pid)
    score=$(cat $pid/oom_score 2>/dev/null)
    comm=$(cat $pid/comm 2>/dev/null)
    echo "$score $pid_num $comm"
done | sort -rn | head -20

# Output contoh:
# 850  12345  node
# 720  12346  python3
# 400  12347  java
# 50   1      systemd
# 0    2      kthreadd  (kernel thread — tidak pernah dibunuh)

Memahami oom_score dan oom_score_adj #

Faktor yang mempengaruhi oom_score:

  Faktor utama:
  → Ukuran memory yang digunakan oleh proses
    (termasuk memory yang di-share dengan proses lain)
  → Semakin banyak memory digunakan → skor lebih tinggi → lebih diprioritaskan untuk dibunuh

  Faktor modifikasi:
  → oom_score_adj: nilai -1000 sampai +1000 yang bisa diset oleh administrator
    - Nilai negatif: kurangi kemungkinan proses ini dibunuh
    - Nilai positif: tambah kemungkinan proses ini dibunuh
    - Nilai -1000: proses tidak akan pernah dibunuh oleh OOM Killer

  Rumus sederhana:
  oom_score = base_score + oom_score_adj
  di mana base_score dihitung dari memory usage relatif terhadap total system memory
# Melihat dan mengubah oom_score_adj

# Lihat oom_score_adj proses tertentu
cat /proc/<PID>/oom_score_adj

# Set oom_score_adj untuk proses yang sedang berjalan
# (perlu root atau capability CAP_SYS_RESOURCE)
echo -500 > /proc/<PID>/oom_score_adj
# Proses ini sekarang lebih tidak mungkin dibunuh

# Set oom_score_adj ke nilai minimum (proses tidak akan dibunuh)
echo -1000 > /proc/<PID>/oom_score_adj

# Untuk proses yang harus TIDAK PERNAH dibunuh (database, monitoring agent):
echo -1000 > /proc/$(pgrep postgres)/oom_score_adj
echo -1000 > /proc/$(pgrep mysqld)/oom_score_adj

# Set oom_score_adj via systemd service unit:
# [Service]
# OOMScoreAdjust=-1000
# Ini lebih reliable karena diterapkan saat service start
# Set oom_score_adj dari dalam aplikasi Python saat startup

import os

def protect_from_oom():
    """
    Kurangi kemungkinan proses ini dibunuh OOM Killer.
    Harus dipanggil dengan privilege yang cukup.
    """
    try:
        pid = os.getpid()
        oom_adj_path = f"/proc/{pid}/oom_score_adj"

        with open(oom_adj_path, 'w') as f:
            f.write("-500")  # Kurangi kemungkinan dibunuh signifikan

        current_score = int(open(f"/proc/{pid}/oom_score").read().strip())
        print(f"OOM protection set. Current oom_score: {current_score}")

    except PermissionError:
        print("Warning: Tidak bisa set OOM protection (perlu privilege)")
    except Exception as e:
        print(f"Warning: OOM protection gagal: {e}")

# Panggil saat aplikasi startup
protect_from_oom()

Mendeteksi dan Menginvestigasi OOM Event #

OOM event selalu meninggalkan jejak di kernel log. Memahami cara membacanya sangat penting untuk investigasi.

# Cari OOM event di kernel log
dmesg | grep -i "out of memory\|oom_kill\|kill process\|killed process"

# Atau dengan journalctl (systemd)
journalctl -k | grep -i "out of memory\|oom"

# Output OOM event yang khas:
# [1234567.890] Out of memory: Kill process 12345 (node) score 850 or sacrifice child
# [1234567.891] Killed process 12345 (node) total-vm:2097152kB, anon-rss:1048576kB, file-rss:4096kB
# [1234567.892] oom_reaper: reaped process 12345 (node), now anon-rss:0kB, file-rss:0kB

# Mengurai informasi dari OOM log:
# - Proses: node (PID 12345)
# - Score: 850 (tinggi = memang banyak pakai memory)
# - total-vm: virtual memory = 2GB
# - anon-rss: resident set size (memory fisik aktual) = 1GB
# - file-rss: memory dari file-mapped = 4MB
# Script untuk memonitor OOM event secara real-time
#!/bin/bash

echo "Monitoring OOM events (Ctrl+C untuk stop)..."
dmesg -w | grep --line-buffered -i "out of memory\|oom_kill\|killed process" | while read line; do
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] OOM EVENT: $line"

    # Kirim alert (contoh: via curl ke webhook)
    # curl -s -X POST "https://hooks.slack.com/..." \
    #     -H "Content-Type: application/json" \
    #     -d "{\"text\": \"OOM Event: $line\"}"
done
# Investigasi setelah OOM event — cari penyebabnya

# 1. Kapan OOM terjadi?
journalctl -k --since "1 hour ago" | grep -i oom

# 2. Proses apa yang dibunuh?
dmesg | grep "Killed process" | tail -20

# 3. Berapa memory yang tersedia saat OOM terjadi?
# Ini tersimpan di kernel log saat OOM
dmesg | grep -A 30 "Out of memory" | grep "Normal\|free\|active\|inactive"
# Output berisi snapshot memory state saat OOM terjadi

# 4. Apakah ada memory leak? (cek history sebelum OOM)
# Gunakan monitoring tool untuk melihat trend memory usage
# Biasanya memory leak terlihat sebagai grafik yang terus naik

# 5. Apakah ada proses lain yang juga besar?
dmesg | grep "oom_kill_process\|oom_score"

Konfigurasi vm.overcommit: Mengontrol Perilaku Alokasi Memory #

Linux secara default mengizinkan overcommit — proses bisa meminta memory lebih dari yang tersedia, dengan asumsi tidak semua memory akan benar-benar digunakan sekaligus. Ini adalah optimasi yang berguna tapi bisa menyebabkan OOM yang tidak terduga.

# Melihat setting overcommit saat ini
cat /proc/sys/vm/overcommit_memory

# Nilai yang mungkin:
# 0 = Heuristic overcommit (default)
#     Kernel mengizinkan overcommit tapi coba batasi yang terlalu berlebihan
#     Paling umum digunakan
#
# 1 = Always overcommit
#     Semua alokasi memory diizinkan tanpa batasan
#     Malloc() tidak pernah return NULL
#     Tapi OOM kill bisa terjadi kapan saja dan tidak bisa diprediksi
#     TIDAK DISARANKAN untuk production
#
# 2 = Never overcommit (strict mode)
#     Total committed memory tidak boleh melebihi swap + RAM * overcommit_ratio
#     Lebih aman, tapi malloc() bisa return NULL
#     Aplikasi harus siap handle allocation failure

# Melihat overcommit_ratio (default: 50%)
cat /proc/sys/vm/overcommit_ratio
# Total committed memory max = swap + (RAM * overcommit_ratio / 100)

# Melihat berapa memory yang sudah di-commit
cat /proc/meminfo | grep CommitLimit
cat /proc/meminfo | grep Committed_AS

# Commit_AS > CommitLimit = OOM condition bisa terjadi kapan saja
# Mengubah overcommit behavior (perlu root)

# Untuk database seperti PostgreSQL — strict mode lebih aman
# sysctl -w vm.overcommit_memory=2
# sysctl -w vm.overcommit_ratio=80  # 80% RAM + swap untuk committed memory

# Untuk aplikasi umum — biarkan default (0)
# atau tambahkan ke /etc/sysctl.conf untuk persistent:
# vm.overcommit_memory = 0
# vm.swappiness = 10  # Kurangi agresivitas swap (0-100)

# vm.swappiness: seberapa agresif kernel menggunakan swap
# 10 = hanya swap jika benar-benar perlu (recommended untuk production server)
# 60 = default
# 0  = jangan swap kecuali terpaksa
echo 10 > /proc/sys/vm/swappiness

Melindungi Proses Kritis #

Tidak semua proses sama pentingnya. Database, monitoring agent, dan proses inti sistem tidak boleh dibunuh OOM Killer.

# Proteksi via systemd (cara terbaik — persistent dan otomatis)

# Buat atau edit service file
cat > /etc/systemd/system/myapp.service << 'EOF'
[Unit]
Description=My Critical Application
After=network.target

[Service]
ExecStart=/usr/bin/myapp
Restart=always
RestartSec=5s

# OOM protection — nilai -1000 sampai +1000
# -1000 = tidak pernah dibunuh OOM
# -500  = sangat tidak mungkin dibunuh
OOMScoreAdjust=-500

# Memory limit (opsional) — mencegah satu proses monopoli memory
# Jika melebihi ini, process akan di-kill SEBELUM menyebabkan OOM seluruh sistem
MemoryMax=2G
MemoryHigh=1.5G  # Soft limit — mulai throttle di sini

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable myapp
systemctl start myapp
# Proteksi untuk database yang sudah berjalan

# PostgreSQL
PG_PID=$(pgrep -x postgres | head -1)
echo -900 > /proc/$PG_PID/oom_score_adj
echo "PostgreSQL OOM score adj set to: $(cat /proc/$PG_PID/oom_score_adj)"

# MySQL / MariaDB
MYSQL_PID=$(pgrep -x mysqld)
echo -900 > /proc/$MYSQL_PID/oom_score_adj

# Redis
REDIS_PID=$(pgrep -x redis-server)
echo -900 > /proc/$REDIS_PID/oom_score_adj

# Script untuk set OOM protection semua service kritis setelah boot
cat > /usr/local/bin/set-oom-protection.sh << 'EOF'
#!/bin/bash
# Set OOM protection untuk proses kritis

declare -A CRITICAL_PROCESSES=(
    ["postgres"]=-900
    ["mysqld"]=-900
    ["redis-server"]=-900
    ["prometheus"]=-500
    ["node_exporter"]=-500
)

for process_name in "${!CRITICAL_PROCESSES[@]}"; do
    adj_value="${CRITICAL_PROCESSES[$process_name]}"
    pids=$(pgrep -x "$process_name" 2>/dev/null)
    for pid in $pids; do
        echo "$adj_value" > "/proc/$pid/oom_score_adj" 2>/dev/null
        echo "Set OOM adj=$adj_value untuk $process_name (PID: $pid)"
    done
done
EOF
chmod +x /usr/local/bin/set-oom-protection.sh

Konfigurasi Memory Limit per Proses #

Strategi yang lebih proaktif dari mengandalkan OOM Killer adalah memberi setiap proses batas memory — sehingga jika satu proses mengalami memory leak, ia di-kill sendiri sebelum menyebabkan OOM seluruh sistem.

# Menggunakan cgroups v2 (via systemd) untuk membatasi memory

# Contoh: limit aplikasi Node.js ke maksimal 512MB
systemctl edit nodejs-app.service
# Tambahkan di [Service]:
# MemoryMax=512M
# MemoryHigh=400M  # Mulai throttle di 400MB, kill di 512MB

# Verifikasi:
systemctl show nodejs-app.service | grep Memory

# Menggunakan ulimit untuk session saat ini
ulimit -v 524288  # 512MB dalam kilobytes (virtual memory limit)
# Setelah ini, program yang berjalan di session ini tidak bisa alokasi lebih dari 512MB

# Menggunakan Docker untuk isolasi memory
docker run \
    --memory="512m" \               # Hard limit
    --memory-reservation="256m" \   # Soft limit (warning tapi tidak kill)
    --oom-kill-disable=false \       # Izinkan OOM kill (default)
    myapp
# Jika container melebihi --memory, Docker mengirim SIGKILL

# Jangan gunakan --oom-kill-disable=true kecuali sangat yakin
# karena ini bisa menyebabkan host system OOM

Mendeteksi Memory Leak #

OOM kill yang berulang hampir selalu mengindikasikan memory leak di salah satu proses.

# Monitoring memory usage trend untuk deteksi memory leak

import psutil
import time
import logging
from collections import deque

logger = logging.getLogger(__name__)

class MemoryLeakDetector:
    """
    Deteksi kemungkinan memory leak berdasarkan trend penggunaan memory.
    """

    def __init__(
        self,
        pid: int,
        sample_interval: int = 60,      # detik antar sample
        window_size: int = 30,           # jumlah sample untuk trend
        growth_threshold_pct: float = 5.0  # pertumbuhan % per jam yang dianggap mencurigakan
    ):
        self.pid = pid
        self.sample_interval = sample_interval
        self.window_size = window_size
        self.growth_threshold_pct = growth_threshold_pct
        self.samples = deque(maxlen=window_size)

    def collect_sample(self) -> dict | None:
        """Ambil sample memory usage saat ini."""
        try:
            proc = psutil.Process(self.pid)
            mem_info = proc.memory_info()

            sample = {
                'timestamp': time.time(),
                'rss_mb': mem_info.rss / 1024 / 1024,  # Resident Set Size
                'vms_mb': mem_info.vms / 1024 / 1024,  # Virtual Memory Size
            }
            self.samples.append(sample)
            return sample
        except psutil.NoSuchProcess:
            return None

    def analyze_trend(self) -> dict:
        """Analisis apakah ada tren pertumbuhan yang mencurigakan."""
        if len(self.samples) < 5:
            return {'status': 'insufficient_data'}

        samples_list = list(self.samples)
        oldest = samples_list[0]
        newest = samples_list[-1]

        time_diff_hours = (newest['timestamp'] - oldest['timestamp']) / 3600
        if time_diff_hours < 0.01:
            return {'status': 'insufficient_time'}

        rss_growth_mb = newest['rss_mb'] - oldest['rss_mb']
        rss_growth_pct_per_hour = (rss_growth_mb / oldest['rss_mb']) * 100 / time_diff_hours

        is_leaking = rss_growth_pct_per_hour > self.growth_threshold_pct

        result = {
            'pid': self.pid,
            'current_rss_mb': round(newest['rss_mb'], 1),
            'oldest_rss_mb': round(oldest['rss_mb'], 1),
            'growth_mb': round(rss_growth_mb, 1),
            'growth_pct_per_hour': round(rss_growth_pct_per_hour, 2),
            'time_window_hours': round(time_diff_hours, 2),
            'is_likely_leaking': is_leaking,
            'status': 'warning' if is_leaking else 'ok'
        }

        if is_leaking:
            logger.warning(
                "Possible memory leak detected",
                extra=result
            )

        return result

# Penggunaan:
# detector = MemoryLeakDetector(pid=os.getpid())
# while True:
#     detector.collect_sample()
#     analysis = detector.analyze_trend()
#     if analysis.get('is_likely_leaking'):
#         alert_team(analysis)
#     time.sleep(60)
# Profiling memory di Python untuk menemukan leak
# pip install memory-profiler tracemalloc

import tracemalloc
import linecache

def start_memory_tracing():
    """Mulai tracking alokasi memory."""
    tracemalloc.start(10)  # simpan 10 frame terakhir

def get_top_memory_allocations(limit: int = 20) -> list:
    """Dapatkan lokasi kode yang paling banyak mengalokasi memory."""
    snapshot = tracemalloc.take_snapshot()
    top_stats = snapshot.statistics('lineno')

    results = []
    for stat in top_stats[:limit]:
        frame = stat.traceback[0]
        results.append({
            'file': frame.filename,
            'line': frame.lineno,
            'size_kb': round(stat.size / 1024, 1),
            'count': stat.count,
            'code': linecache.getline(frame.filename, frame.lineno).strip()
        })

    return results

def compare_snapshots():
    """Bandingkan dua snapshot untuk melihat pertumbuhan."""
    snapshot1 = tracemalloc.take_snapshot()
    time.sleep(60)  # tunggu 1 menit
    snapshot2 = tracemalloc.take_snapshot()

    top_stats = snapshot2.compare_to(snapshot1, 'lineno')

    print("Top memory growth:")
    for stat in top_stats[:10]:
        print(f"  {stat}")

Penanganan OOM Kill di Aplikasi #

Karena OOM kill menggunakan SIGKILL yang tidak bisa di-catch, tidak ada cara untuk melakukan cleanup saat OOM kill terjadi. Tapi ada beberapa hal yang bisa dilakukan:

# 1. Graceful shutdown saat menerima sinyal yang bisa di-catch
# (OOM kill duluan mengirim SIGTERM ke beberapa sistem — tidak selalu)
import signal
import sys

def handle_sigterm(signum, frame):
    """Graceful shutdown handler."""
    logger.info("Received SIGTERM — cleaning up...")

    # Simpan state penting
    save_checkpoint()

    # Tutup koneksi database
    db.session.close()

    # Tunggu request yang sedang diproses selesai
    # (dengan timeout)
    server.shutdown(timeout=10)

    sys.exit(0)

signal.signal(signal.SIGTERM, handle_sigterm)

# 2. Checkpoint reguler — jangan simpan state penting hanya di memory
import pickle
import time

class StatefulProcessor:
    def __init__(self, checkpoint_file: str):
        self.checkpoint_file = checkpoint_file
        self.state = self._load_checkpoint()

    def _load_checkpoint(self):
        try:
            with open(self.checkpoint_file, 'rb') as f:
                return pickle.load(f)
        except FileNotFoundError:
            return {'processed': 0, 'last_id': None}

    def save_checkpoint(self):
        """Simpan state ke disk secara reguler."""
        with open(self.checkpoint_file + '.tmp', 'wb') as f:
            pickle.dump(self.state, f)
        os.replace(self.checkpoint_file + '.tmp', self.checkpoint_file)
        # Atomic write: tidak ada intermediate state yang corrupt

    def process_batch(self, items):
        for item in items:
            self.process_item(item)
            self.state['processed'] += 1
            self.state['last_id'] = item.id

            # Checkpoint setiap 1000 item
            if self.state['processed'] % 1000 == 0:
                self.save_checkpoint()

Monitoring dan Alert untuk OOM Event #

# Setup alert menggunakan systemd dan script monitoring

# Script monitoring OOM events
cat > /usr/local/bin/oom-monitor.sh << 'SCRIPT'
#!/bin/bash

LOG_FILE="/var/log/oom-events.log"
WEBHOOK_URL="${OOM_ALERT_WEBHOOK}"

# Monitor kernel log untuk OOM events
journalctl -kf | grep --line-buffered "Out of memory\|Killed process\|oom_kill" | \
while read -r line; do
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] $line" >> "$LOG_FILE"

    # Kirim alert
    if [ -n "$WEBHOOK_URL" ]; then
        curl -s -X POST "$WEBHOOK_URL" \
            -H "Content-Type: application/json" \
            -d "{
                \"event\": \"oom_kill\",
                \"host\": \"$(hostname)\",
                \"message\": \"$line\",
                \"timestamp\": \"$timestamp\"
            }" > /dev/null
    fi

    # Log memory state saat terjadi OOM
    free -h >> "$LOG_FILE"
    echo "---" >> "$LOG_FILE"
done
SCRIPT

chmod +x /usr/local/bin/oom-monitor.sh
# Prometheus metrics untuk OOM monitoring

from prometheus_client import Counter, Gauge
import subprocess

oom_events_total = Counter(
    'oom_kill_events_total',
    'Total number of OOM kill events'
)

memory_available_bytes = Gauge(
    'memory_available_bytes',
    'Available memory in bytes'
)

def parse_oom_events():
    """Parse OOM events dari kernel log."""
    result = subprocess.run(
        ['journalctl', '-k', '--since', '1 minute ago', '--no-pager'],
        capture_output=True, text=True
    )
    count = result.stdout.lower().count('out of memory')
    if count > 0:
        oom_events_total.inc(count)
    return count

def update_memory_metrics():
    """Update memory metrics untuk Prometheus."""
    import psutil
    mem = psutil.virtual_memory()
    memory_available_bytes.set(mem.available)

Anti-Pattern yang Harus Dihindari #

# ✗ Anti-pattern 1: mengabaikan OOM event
# "Server restart, semua normal kembali"
# OOM kill tanpa investigasi berarti masalah akan berulang
# ✓ Setiap OOM event harus diinvestigasi: kenapa terjadi, proses apa yang dibunuh,
#   apakah ada memory leak, apakah capacity planning perlu direvisi

# ✗ Anti-pattern 2: oom_kill_disable=true di Docker
# docker run --oom-kill-disable=true myapp
# Jika container mengalami memory leak, ia bisa membuat HOST system OOM
# ✓ Gunakan --memory limit dan biarkan OOM kill container, bukan host

# ✗ Anti-pattern 3: vm.overcommit_memory=1 (always overcommit)
# Malloc() tidak pernah gagal tapi OOM bisa terjadi kapan saja dan tidak bisa diprediksi
# ✓ Gunakan default (0) atau strict mode (2) untuk database server

# ✗ Anti-pattern 4: tidak ada memory limit per proses/container
# Satu proses yang bocor bisa menyebabkan seluruh sistem OOM
# ✓ Set MemoryMax di systemd atau --memory di Docker untuk isolasi

# ✗ Anti-pattern 5: tidak ada monitoring memory trend
# Memory leak terdeteksi hanya setelah OOM terjadi
# ✓ Monitor memory usage trend per proses, alert jika ada pertumbuhan mencurigakan

# ✗ Anti-pattern 6: mengubah oom_score_adj semua proses ke -1000
# Jika semua proses "tidak bisa dibunuh", OOM akan menyebabkan kernel panic
# ✓ Hanya proses yang benar-benar kritis yang perlu perlindungan penuh
#   Sisanya biarkan OOM Killer bisa memilih

Checklist OOM Killer Management #

DETEKSI DAN MONITORING:
  □ Alert terpasang untuk OOM event (journalctl atau dmesg monitoring)
  □ Memory usage trend per proses dimonitor (bukan hanya total)
  □ Alert jika memory available < 15% (sebelum OOM terjadi)
  □ Alert jika swap mulai digunakan (tanda awal memory pressure)
  □ OOM event di-log dengan detail (proses apa, berapa memory-nya)

KONFIGURASI PROSES KRITIS:
  □ Database (PostgreSQL, MySQL, Redis) memiliki oom_score_adj yang rendah
  □ Monitoring agent tidak bisa dibunuh OOM (diperlukan untuk alert saat krisis)
  □ Konfigurasi OOM protection via systemd (OOMScoreAdjust) — bukan manual
  □ Aplikasi yang bisa di-restart memiliki OOMScoreAdjust yang lebih tinggi

MEMORY LIMITS:
  □ Setiap container Docker memiliki --memory limit
  □ Setiap systemd service memiliki MemoryMax jika relevant
  □ Memory limit diset berdasarkan profiling, bukan angka sembarang

OVERCOMMIT:
  □ vm.overcommit_memory dikonfigurasi sesuai kebutuhan (bukan default buta)
  □ vm.swappiness diset rendah untuk server (10 adalah titik awal yang baik)
  □ CommitLimit > Committed_AS dipantau (indikator seberapa dekat ke OOM)

INVESTIGASI:
  □ Setiap OOM event diinvestigasi — tidak diabaikan
  □ Ada runbook untuk investigasi OOM (langkah-langkah yang harus dilakukan)
  □ Memory leak dideteksi dan diperbaiki, bukan hanya restart service

CAPACITY PLANNING:
  □ Total memory usage semua proses diukur di peak load
  □ Growth rate memory usage dipantau
  □ Ada headroom 30% untuk mencegah OOM saat spike

Ringkasan #

  • OOM Killer adalah mekanisme darurat, bukan fitur — jika OOM Killer aktif, ada yang salah dengan manajemen memory di sistem. Setiap OOM event harus diinvestigasi, bukan diabaikan.
  • oom_score ditentukan oleh memory usage — proses yang menggunakan banyak memory mendapat skor tinggi dan lebih mungkin dibunuh. oom_score_adj memungkinkan adjustment manual untuk proses kritis.
  • Database dan monitoring agent harus dilindungi — set oom_score_adj ke -1000 (via systemd OOMScoreAdjust) untuk proses yang tidak boleh dibunuh. Jangan set semua proses ke -1000 — OOM Killer butuh proses yang bisa dibunuh.
  • SIGKILL tidak bisa di-catch — saat OOM kill terjadi, tidak ada cleanup yang bisa dilakukan. Strategi pencegahan (checkpoint reguler, memory limits) jauh lebih baik dari strategi recovery.
  • vm.overcommit_memory=1 adalah pilihan berbahaya — malloc() tidak pernah gagal tapi OOM kill bisa terjadi kapan saja tanpa bisa diprediksi. Gunakan default atau strict mode.
  • Memory limit per proses mengisolasi kegagalan — jika satu proses mengalami memory leak dan punya MemoryMax, ia di-kill sendiri tanpa menyebabkan OOM seluruh sistem.
  • Swap yang aktif adalah tanda memory saturasi — akses swap ribuan kali lebih lambat dari RAM. Set vm.swappiness rendah (10) dan alert saat swap mulai digunakan.
  • Memory leak terdeteksi dari trend, bukan snapshot — monitoring memory usage setiap menit dan analisis pertumbuhannya jauh lebih berguna dari hanya melihat penggunaan saat ini.
  • dmesg dan journalctl -k adalah sumber kebenaran untuk OOM — setiap OOM event meninggalkan jejak detail di kernel log termasuk state memory saat kejadian.
  • Capacity planning mencegah OOM — headroom 30% dari total memory, scaling sebelum mencapai saturasi, dan load testing untuk mengetahui batas kapasitas adalah pertahanan terbaik.

← Sebelumnya: Saturation   Berikutnya: Infrastructure as Code →

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