Post-Mortem & Incident Runbook: Mastering Linux Crontab di Skala Enterprise
1. Alarm Tengah Malam: Saat Layanan Produksi Mulai Tumbang
Pukul 02:14 WIB, pagerduty saya menjerit melengking di samping tempat tidur. Bukan alarm biasa, melainkan eskalasi P0 dari sistem monitoring infrastruktur yang menandakan latensi database MySQL produksi melonjak hingga melampaui 8.000 milidetik, sementara queue di Redis membengkak menembus angka jutaan item. Bagi seorang Principal Infrastructure Engineer, jam-jam dini hari seperti ini adalah medan uji ketahanan mental. Saya langsung melompat dari kasur, membuka laptop, dan melakukan tunneling VPN ke dalam private subnet pusat data kami.
Layanan utama kamiโsebuah platform e-commerce berbasis backend Go dan PHP-FPM dengan Nginx sebagai reverse proxyโtiba-tiba merangkak mati. Pengguna mulai mengeluhkan gateway timeout, halaman checkout berputar tanpa henti, dan email transaksional mandek di antrean Postfix. Awalnya, kecurigaan mengarah pada lonjakan trafik bot atau serangan DDoS yang menembus lapisan Cloudflare WAF kami. Namun, saat saya memeriksa dashboard Grafana, metrik penggunaan CPU dan RAM pada node worker backend menunjukkan pola yang sangat spesifik dan berulang, bukan pola serangan eksternal. Sesuatu yang berada di dalam sistem sedang memakan seluruh sumber daya secara brutal pada detik-detik tertentu.
Sesuatu yang beroperasi secara otomatis, tersembunyi, dan luput dari pengawasan ketat arsitektur modern kita. Ya, pelakunya adalah cron job yang berjalan setiap jam, sebuah skrip otomatisasi sederhana yang malam itu berubah menjadi monster yang melumpuhkan seluruh infrastruktur web kita. Mari kita telusuri jejak digital yang ditinggalkannya di balik layar terminal.
2. Jejak Digital: Menelusuri Anomali Log & Lonjakan Metrik
Dengan akses root di tangan, langkah pertama saya adalah menyelam ke dalam log sistem untuk mencari korelasi antara waktu lonjakan beban dan aktivitas proses latar belakang. Saya membuka terminal dan langsung memeriksa log daemon cron sistem menggunakan systemd journal:
journalctl -u cron --since "1 hour ago" --until "now"
Baris-baris log mengalir deras, dan di antara tumpukan tersebut, sebuah pola mengerikan mulai terlihat. Tepat pukul 02:00:00, cron memicu sebuah skrip pemeliharaan basis data dan sinkronisasi inventaris (inventory-sync.php). Masalahnya, skrip yang sama yang dijadwalkan berjalan setiap 10 menit ternyata belum selesai mengeksekusi iterasi sebelumnya ketika iterasi berikutnya dipanggil.
[!WARNING] Cron tidak peduli apakah eksekusi sebelumnya sudah selesai atau belum. Jika jadwal tiba, ia akan meluncurkan proses baru tanpa ampun, memicu efek bola salju (snowball effect) pada konsumsi memori dan koneksi database.
Untuk membuktikan hipotesis ini, saya memeriksa proses yang sedang aktif menggunakan perintah ps yang dikombinasikan dengan pencarian string skrip PHP kami:
ps aux | grep inventory-sync.php
Ternyata, ada lebih dari 45 instans proses PHP-CLI yang berjalan secara bersamaan, masing-masing membuka koneksi pool MySQL yang independen. Pool koneksi database langsung habis (connection exhaustion), mengakibatkan ribuan thread backend web server mengalami deadlock saat mencoba menulis transaksi baru. Ini bukan sekadar masalah skrip lambat, melainkan kegagalan fundamental dalam manajemen konkurensi tugas terjadwal.
3. Membongkar Biang Kerok: Akar Masalah yang Sebenarnya
Setelah menggali lebih dalam pada kode skrip dan konfigurasi crontab, kami menemukan tiga dosa besar arsitektur yang sering diabaikan bahkan oleh engineer berpengalaman sekalipun.
Pertama, tidak ada mekanisme penguncian (locking) proses. Pengembang menulis crontab dengan baris sederhana seperti ini:
*/10 * * * * /usr/bin/php /var/www/html/artisan schedule:run >> /var/log/cron.log 2>&1
Kedua, jebakan Environment Variable. Ketika dijalankan secara manual oleh user deploy, skrip berjalan dengan mulus karena variabel PATH, kredensial AWS S3, dan konfigurasi database termuat dari .bashrc atau .env. Namun, ketika dieksekusi oleh daemon cron, environment variabel tersebut kosong atau berbeda, menyebabkan skrip gagal mendeteksi path biner yang benar dan sering kali jatuh ke kondisi silent fail atau menggunakan konfigurasi default yang salah.
Ketiga, tidak ada sistem observability yang layak. Output dialihkan ke file log lokal tanpa rotasi yang benar, membuat disk penuh dalam hitungan minggu, dan tidak ada satupun mekanisme pemberitahuan instan ke kanal komunikasi tim ketika sebuah tugas gagal di tengah jalan. Kita butuh tindakan darurat sekarang juga sebelum layanan benar-benar tumbang permanen.
4. Tindakan Darurat: Menghidupkan Kembali Layanan dalam Hitungan Menit
Situasi kritis menuntut eksekusi kilat tanpa keraguan. Dalam waktu kurang dari 60 detik, saya harus membersihkan kekacauan proses yang berjalan dan menormalkan kembali koneksi database MySQL dan Redis.
Berikut adalah rangkaian perintah darurat one-liner terminal yang saya eksekusi untuk menyelamatkan sistem saat itu juga:
# 1. Hentikan paksa seluruh instans proses cron yang membebani sistem
pkill -9 -f "inventory-sync.php"
# 2. Bersihkan koneksi menggantung (idle/locked threads) di MySQL
mysql -u root -p'SecureRootPassword' -e "SHOW PROCESSLIST;" | awk '$Time > 60 {print "KILL " $1 ";\