docker stats pada Chapter Docker Stats mengukur denyut — tetapi angka yang berdenyut tidak pernah bercerita tentang sebab. Ketika cache mati mendadak, ketika worker dibangkitkan entah oleh siapa, ketika container diciptakan ulang di sela rapat singkat, semua kejadian itu lewat tanpa meninggalkan satu baris pun di tabel stats maupun di log aplikasi. Daemon sesungguhnya mencatat setiap kejadiannya sendiri — lahir, mati, dibekukan, dihapus, di-pin, ganti health — dalam satu aliran bernama docker events, dan chapter ini mengajak kita berlangganan langsung: menyaksikan kejadian lahir di layar detik demi detik, membedah anatomi satu barisnya, menyaring arus yang riuh, lalu membaca ke belakang untuk menyusun kronologi insiden.

Langganan Langsung ke Kronik Daemon #

docker events tanpa argumen adalah langganan langsung: terminal duduk tenang menunggu, dan setiap kejadian yang menimpa objek Docker di mesin ini akan tercetak seketika. Kita butuh dua terminal — terminal pertama menjadi penonton, terminal kedua menjadi aktor:

bash
$ docker events

Terminal pertama diam; tidak ada keluaran apa pun karena memang belum ada yang terjadi. Dari terminal kedua, kita hentikan cache stack toko dengan jalur sopan yang sudah kita kenal:

bash
$ docker compose stop cache

Output yang Dihasilkan (terminal kedua):

text
[+] Running 1/1
 ✔ Container toko-cache-1  Stopped  1.1s

Baris ramping itu adalah versi Compose; kronik lengkapnya tercetak di terminal pertama:

text
2026-10-13T07:58:24.512401337+07:00 container kill 69bebb2487a6e28d27688b69f1241d4395da278739f691bc3efff7774ab2e32a (image=redis:7-alpine, name=toko-cache-1, signal=15)
2026-10-13T07:58:24.613547882+07:00 container die 69bebb2487a6e28d27688b69f1241d4395da278739f691bc3efff7774ab2e32a (image=redis:7-alpine, name=toko-cache-1, exitCode=0)
2026-10-13T07:58:24.704891245+07:00 container stop 69bebb2487a6e28d27688b69f1241d4395da278739f691bc3efff7774ab2e32a (image=redis:7-alpine, name=toko-cache-1)

Mari kita telaah tiga kejadian yang lahir dari satu perintah stop:

  1. kill dengan signal=15 — docker compose stop pada akhirnya menjalankan docker stop, dan ritualnya dimulai dengan SIGTERM (sinyal 15) yang dikirim ke PID 1; nama kejadiannya kill, tetapi atribut signal=15 menegaskan ini jalur sopan, bukan docker kill yang mengirim sinyal 9.
  2. die dengan exitCode=0 — Redis menangkap SIGTERM dan menutup dirinya dengan rapi; exit code 0 pada kejadian mati adalah tanda kematian paling terhormat, persis perilaku yang kita rancang pada Chapter Graceful Shutdown.
  3. stop — penutup resmi ritual: daemon menuntaskan penghentian sebelum masa tenggang 10 detik terpakai, karena prosesnya memang sudah keluar sendiri seratus milidetik setelah SIGTERM.

Kita bangunkan kembali cache dari terminal kedua:

bash
$ docker compose start cache

Output yang Dihasilkan (terminal kedua):

text
[+] Running 1/1
 ✔ Container toko-cache-1  Started  0.8s

Terminal pertama menyambut dua kejadian:

text
2026-10-13T07:58:51.310294716+07:00 container start 69bebb2487a6e28d27688b69f1241d4395da278739f691bc3efff7774ab2e32a (image=redis:7-alpine, name=toko-cache-1)
2026-10-13T07:59:01.845120331+07:00 container health_status: healthy 69bebb2487a6e28d27688b69f1241d4395da278739f691bc3efff7774ab2e32a (image=redis:7-alpine, name=toko-cache-1)

Dua baris penutup itu menyimpan dua pelajaran: start membawa ID yang sama persis — container yang dibangunkan adalah container yang sama, bukan ciptaan baru; dan health_status: healthy muncul sepuluh detik kemudian karena probe redis-cli ping akhirnya lulus — kejadian yang lahir bukan dari hidup-matinya proses, melainkan dari penilaian kesehatan yang kita tanam di compose.yaml.

Anatomi Satu Baris Event #

Satu baris kronik padat berisi lima unsur, dan membedahnya sekali membuat kita bisa membaca baris mana pun selamanya. Mari kita gunakan baris pertama tadi sebagai spesimen:

  1. Waktu kejadian — 2026-10-13T07:58:24.512401337+07:00: tanggal, jam dengan presisi nanodetik, dan zona waktu mesin kita — WIB, tujuh jam di depan UTC.
  2. Jenis objek — container: kejadian menimpa container; jenis lain yang mungkin adalah image, network, volume, hingga daemon itu sendiri.
  3. Aksi — kill: kata kerja kejadian; daftar katanya panjang, dari create, start, stop, die, oom, sampai health_status.
  4. ID penuh — 64 karakter heksadesimal, bukan bentuk pendek 12 seperti di docker ps; inilah penghubung ke docker inspect dan docker logs pada chapter-chapter sebelumnya.
  5. Atribut dalam kurung — kamus kunci=nilai yang menemani aksi: kill membawa signal, die membawa exitCode, semuanya membawa image dan name; atribut inilah yang membuat satu baris kronik bisa dibaca tanpa menyalakan perintah lain.

Menyaring Arus dengan Filter #

Aliran penuh docker events riuh pada mesin yang sibuk — build, pull, network, volume, semuanya bicara bersamaan. Flag --filter menyempitkannya, dan beberapa penyaring paling berguna dirangkum di tabel berikut:

FilterMenyaring berdasarkanContoh pemakaian
type=containerjenis objek kejadiandocker events --filter type=container
event=dienama aksi secara persisdocker events --filter event=die
container=toko-backend-1satu objek tertentudocker events --filter container=toko-backend-1

Penyaring boleh dirangkai — docker events --filter type=container --filter event=die hanya menampilkan kejadian die milik container — dan setiap penyaring tambahan mempersempit hasil dengan logika DAN. Penyaring objek juga menerima nama image, memberi kita cara mengawasi seluruh container yang lahir dari satu image.

Menoleh ke Belakang dengan –since dan –until #

Semua demo sejauh ini bersifat langganan: kita harus sedang menonton ketika kejadian terjadi. Padahal kebutuhan diagnosis yang paling sering justru menoleh ke belakang — “apa yang terjadi sepuluh menit terakhir?”. Pasangan --since dan --until mengubah perintah menjadi mode historis. Kita tekan Ctrl-C pada langganan tadi, lalu membaca ulang pagi ini:

bash
$ docker events --since 10m --until 0s

Output yang Dihasilkan (dipotong):

text
2026-10-13T07:58:24.512401337+07:00 container kill 69bebb2487a6e28d27688b69f1241d4395da278739f691bc3efff7774ab2e32a (image=redis:7-alpine, name=toko-cache-1, signal=15)
2026-10-13T07:58:24.613547882+07:00 container die 69bebb2487a6e28d27688b69f1241d4395da278739f691bc3efff7774ab2e32a (image=redis:7-alpine, name=toko-cache-1, exitCode=0)
...
2026-10-13T07:59:01.845120331+07:00 container health_status: healthy 69bebb2487a6e28d27688b69f1241d4395da278739f691bc3efff7774ab2e32a (image=redis:7-alpine, name=toko-cache-1)

Perintah ini berhenti dengan sendirinya — berbeda dari mode langganan: --since 10m membuka jendela sepuluh menit ke belakang, --until 0s menutup jendela tepat di detik sekarang (nol detik dari masa kini), dan setelah buffer dicetak, prompt kembali. Nilai keduanya juga menerima durasi lain seperti 1h atau timestamp mutlak, sama seperti --since milik docker logs.

Kasus Riil: Worker yang Terbunuh OOM #

Kekuatan kronik paling terasa pada kejadian yang tidak meninggalkan pesan perpisahan. Kita pinjam resep eksperimen lapar dari Chapter *CPU dan Memory Limit`: pekerja yang haus memori, kurungan yang lebih kecil dari nafsunya — kali ini sebagai container latar belakang dengan kebijakan bangkit tanpa henti. Kita hidupkan kembali langganan di terminal pertama, lalu dari terminal kedua:

bash
$ docker run -d --name uji-worker --restart always -m 100m polinux/stress --vm 1 --vm-bytes 150M --vm-hang 0

Output yang Dihasilkan:

text
009404942603e981671d310b53edc921d625062810e6e5459d1c4754e6d6c7a1

Pekerja diminta mengalokasikan 150 MB di dalam kurungan 100 MB; nasibnya tinggal soal waktu. Terminal pertama mencatat tiga baris yang menceritakan satu putaran hidup:

text
2026-10-13T08:12:41.104512312+07:00 container oom 009404942603e981671d310b53edc921d625062810e6e5459d1c4754e6d6c7a1 (image=polinux/stress, name=uji-worker)
2026-10-13T08:12:41.918044293+07:00 container die 009404942603e981671d310b53edc921d625062810e6e5459d1c4754e6d6c7a1 (image=polinux/stress, name=uji-worker, exitCode=137)
2026-10-13T08:12:48.240193852+07:00 container start 009404942603e981671d310b53edc921d625062810e6e5459d1c4754e6d6c7a1 (image=polinux/stress, name=uji-worker)

Mari kita telaah drama tiga babak itu:

  1. oom — kernel menyatakan cgroup uji-worker kehabisan memori dan algojonya bekerja; pekerja berhasil mengalokasikan kira-kira 100 MB sebelum langit-langit cgroups menutup pintu.
  2. die dengan exitCode=137 — 137 adalah 128 ditambah 9: proses dijatuhkan SIGKILL, sinyal yang tidak bisa ditangkap siapa pun, sehingga tidak ada satu baris log perpisahan pun di docker logs; angka ini adalah seluruh pesan yang ditinggalkan.
  3. start enam detik kemudian — kebijakan --restart always membangkitkan container dari kematian; jeda backoff yang dimulai 100 milidetik dan berlipat ganda pada setiap percobaan kini telah tumbuh menjadi sekitar enam detik — artinya uji-worker sudah berputar beberapa putaran sebelum kita mulai menonton. Putaran berikutnya sudah tertulis di nasibnya: bangkit, lapar, terbunuh, berulang — crash loop yang akan kita jinakkan pada Chapter Mengatasi Container Crash.

Kronik yang lengkap itu kita tutup dengan membersihkan korban percobaan:

bash
$ docker rm -f uji-worker

Output yang Dihasilkan:

text
uji-worker

Trio Diagnosis dan Waktu dalam Angka #

Untuk berkas catatan insiden, format bawaan events terlalu bertele-tele. Template --format — kosakata Go template yang sama dengan dua chapter sebelumnya — meringkasnya menjadi kolom pilihan kita:

bash
$ docker events --since 30m --until 0s --format '{{.Time}} {{.Action}} {{.Actor.Attributes.name}}'

Output yang Dihasilkan (dipotong):

text
1791853104 kill toko-cache-1
1791853104 die toko-cache-1
1791853104 stop toko-cache-1
1791853131 start toko-cache-1
1791853141 health_status: healthy toko-cache-1
...
1791853961 oom uji-worker
1791853961 die uji-worker
1791853968 start uji-worker

Mari kita telaah bentuk waktu barunya: {{.Time}} mencetak kejadian sebagai detik Unix — jumlah detik sejak 1 Januari 1970 UTC. Angka 1791853104 itu dapat kita urai: dalam UTC ia jatuh pada 2026-10-13T00:58:24Z, dan karena mesin kita berada tujuh jam di depan, momen kejadian adalah pukul 07:58:24 WIB — persis stempel baris kill yang kita baca di awal chapter. Tiga kejadian pertama berbagi angka yang sama karena lahir pada detik yang sama; presisi nanodetik dan zona waktu hilang demi bentuk yang ringkas, mudah diurutkan, dan mudah dihitung selisihnya — 1791853968 dikurangi 1791853961 langsung memberi kita jeda bangkit uji-worker tujuh detik secara kasar, tanpa kalkulator waktu.

Kronik daemon kini terbaca lancar dua arah — langsung saat kejadian dan ke belakang sesudahnya. Namun di antara semua aksi yang kita saksikan hari ini, satu baris menonjol karena sumbernya yang unik: health_status: healthy tidak lahir dari proses yang hidup atau mati, melainkan dari probe yang menilai. Siapa yang menilai, bagaimana siklus penilaiannya, dan — yang paling penting — apa yang Docker lakukan ketika putusannya buruk, adalah kisah Chapter Container Health.

Referensi #