Level 4 · Mata yang Tak Pernah Tidur
Sesi 32: Pipa yang Tak Terlihat (Sidecar, Fluent Bit, & Fluentd)
Bagaimana jika aplikasi sudah menulis log dengan benar, tapi saat insiden tiba, log dari pod yang sudah mati ikut hilang bersamanya — dan tidak ada satu orang pun yang tahu file itu pernah ada?
1. Bayangkan Jika: Pabrik dengan Buku Catatan Pribadi
Bayangkan sebuah pabrik dengan dua belas mesin produksi. Setiap mesin punya buku catatan pribadi yang diletakkan di sampingnya. Setiap kali mesin mengalami keanehan, operator menulis di buku itu: jam berapa, apa yang aneh, apa yang dia lakukan.
Sekarang bayangkan aturan pabriknya begini: begitu satu mesin rusak dan diganti mesin baru, buku catatan mesin lama langsung dibuang bersama mesinnya. Tidak ada yang menyalin. Tidak ada yang mengarsipkan.
Suatu malam mesin nomor 7 rusak pukul 03:12. Paginya tim datang, mesin pengganti sudah terpasang rapi, produksi jalan lagi. Semua orang lega — sampai seseorang bertanya: "sebenarnya mesin 7 rusak kenapa?" Dan jawabannya: tidak ada yang tahu, karena catatan satu-satunya sudah jadi sampah sejak pukul 03:13.
Inilah yang terjadi pada log container kalau kita membiarkannya tinggal di dalam pod.
2. Apa yang Sebenarnya Terjadi
2.1 Container adalah Proses, Bukan Kotak yang Awet
Pod dan container dirancang untuk mati tanpa penyesalan. Pod dibunuh, digantikan, dipindahkan ke node lain, di-scale down — semua ini terjadi puluhan kali sehari tanpa ada yang menyadarinya. Ini fitur, bukan bug.
Konsekuensinya untuk log: apa pun yang disimpan di dalam filesystem container punya umur yang sama dengan container itu. Begitu container dibunuh, isi tulisannya lenyap.
2.2 Kenapa Log Diambil dari stdout, Bukan dari File
Standar 12-Factor (faktor ke-11) menyebutnya jelas: perlakukan log sebagai aliran peristiwa, bukan file. Aplikasi menulis ke stdout dan stderr, lalu runtime container (Docker, containerd) yang menangkap aliran itu dan menyimpannya di luar container.
Kenapa ini penting:
| Aspek | Tulis ke file di dalam pod | Tulis ke stdout |
|---|---|---|
| Umur data | Sama dengan umur container | Dikelola runtime, tetap ada saat container mati |
| Rotasi | Aplikasi harus urus sendiri | Runtime yang urus |
| Pengambilan | Harus masuk ke dalam pod | Cukup baca stream dari luar |
| Restart pod | File hilang, perlu volume khusus | Langsung tertangkap agent |
| Konsistensi | Tiap aplikasi beda format | Satu titik kontrol |
Jebakannya: container hanya menangkap satu aliran per proses utama. Aplikasi multi-proses yang menulis ke file internal (misal log nginx atau Java app server dengan file log sendiri) akan membuat aliran itu tidak terlihat oleh runtime — dan itu penyebab paling umum "kenapa log aplikasi saya tidak muncul di Kibana".
2.3 Tiga Pola Penempatan Pengambil Log
Pertanyaannya bukan "pakai alat apa", tapi di mana alat itu diletakkan.
| Pola | Cara Kerja | Kelebihan | Biaya |
|---|---|---|---|
| Sidecar | Satu kontainer pendamping per pod, membaca aliran container utama | Isolasi bersih, bisa punya config per aplikasi, tidak berbagi sumber daya | Boros: 1 pod × 3 kontainer = 3× jumlah pod yang harus dijalankan. Untuk 50 pod, ini 50 proses tambahan |
| DaemonSet / Node Agent | Satu agent per node, membaca semua container di node itu | Hemat: 3 node = 3 agent, bukan 50. Sumber daya terpusat | Berbagi CPU/RAM node dengan aplikasi. Config harus generik. Kalau agent mati, semua log node itu tertahan |
| Library di Aplikasi | Aplikasi mengirim log langsung ke backend (SDK) | Paling sedikit hop, latensi paling rendah | Mencampur urusan bisnis dengan pipa log. Ganti backend = ubah kode + rilis ulang 30 layanan |
Untuk kebanyakan sistem, DaemonSet adalah default yang benar, dan sidecar dipakai sebagai pengecualian untuk aplikasi yang butuh perlakuan khusus (misalnya yang menulis ke file internal dan butuh tail dari sidecar).
2.4 Fluent Bit vs Fluentd: Forwarder dan Aggregator
Dua nama ini sering disebut bergantian, padahal perannya berbeda.
- Fluent Bit — ditulis dalam C, footprint sekitar 1 MB, konsumsi RAM rendah, dirancang untuk dipasang di setiap node sebagai forwarder. Tugasnya sempit: ambil dari sumber, beri tag, teruskan.
- Fluentd — ditulis dalam Ruby (dan C untuk sebagian inti), ratusan plugin, kaya transformasi (parsing, enrichment, routing kompleks, filtering), dirancang sebagai aggregator di tengah.
Arsitektur yang sehat adalah dua tingkat:
[Node 1] Fluent Bit ─┐
[Node 2] Fluent Bit ─┼──► [Fluentd Aggregator] ──► Elasticsearch
[Node 3] Fluent Bit ─┘ (1-2 instance)
Menaruh Fluentd di setiap node adalah pilihan yang salah secara ekonomi: kamu membayar konsumsi memori Ruby di setiap node hanya untuk pekerjaan yang bisa dilakukan satu instance terpusat.
2.5 Kegagalan yang Senyap — Ini Bagian Paling Berbahaya
Pipa log gagal dengan cara yang tidak berteriak. Berbeda dari aplikasi yang error dan mengirim 500, pipa log yang rusak hanya jadi lebih sepi.
- Buffer penuh → log dibuang. Forwarder punya buffer terbatas. Kalau backend lambat atau mati, buffer penuh, dan kebijakan default biasanya membuang data paling lama agar tidak menahan aplikasi. Hasilnya: saat insiden terbesar, justru log insiden itu yang hilang.
- Retry tanpa batas. Kalau dikonfigurasi "jangan pernah buang", buffer akan tumbuh terus sampai memori node habis — dan yang tumbang bukan pipa log saja, tapi aplikasi di node yang sama.
- Disk node penuh. Buffer berbasis file (
filesystem storage) akan memakan disk node. Disk penuh = container tidak bisa menulis = aplikasi di node itu ikut mati. - Multi-line log yang terpecah. Stack trace Java/YAML/Python adalah satu peristiwa log tapi tercatat sebagai 40 baris terpisah. Tanpa
multiline parseryang mengerti polanya, pencarian di Kibana akan mengembalikan 40 dokumen tak berarti, dan akar masalahnya tidak pernah terbaca utuh. - Agent jadi single point of failure. Semua pod di satu node bergantung pada satu agent. Agent crash, config salah, atau OOM — dan seluruh log node itu hilang tanpa alarm.
Prinsip yang harus dipegang: pipa log wajib punya metrik untuk dirinya sendiri — berapa baris masuk, berapa baris keluar, berapa yang dibuang, seberapa penuh buffer. Tanpa itu, kamu tidak akan pernah tahu bahwa log-mu sudah separuh hilang; kamu hanya akan tahu saat paling membutuhkannya.
3. Bagaimana Jika Kita Coba... (Solusi Naif)
"Kita simpan saja log di file, lalu mount volume supaya tetap ada."
Ini bekerja untuk satu aplikasi yang tinggal di satu server. Tapi begitu masuk ke orkestrasi container: pod berpindah node, nama pod berubah setiap restart, dan volume yang tidak dikelola dengan baik akan menumpuk sampah — siapa yang membersihkan file log dari 200 pod yang sudah mati bulan lalu? Ini solusi yang memindahkan masalah, bukan menyelesaikannya.
"Kita baca file log-nya langsung dari pod dengan SSH."
Ini adalah cara orang bekerja sebelum ada pipa log. Konsekuensinya: kamu butuh akses, kamu butuh pod-nya masih hidup, dan saat insiden paling penting (pod yang sudah dibunuh), caranya tidak berlaku sama sekali.
"Satu vendor, satu alat, semua beres."
Tergoda, dan terkadang benar. Tapi itulah pertanyaan yang kita bahas di Sesi 34 — dengan harga yang harus dibayar lebih dulu di Sesi 33.
4. Nama Resminya
| Istilah | Arti praktis |
|---|---|
| Sidecar | Kontainer pendamping dalam satu pod, berbagi jaringan dan volume, mengambil alih satu tanggung jawab |
| DaemonSet | Beban kerja Kubernetes yang menjamin satu pod berjalan di setiap node |
| Node agent | Proses pengambil log yang berjalan di node, di luar container aplikasi |
| stdout / stderr | Dua aliran standar proses; cara aplikasi menyerahkan lognya ke runtime |
| Log driver | Mekanisme runtime container yang menangkap aliran stdout/stderr |
| Fluent Bit | Forwarder ringan (C, ~1 MB) untuk dipasang di node |
| Fluentd | Aggregator kaya plugin (Ruby) untuk dipasang terpusat |
| Forwarder / Aggregator | Dua peran berbeda dalam arsitektur pipa log dua tingkat |
| Buffer | Penyimpanan sementara saat backend lambat; di sinilah log hilang secara senyap |
| Flush interval | Seberapa sering buffer dikirim ke backend; kompromi antara latensi dan efisiensi |
| Multiline parser | Aturan yang menggabungkan banyak baris jadi satu peristiwa log |
| Backpressure | Kondisi saat produsen lebih cepat dari konsumen; sumber utama log loss |
| Log loss | Kehilangan data log tanpa alarm — kegagalan yang paling berbahaya karena senyap |
5. Di Dunia Kita
Di sistem produksi dengan puluhan sampai ratusan pod, pipa log adalah infrastruktur, bukan konfigurasi. Ciri sistem yang pipa log-nya matang:
- Aplikasi hanya menulis ke stdout, tidak ada file log lokal yang dirotasi sendiri.
- Setiap node punya satu agent (Fluent Bit / Promtail / OTel Collector).
- Aggregator terpusat melakukan parsing dan routing, bukan edge.
- Ada dashboard untuk pipa log itu sendiri: baris/detik, tingkat drop, ukuran buffer, keterlambatan pengiriman.
- Multiline stack trace sudah dikenali sebagai satu peristiwa.
- Ada kebijakan retensi yang keputusan bisnisnya jelas.
Ciri sistem yang belum matang: log "ada di Kibana", tapi saat insiden terbesar, tidak ada satu pun baris dari pod yang bermasalah. Inilah kegagalan yang paling sering terjadi, dan paling jarang disadari.
6. Lensa Performance Tester
Angka yang harus diminta begitu melihat pipa log di sebuah design doc:
- Log engine throughput — berapa baris/detik yang sanggup ditangani pipa pada beban puncak? Ini diuji dengan mengirim trafik uji sambil mengukur berapa baris yang benar-benar sampai.
- Drop rate — minta angka eksplisit: berapa persen log yang dibuang saat backend lambat. Kalau tidak ada angkanya, tidak ada yang pernah mengukur.
- Overhead di node — berapa CPU dan memori yang dipakai agent, diukur saat beban penuh, bukan saat idle. Agent yang tenang di lab bisa jadi rakus saat aplikasi berteriak.
- Buffer depth dan drain time — berapa lama pipa mampu menahan data saat backend mati total sebelum mulai membuang?
- Skenario uji: matikan backend log (Elastic) selama 5 menit saat load test berjalan. Berapa banyak log yang hilang? Apakah aplikasi tetap jalan? Apakah ada alarm?
- Kesiapan multiline — pastikan stack trace dibaca sebagai satu peristiwa, bukan 40. Uji dengan melemparkan exception bertumpuk.
7. Pertanyaan untuk Ronde Berikutnya
"Log-nya sudah terkumpul rapi di satu tempat. Tapi menyimpan semua itu supaya bisa dicari dalam 1 detik — berapa sebenarnya harganya? Dan kenapa para engineer tiba-tiba tidak bisa menulis log sama sekali begitu disk penuh?"
(Lanjut ke Sesi 33: Harga Sebuah Index — Elasticsearch & Kibana)