Log yang kita baca pada Chapter Docker Logs adalah kesaksian aplikasi — ia hanya menceritakan apa yang aplikasi rasakan, sebatas apa yang sempat dicetak ke stdout dan stderr. Untuk memeriksa tempat kejadian itu sendiri, daemon menyimpan arsip yang jauh lebih kaya: setiap container membawa metadata lengkap tentang kelahirannya, keadaan prosesnya, alamat jaringannya, batas resource-nya, sampai variabel environment yang tersuntik ke dalamnya. Arsip itu terbuka lewat satu perintah — docker inspect — dan di chapter ini kita membacanya bertingkat: dari JSON mentah, penyelaman ke field State, delapan Go template siap pakai untuk skrip diagnosis, sampai perbedaan mendasar antara membedah container dan membedah image.
Arsip Keadaan dalam Satu Perintah #
Tanpa argumen tambahan, docker inspect mencurahkan seluruh metadata dalam bentuk JSON — ratusan baris untuk satu container. Mari kita intip arsip toko-backend-1, dipotong pada bagian yang paling penting:
$ docker inspect toko-backend-1Output yang Dihasilkan (dipotong):
[
{
"Id": "20269502f039d2d914b5845a97033f9a7ab8bbaf7ab58440affe76c390453a9c",
"Created": "2026-10-13T01:31:33.812401337Z",
"Path": "docker-entrypoint.sh",
"Args": [
"node",
"src/server.js"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 4821,
"ExitCode": 0,
"Error": "",
"StartedAt": "2026-10-13T01:31:34.104512312Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "95d67d49b8fd52a07cd0e38c3b95d13c266ad475484b3626c15a896c3352789e",
"Name": "/toko-backend-1",
...
}
]Mari kita telaah tujuh penghuni terpenting potongan tersebut:
Id— identitas penuh container sepanjang 64 karakter heksadesimal; bentuk pendeknya yang 12 karakter itulah yang tampil didocker psdandocker stats.Created— momen container diciptakan, presisi nanodetik; bagitoko-backend-1, pagi ini pukul01:31:33Z— tepat setelah Siti menarik perubahan terbaru dan menjalankandocker compose up -d --build.PathdanArgs— perintah yang dijalankan proses utama beserta argumennya; pasangan keduanya adalah isi yang tampil terpotong pada kolom COMMAND didocker ps.State— panel keadaan saat ini: status lima state dari Chapter Lifecycle Container, exit code terakhir, jejak OOM killer, nomor PID di sisi host, serta waktu dinyalakan dan waktu selesai.Image— identitas penuh image yang menjadi dasar container — bukan namatoko-api:dev, melainkan ID imagenya yang 64 heksadesimal.Name— nama container dengan garis miring di depannya, penanda internal daemon; nama inilah yang kita pakai untuk memanggil container....— sisa ratusan baris yang dipotong:NetworkSettings,Mounts,Config,HostConfig, dan segala hal yang sempat kita konfigurasikan sepanjang course, tersimpan utuh sebagai catatan kelahiran.
Menyelam ke State dengan json #
Field State adalah bagian yang paling sering kita baca saat diagnosis, dan menelan JSON mentah satu baris bukan cara membaca yang ramah. Kombinasi template {{json .State}} dengan pretty-printer bawaan Python merapikannya seketika:
$ docker inspect --format '{{json .State}}' toko-backend-1 | python3 -m json.toolOutput yang Dihasilkan:
{
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 4821,
"ExitCode": 0,
"Error": "",
"StartedAt": "2026-10-13T01:31:34.104512312Z",
"FinishedAt": "0001-01-01T00:00:00Z"
}Pola pipa ini bekerja untuk field mana pun yang berbentuk objek — cukup ganti .State dengan .NetworkSettings atau .HostConfig. Nilai FinishedAt "0001-01-01T00:00:00Z" adalah cara daemon menulis “belum pernah selesai” — container yang masih hidup tidak punya waktu kematian, sehingga waktunya berisi nilai nol zaman Go.
Delapan Template Go Siap Pakai #
Kekuatan sesungguhnya --format adalah Go template: kita memilih persis field mana yang ingin dibaca, dan hasilnya menjadi bahan baku skrip diagnosis. Delapan template berikut menutup sebagian besar kebutuhan harian:
| Template | Contoh keluaran | Yang dibacanya |
|---|---|---|
{{.State.Status}} | running | posisi container pada peta lima state |
{{.State.ExitCode}} | 0 | kode keluar terakhir PID 1 |
{{.State.OOMKilled}} | false | apakah kematian terakhir karya OOM killer |
{{.NetworkSettings.IPAddress}} | 172.19.0.4 | alamat IP container di network-nya |
{{.HostConfig.Memory}} | 268435456 | batas memori dalam byte mentah; 0 berarti tanpa batas |
{{range .Mounts}}{{.Source}} -> {{.Destination}}{{println}}{{end}} | /home/budi/toko/frontend -> /usr/share/nginx/html | daftar mount dari sumber ke target |
{{range .Config.Env}}{{println .}}{{end}} | DATABASE_URL=postgres://toko:rahasia123@db-utama:5432/toko | environment tersuntik, satu baris satu variabel |
{{.Config.Image}} | toko-api:dev | image yang menjadi dasar container |
Empat yang pertama sudah akrab dari Chapter Lifecycle Container dan CPU dan Memory Limit; empat berikutnya memperluas jangkauan kita. Mari kita uji beberapa di antaranya pada stack yang sedang berjalan:
$ docker inspect --format '{{.State.Status}}' toko-backend-1Output yang Dihasilkan:
running$ docker inspect --format '{{.NetworkSettings.IPAddress}}' toko-backend-1Output yang Dihasilkan:
172.19.0.4$ docker inspect --format '{{.HostConfig.Memory}}' toko-backend-1Output yang Dihasilkan:
268435456Angka 268435456 itu bukan galat tampilan: cgroups memang hanya berbicara dalam byte polos, dan 268435456 adalah 256m yang kita pasang pada service backend — 256 dikali 1024 dua kali, persis konversi yang kita telaah di Chapter CPU dan Memory Limit. Nilai 0 pada template yang sama berarti container berjalan tanpa langit-langit memori — toko-proxy-1 akan menjawab 0.
Dua template range membutuhkan sorotan khusus karena menelusuri daftar. Yang pertama membaca mount — kita uji pada frontend yang ber-bind mount:
$ docker inspect --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{println}}{{end}}' toko-frontend-1Output yang Dihasilkan:
/home/budi/toko/frontend -> /usr/share/nginx/htmlYang kedua membaca environment tersuntik:
$ docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' toko-backend-1Output yang Dihasilkan:
DATABASE_URL=postgres://toko:rahasia123@db-utama:5432/toko
REDIS_URL=redis://cache:6379
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NODE_VERSION=22.20.0
HOME=/home/nodeInspect Container Berbeda dengan Inspect Image #
Perintah kembar docker image inspect membedah objek yang berbeda — image, bukan container — dan field-nya pun berbeda kelas. Container menyimpan keadaan: State, NetworkSettings, Mounts, HostConfig. Image menyimpan cetak biru: perintah bawaan, environment bawaan, ukuran, dan susunan layer:
$ docker image inspect nginx:1.29-alpine --format '{{json .Config.Env}}'Output yang Dihasilkan (dipotong):
["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.29.4",...]$ docker image inspect nginx:1.29-alpine --format '{{.Size}}'Output yang Dihasilkan:
47892034$ docker image inspect nginx:1.29-alpine --format '{{json .Config.Cmd}}'Output yang Dihasilkan:
["nginx","-g","daemon off;"]Mari kita telaah tiga jawaban itu: Config.Env milik image memuat variabel yang dibawa sejak build — di sinilah NGINX_VERSION hidup, bukan di container; Size adalah ukuran total seluruh layer image dalam byte — sekitar 47,9 megabyte untuk nginx alpine, persis angka yang kita kenal dari docker images; dan Config.Cmd adalah perintah bawaan yang dijalankan container dari image ini — nginx -g daemon off; yang kita kenal dari arsitektur image nginx. Bandingkan dengan container: field yang sama pada docker inspect toko-proxy-1 akan memuat environment hasil suntikan compose.yaml dan State yang terus berubah mengikuti hidupnya container.
Perbedaan objek ini melahirkan satu jebakan nama. docker inspect menerima nama apa pun, dan daemon mencocokkannya lebih dulu terhadap daftar container sebelum image — bila suatu saat kita menjalankan container bernama nginx dari image nginx, perintah docker inspect nginx mengembalikan container itu, bukan imagenya. Dua jalan keluarnya sama sahnya: perintah eksplisit docker image inspect nginx untuk memastikan sasaran, atau flag --type image pada docker inspect untuk memaksa jenis objek — --type container bekerja sebaliknya.
Pola Post-Mortem Satu Baris #
Saat insiden berlalu dan kita hanya butuh satu kalimat berita, tiga field kunci bisa dirangkai menjadi satu baris jawaban. Korban latihan kita adalah job migrasi yang sudah lama tenang:
$ docker inspect --format '{{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}}' toko-migrate-1Output yang Dihasilkan:
exited exit=0 oom=falseSatu baris itu berbunyi: container sudah selesai, keluar dengan kode sukses, dan tidak pernah dibunuh OOM killer. Teks di luar kurung kurawal dicetak apa adanya sehingga label bisa kita susun sendiri — pola yang sama siap diperpanjang dengan field lain seperti {{.RestartCount}} untuk menghitung kebangkitan. Inilah bentuk docker inspect yang paling sering berakhir di berkas catatan insiden: pendek, pasti, dan bisa dibandingkan antar container dalam sekejap.
Arsip ini satu sifat dengan foto — ia membekukan keadaan pada momen pembacaan. State.Running hari ini adalah running, tetapi berapa banyak CPU yang benar-benar dipakai proses node di dalamnya, seberapa dekat ia mengejar langit-langit 256 MiB, dan seberapa riuh lalu lintas jaringannya — semua itu adalah angka yang berdenyut, dan Docker punya jendela khusus untuk angka-angka hidup tersebut. Chapter Docker Stats membuka jendela itu.
Referensi #
- docker container inspect
— struktur metadata container beserta contoh pemakaian
--format. - docker image inspect
— metadata image dan field khasnya seperti
SizesertaConfig.Cmd. - Format command and log output
— sintaks Go template untuk CLI Docker, dari placeholder sederhana hingga
rangedanjson.