Swap storm di staging bisa kita ceritakan dengan tenang karena sudah selesai, tetapi kejadian nyata jarang memberi kehormatan itu: puncak datang jam 10 saat kita sedang makan siang, dan docker stats yang kita buka sore harinya hanya menampilkan sisa-sesa kejadian. Potret menjawab “berapa sekarang”, sedangkan keputusan angka — naikkan limit atau tidak, beli RAM atau tidak — menuntut “berapa puncaknya dan seberapa sering ia kembali”. Chapter Docker Stats di Section Observability dan Troubleshooting memberi kita kamera; chapter ini membangun perekamnya: membaca angka langsung dari berkas cgroups tempat kernel menulisnya, merekam tren ke CSV dengan skrip sederhana, menetapkan ambang yang berteriak sebelum kernel bersuara, lalu menutup dengan ritual pemantauan yang bisa dijalankan satu orang yang sibuk.
Tren yang Menghasilkan Keputusan #
Tiga alasan mengapa tren selalu mengalahkan snapshot dalam urusan resource. Pertama, puncak bersifat episodik: rekap bulanan, flash sale, dan backup menyala pada jam yang berbeda, dan potret acak hampir pasti melewatkan semuanya. Kedua, angka tunggal tidak bisa membedakan kegentingan sesaat dari penyakit menetap — lonjakan MEM % selama lima menit adalah berita biasa, lonjakan yang kembali tiap hari pada jam yang sama adalah permintaan perubahan. Ketiga, keputusan menetapkan limit adalah keputusan jangka panjang: angka yang kita tulis di compose.yaml akan dibaca berbulan-bulan, jadi ia berhak dibangun di atas data berhari-hari, bukan atas satu sore pengamatan.
Perekam yang kita bangun hari ini menarik angka dari dua sumber yang sama sekali berbeda wujudnya: docker stats --no-stream sebagai potret sekali jalan yang ramah skrip, dan berkas-berkas cgroups sebagai pembukuan mentah yang ditulis kernel sendiri. Keduanya saling melengkapi — stats memberi potret seluruh host dalam satu tabel, cgroups memberi penghitung detail per container termasuk kejadian yang tidak pernah tampil di stats.
Angka dari Sumbernya: Berkas cgroups #
Kita sudah tahu dari Chapter Docker Stats bahwa seluruh angka pemantauan lahir dari berkas-berkas yang ditulis kernel di cgroups — daemon Docker hanya membacanya dari luar. Sekarang kita membuka buku kasnya sendiri. Empat berkas berikut adalah yang paling sering dibuka saat menyelidiki resource, dibaca dari dalam container backend lewat pintu exec yang kita kenal sejak Section Docker dalam Pengembangan:
$ docker compose exec backend cat /sys/fs/cgroup/memory.maxOutput yang Dihasilkan:
536870912$ docker compose exec backend cat /sys/fs/cgroup/memory.currentOutput yang Dihasilkan:
458227712$ docker compose exec backend cat /sys/fs/cgroup/memory.eventsOutput yang Dihasilkan:
low 0
high 0
max 0
oom 3
oom_kill 2
oom_group_kill 0$ docker compose exec backend cat /sys/fs/cgroup/cpu.statOutput yang Dihasilkan:
usage_usec 351246892
user_usec 189342015
system_usec 90233971
nr_periods 63936
nr_throttled 14120
throttled_usec 128745236Mari kita telaah keempat berkas itu satu per satu:
memory.max= 536870912 — langit-langit keras backend dalam byte: 512 MiB, angkadeploy.resources.limits.memoryyang kita tulis pada Chapter Resource di Compose. Nilaimaxdi berkas ini berarti tanpa batas — container tanpa limit akan membaca persis kata itu.memory.current= 458227712 — pemakaian saat ini, sekitar 437 MiB atau 85,3% dari langit-langitnya. Angka ini termasuk page cache yang bisa dilepas kernel saat tertekan, tetapi untuk pemantauan rutin ia indikator utama yang paling murah dibaca.memory.events— kronik kejadian luar biasa:oom 3berarti kernel tiga kali menjalankan penilaian OOM pada cgroup ini, danoom_kill 2berarti dua proses benar-benar dibunuh. Keduanya adalah jejak pekan uji beban yang diselenggarakan tim — penghitung ini berjalan maju dan tidak pernah turun, sehingga yang dipantau adalah kenaikannya antar pembacaan, bukan angkanya semata.cpu.stat— pembukuan CPU:usage_usecadalah total waktu CPU yang dikonsumsi dalam mikrodetik;nr_periodsmenghitung periode penjadwalan CFS (tiap periode 100 milidetik — 63936 periode berarti container ini berumur sekitar 1,8 jam);nr_throttled 14120mencatat berapa periode yang kena throttle;throttled_usecmenjumlahkan waktu yang dihabiskan mengantre.
Angka nr_throttled itu layak diberi panggung sendiri: 14120 dari 63936 periode berarti 22,1% seluruh periode penjadwalan berakhir dengan container menunggu jatah. Itu jauh di atas ambang yang akan kita tetapkan sebentar lagi — bukti bahwa kuota cpus: "1.0" pada backend lebih kecil dari beban nyatanya selama uji beban, dan petunjuk pertama bahwa angka itu perlu dinaikkan.
Pemantau 60 Detik ke CSV #
Angka-angka di atas baru hidup saat dibaca berulang, dan pekerjaan berulang adalah wilayah skrip. Pemantau berikut memotret seluruh container tiap 60 detik dan menumpuk hasilnya ke satu berkas CSV yang bisa dibuka spreadsheet mana pun — simpan sebagai pantau.sh di folder proyek, lalu jalankan dari terminal tersendiri:
BERKAS=resource-toko.csv
echo "waktu,nama,cpu_persen,mem_persen,pids" > "$BERKAS"
while true; do
STAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
docker stats --no-stream --format "{{.Name}},{{.CPUPerc}},{{.MemPerc}},{{.PIDs}}" \
| sed "s/^/$STAMP,/" >> "$BERKAS"
sleep 60
doneSkrip dijalankan dengan bash pantau.sh dan dihentikan dengan Ctrl+C; ia memang tidak mencetak apa pun ke terminal — seluruh hasilnya mengalir ke berkas. Setelah berjalan semalam, isi berkasnya terbaca lewat head:
$ head -n 7 resource-toko.csvOutput yang Dihasilkan:
waktu,nama,cpu_persen,mem_persen,pids
2026-10-20T08:01:00Z,toko-proxy-1,0.07%,0.07%,3
2026-10-20T08:01:00Z,toko-backend-1,99.87%,18.91%,11
2026-10-20T08:01:00Z,toko-cache-1,0.19%,1.92%,4
2026-10-20T08:01:00Z,toko-db-1,8.21%,2.01%,9
2026-10-20T08:02:00Z,toko-proxy-1,0.09%,0.07%,3
2026-10-20T08:02:00Z,toko-backend-1,98.21%,19.44%,11Mari kita telaah anatomi perekam kecil itu:
docker stats --no-stream --format— potret sekali jalan yang disusun sendiri kolomnya lewat Go template, kosakata yang kita kenal dari Chapter Docker Stats; keluarannya baris polos yang ramah CSV, satu baris per container.sed "s/^/$STAMP,/"— menempelkan waktu pengambilan di depan tiap baris; tanpa stempel waktu, tren hanyalah tumpukan angka tanpa sumbu.sleep 60— irisan waktu yang menentukan resolusi: 60 detik menghasilkan 1440 baris per container per hari, sekitar 7200 baris untuk lima service — ukuran yang masih nyaman dibuka spreadsheet dan cukup tajam untuk menangkap puncak jam sibuk.- Dua potret berturut-turut menampilkan backend nyaris menempel 100% — itu kuota
cpus: "1.0"yang terpasang dari Chapter *Resource di Compose; konsistensi antara CSV dancpu.stat` di bagian sebelumnya adalah cara saling memeriksa bahwa kamera dan buku kas mencatat kejadian yang sama.
Ambang yang Memicu Tindakan #
Data tanpa ambang hanyalah arsip. Empat ambang berikut adalah yang dipakai tim toko — masing-masing dipilih karena bisa dihitung dari angka yang kita rekam, dan masing-masing menunjuk tindakan yang jelas:
| Ambang | Artinya | Tindakan pertama |
|---|---|---|
Throttle di atas 20% berkelanjutan (nr_throttled/nr_periods) | kuota CPU lebih kecil dari beban nyata | naikkan cpus atau tinjau beban aplikasinya |
memory.current di atas 85% dari memory.max | langit-langit mepet secara rutin, bukan sesaat | naikkan limit atau buru kebocoran memori |
PIDS di atas 80% dari pids_limit | jumlah proses menanjak mendekati pagar | audit aplikasi; jangan buru-buru menaikkan pagar |
oom/oom_kill di memory.events naik antar pembacaan | OOM killer sudah turun tangan pada container ini | segera: naikkan limit atau kurangi beban puncaknya |
Kata kunci pada baris pertama adalah berkelanjutan: lonjakan throttle selama dua menit saat backup menyala adalah berita biasa, tetapi 20% yang bertahan tiga hari berturut-turut adalah bukti kuota yang keliru. Ambang memori 85% memberi masa tenggang sekitar 15% sebelum krisis — cukup untuk tindakan terjadwal, terlalu sempit untuk diabaikan. Ambang oom tidak memberi masa tenggang sama sekali, karena setiap kenaikannya berarti satu proses sudah mati; angka ini bukan peringatan dini melainkan pengakuan kejadian yang harus direspons hari itu juga.
Dari CSV ke Riwayat Produksi #
Skrip 60 detik dan CSV adalah peralatan tim kecil yang jujur, tetapi produksi menuntut lebih: pengumpulan yang tidak mati saat laptop ditutup, penyimpanan berminggu-minggu, dan grafik yang bisa dibagi ke seluruh tim. Dunia produksi menjawabnya dengan tumpukan timeseries — di ekosistem Docker yang paling lazim adalah Prometheus sebagai basis data deret waktu, ditemani cAdvisor sebagai eksportor yang mengekspos seluruh metrik container (CPU, memori, jaringan, sampai penghitung OOM) dalam format yang bisa di-scrape. Grafana lalu membaca Prometheus untuk menggambar tren berbulan-bulan di satu dashboard. Membangun tumpukan itu adalah proyek tersendiri yang menunggu waktunya di Section Deployment dan Production; yang perlu kita bawa pulang hari ini adalah prinsipnya — angka harus hidup di luar mesin tempat ia direkam.
Ambang-ambang di atas juga tidak berguna bila hanya dibaca orang yang kebetulan ingat membuka CSV. Kanal notifikasi menutup celah itu dengan satu curl dari skrip pemantau: begitu ambang terlanggar, pesan terkirim ke webhook milik tim — layanan notifikasi sederhana seperti ntfy, atau bot Slack dengan incoming webhook — tanpa satu pun URL yang tertulis keras di skrip:
$ curl -d "toko-backend-1: throttle 22% (ambang 20%)" "$ALERT_WEBHOOK"Output yang Dihasilkan:
{"ok":true}Variabel $ALERT_WEBHOOK dibaca dari environment shell yang menjalankan pemantau, sehingga skripnya aman di-commit tanpa membocorkan alamat tujuan, dan tiap anggota tim bisa mengarahkan salinan pemantauannya ke kanal masing-masing.
Untuk kerja harian, satu pintu pemantauan dari sisi project tetap paling sering dipakai, dan bentuknya sudah kita kenal dari Chapter Docker Stats:
$ docker compose statsOutput yang Dihasilkan (dipotong):
NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
toko-proxy-1 0.07% 5.312MiB / 128MiB 4.15% 1.46MB / 2.31MB 12.6MB / 0B 3
toko-backend-1 3.42% 96.84MiB / 512MiB 18.91% 9.87MB / 11.2MB 3.14MB / 0B 11
...Seluruh angka yang dibutuhkan ritual berikut tinggal dibuka dalam kosakata service — tanpa mengingat-ingat nama container satu per satu:
| Ritme | Ritual |
|---|---|
| Tiap pagi | baca notifikasi alert semalam; periksa container yang mati dengan docker ps -a dan telusuri yang berakhir 137 |
| Tiap pekan | buka CSV dan grafik: siapa yang menempel ambang? usulkan revisi limit ke tim sebagai perubahan compose.yaml |
| Tiap insiden | jalankan runbook diagnosis — alur baku yang kita susun pada chapter penutup section ini |
Dua pekan pemantauan di laptop Budi dan staging kini menghasilkan tumpukan CSV dengan satu pesan yang nyaring: backend menempel 100% CPU dan 22% throttle hampir setiap jam sibuk, sementara db di staging yang sengaja diberi limit longgar untuk pengukuran menunjukkan puncak yang jauh lebih rendah dari tebakan awal tim. Data itu menunggu satu hal — dijadikan keputusan angka yang tertulis di compose.yaml dan tagihan cloud. Chapter Mengelola Resource di Produksi menuntaskan pekerjaan itu: right-sizing dari p95, kebijakan per kelas service, dan anggaran node yang diperhitungkan sampai ke biaya.
Referensi #
- docker container stats
— flag
--no-streamdan--formatsebagai mesin poll loop pemantau. - Runtime options with Memory, CPUs, and GPUs — latar cgroups di balik seluruh angka yang direkam.
- docker compose stats — pemantauan harian dalam kosakata service dan project.