Docker adalah alat untuk membungkus aplikasi beserta semua yang dibutuhkannya (versi Node, library, file sistem operasi) jadi satu paket bernama image, lalu menjalankan paket itu sebagai container yang terpisah dari isi komputer lainnya. Bayangin kontainer kapal: isinya bisa apa saja, tapi bentuk luarnya standar, jadi kapal, truk, dan crane mana pun memindahkannya dengan cara yang sama tanpa membongkar isinya. Docker bikin aplikasimu jadi kotak standar begitu: laptopmu, server CI, dan VPS menjalankannya dengan satu perintah yang sama, dan isinya gak berubah di jalan.
🧪 Tebakan cepat: kamu menjalankan dua container dari image
catatan-api:1.0, lalu menghapus keduanya dengandocker rm. Apakah imagecatatan-api:1.0ikut hilang? Tebak dulu, jawabannya ada di bagian "Image dan container".
Masalah "works on my machine"
Ingat catatan-api, API catatan kita (app.js, port 3000) yang jalan di server web-1. Misalnya laptopmu memakai Node 20, tapi web-1 masih Node 18. Kamu menulis catatan.toSorted(...) untuk mengurutkan catatan terbaru dulu, method yang baru ada sejak Node 20. Di laptop jalan, di web-1 langsung error:
TypeError: catatan.toSorted is not a function
Kalimat "di laptopku jalan kok" (works on my machine) muncul karena tiap mesin punya isi yang berbeda: versi Node, versi library, sistem operasi, environment variable. Docker menyelesaikannya dengan ikut membawa isi itu. Image catatan-api:1.0 berisi file Alpine Linux, Node 20, node_modules, dan kode kamu. web-1 cukup punya Docker; Node versi berapa yang terpasang di sana gak penting lagi, karena yang dipakai adalah Node 20 di dalam image.
Image dan container
- Image: paket yang isinya gak bisa diubah (read-only). Dia cuma file, gak jalan. Image dibuat dari resep bernama
Dockerfile(lesson berikutnya) dan disimpan di registry seperti Docker Hub (lesson setelahnya). - Container: image yang sedang dijalankan. Dia proses yang terisolasi, dengan lapisan tulis miliknya sendiri di atas image. Satu image bisa jadi banyak container.
Ini terminal di laptop (folder ~/catatan-api), persis seperti di Lab:
$ docker run -d --name catatan-api -p 3000:3000 catatan-api:1.0
3f2a9c1b7d4e8a51c6f0b2d97e4a1c385f6b0d2e9a7c4b1f8e3d6a0c5b9f2e71
$ docker run -d --name catatan-api-2 -p 3001:3000 catatan-api:1.0
8b7e6d5c4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8e7d
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8b7e6d5c4a3f catatan-api:1.0 "docker-entrypoint.s…" 15 seconds ago Up 15 seconds 0.0.0.0:3001->3000/tcp catatan-api-2
3f2a9c1b7d4e catatan-api:1.0 "docker-entrypoint.s…" 30 seconds ago Up 30 seconds 0.0.0.0:3000->3000/tcp catatan-api
docker runmembuat container baru dari image lalu menyalakannya. Yang dicetak adalah ID container (64 karakter;docker psmenampilkan 12 karakter pertamanya).-d(detached): jalan di background, terminalmu langsung bisa dipakai lagi.--name catatan-api: nama container. Nama harus unik; container kedua butuh nama lain.-p 3000:3000: port 3000 di laptop diteruskan ke port 3000 di dalam container. Container kedua memakai port laptop 3001, karena 3000 sudah dipakai. Detailnya di lesson Docker Networking.docker psmenampilkan container yang sedang jalan (Up).docker ps -ajuga menampilkan yang sudah berhenti (Exited).
Lalu matikan dan hapus keduanya:
$ docker stop catatan-api catatan-api-2
catatan-api
catatan-api-2
$ docker rm catatan-api catatan-api-2
catatan-api
catatan-api-2
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
catatan-api 1.0 5e1f8a2c7b90 2 hours ago 182MB
node 20-alpine 1c4e7b2a9f60 3 weeks ago 135MB
Container-nya hilang, image catatan-api:1.0 tetap ada. Itu jawaban tebakannya. Image itu cetakannya, container itu hasil cetakan yang sedang jalan: membuang hasil cetakan gak merusak cetakannya. Yang ikut hilang adalah file yang ditulis app ke dalam container itu sendiri, karena lapisan tulisnya milik container.
Perintah lain yang sering dipakai: docker logs catatan-api membaca apa yang dicetak app (console.log-nya), dan docker stop mengirim sinyal berhenti ke app lalu menunggunya selesai.
Membaca output docker ps
Tabel docker ps enak dibaca manusia, tapi susah dibaca program karena kolomnya dipisah spasi, padahal isinya juga ada spasi (Up 2 hours). Untuk script, minta format sendiri dengan --format, kolomnya dipisah tab (\t):
$ docker ps --format "{{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}"
3f2a9c1b7d4e catatan-api:1.0 Up 2 hours 0.0.0.0:3000->3000/tcp catatan-api
Satu baris per container, lima kolom: ID, image, status, ports, nama. Mengurainya di JavaScript:
const line = "abc123\tnode:20\tUp 2 hours\t0.0.0.0:3000->3000/tcp\tmy-app";
const parts = line.split("\t");
// ["abc123", "node:20", "Up 2 hours", "0.0.0.0:3000->3000/tcp", "my-app"]
const container = { id: parts[0], image: parts[1], status: parts[2], ports: parts[3], name: parts[4] };
split("\t")memotong di tiap tab, jadiUp 2 hourstetap utuh satu kolom.- Kalau ada beberapa container, barisnya dipisah
\n: potong dulu per baris dengansplit("\n"), lalu.map(...)tiap baris jadi satu object.
Container vs virtual machine
Virtual machine (VM) juga memisahkan aplikasi, tapi dengan cara yang jauh lebih berat. Tiap VM menjalankan sistem operasi lengkap miliknya sendiri, termasuk kernel (inti sistem operasi yang mengatur CPU, memori, dan file), di atas software bernama hypervisor. Container gak punya kernel sendiri: semua container berbagi kernel OS host, dan Docker cuma memisahkan proses, file, dan jaringannya.
| 3 app dijalankan sebagai | 3 VM | 3 container |
|---|---|---|
| Sistem operasi | 3 OS lengkap, masing-masing dengan kernel | 1 kernel host dipakai bersama |
| Memori (contoh) | sekitar 3 GB | sekitar 180 MB |
| Waktu nyala (contoh) | 30 sampai 60 detik | sekitar 1 detik |
Karena berbagi kernel Linux, container Linux butuh kernel Linux. Di Mac dan Windows, Docker Desktop diam-diam menjalankan satu VM Linux kecil, dan semua container-mu jalan di dalamnya.
⚠️ Jebakan yang sering ketemu
docker pskosong padahal kamu baru menjalankan container. Kemungkinan app-nya crash dan container-nya berhenti. Cekdocker ps -auntuk statusnya (Exited (1)), laludocker logs <nama>untuk pesan error-nya.- Menyimpan data penting di dalam container. File yang ditulis app hilang saat container dihapus. Data yang harus awet disimpan di volume (lesson Docker Volumes).
- Nama container bentrok. Menjalankan
docker run --name catatan-apilagi saat containercatatan-apimasih ada (walau sudah berhenti) ditolak denganConflict. The container name "/catatan-api" is already in use. Hapus dulu dengandocker rm catatan-api, atau pakai nama lain. - Memotong output pakai spasi. Status seperti
Up 2 hoursberisi spasi, jadisplit(" ")merusak kolomnya. Potong pakai\tdari--format.
TL;DR: Docker menyelesaikan masalah "works on my machine" dengan container: aplikasi dan semua isinya dibungkus jadi image, lalu dijalankan sebagai container yang terisolasi. Image itu cetakan read-only, container itu image yang sedang jalan; satu image bisa jadi banyak container, dan docker rm cuma menghapus container, bukan image. Perintah dasar: docker run -d --name ... -p host:container image, docker ps (yang jalan), docker ps -a (termasuk yang berhenti), docker stop, docker rm, docker logs. Container berbagi kernel OS host, VM punya OS lengkap sendiri, jadi container jauh lebih ringan dan nyala dalam hitungan detik. Output docker ps --format dipisah tab: potong per baris dengan \n, lalu per kolom dengan \t.