Container membungkus aplikasi beserta seluruh dependensinya menjadi unit yang portabel dan konsisten di laptop, CI, maupun server produksi; di episode ini kalian menguasai image, Dockerfile best practices, multi-stage build, registry, dan Docker Compose, lalu meng-containerisasi aplikasi nyata

Di episode 6 kita belajar bahwa strategi deployment baru terasa aman jika environment konsisten: yang diuji di staging harus persis yang berjalan di produksi. Masalahnya, environment menempel pada mesin — versi library, interpreter, konfigurasi sistem. Container memecahkan masalah ini dengan membungkus aplikasi beserta seluruh dependensinya menjadi satu unit portabel.
Containerization bukan sekadar "cara menjalankan aplikasi", melainkan standar kemasan. Analoginya seperti peti kemas di dunia logistik: sebelum ada container, memindahkan barang membutuhkan bongkar-muat manual yang berbeda untuk setiap jenis barang. Setelah ada peti kemas, satu bentuk standar bisa dipindahkan dengan crane yang sama di kapal, truk, dan kereta. Docker adalah "peti kemas" untuk software — dan Docker menjadi fondasi dari semua praktik yang akan kalian bangun di episode-episode berikutnya, termasuk Kubernetes.
Sebelum masuk ke Docker, kalian harus paham bedanya container dengan VM — keduanya sering tertukar:
| Aspek | Virtual Machine | Container |
|---|---|---|
| Isolasi | Virtualisasi perangkat keras (hypervisor) | Virtualisasi OS level (kernel bersama) |
| Kernel | Sendiri (guest OS penuh) | Berbagi kernel host |
| Ukuran image | Ratusan MB hingga GB | Puluhan hingga ratusan MB |
| Waktu start | Detik hingga menit | Milidetik hingga detik |
| Overhead | Tinggi | Rendah |
VM mengisolasi seluruh sistem operasi; container mengisolasi proses dan filesystem-nya saja dengan memakai kernel yang sama. Inilah mengapa container ringan — tetapi juga mengapa container tidak bisa menjalankan OS yang kernelnya berbeda dari host (misalnya image Linux di host Windows membutuhkan VM pembantu).
Dua konsep inti yang wajib kalian bedakan sejak awal:
Image tersusun dari layer. Setiap perintah dalam Dockerfile menciptakan satu layer, dan layer ini bisa dipakai ulang (cached) antar image:
ubuntu:22.04 ← base image (layer OS)
+ COPY package.json ← layer 1
+ RUN npm install ← layer 2
+ COPY src ./src ← layer 3
+ CMD ["npm", "start"] ← metadata runtimeKarena layer di-cache, perubahan pada src/ hanya mengulang dari layer 3 ke atas — tanpa membangun ulang seluruh image. Inilah alasan mengapa urutan perintah dalam Dockerfile sangat menentukan kecepatan build.
Dockerfile yang baik adalah Dockerfile yang kecil, aman, dan memanfaatkan cache dengan benar. Aturan yang paling sering kalian jumpai di review code:
npm install hanya berjalan ketika package.json berubah.latest).apt-get yang berurutan.FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup -S app && adduser -S app -G app
COPY --from=build --chown=app:app /app/package*.json ./
COPY --from=build --chown=app:app /app/node_modules ./node_modules
COPY --from=build --chown=app:app /app/dist ./dist
USER app
EXPOSE 3000
CMD ["node", "dist/server.js"]Perhatikan bagian FROM build: ini adalah multi-stage build. Tahap pertama mengompilasi aplikasi beserta dependency; tahap kedua menyalin hanya hasil akhir (dist dan node_modules) ke image runtime yang kecil. Image produksi tidak membawa source code maupun toolchain build.
Image yang sudah dibangun perlu disimpan di container registry agar bisa diunduh dari mana saja — CI, staging, maupun produksi:
docker tag myapp:1.2.0 ghcr.io/username/myapp:1.2.0
docker push ghcr.io/username/myapp:1.2.0Important
Jangan pernah memakai tag latest untuk image yang dirilis ke produksi. latest bersifat mutable — kalian tidak akan pernah tahu versi apa yang sedang berjalan di cluster. Gunakan tag versi semver (1.2.0) atau digest image (sha256:...) yang immutable. Kita bahas lebih dalam di episode 17.
Mari praktikkan. Ambil aplikasi Node.js sederhana, lalu bungkus dengan Dockerfile di atas. Pastikan file .dockerignore dibuat agar direktori node_modules dan .git tidak ikut masuk konteks build:
node_modules
.git
distLalu bangun dan jalankan:
docker build -t myapp:0.1.0 .
docker run -d -p 3000:3000 --name myapp myapp:0.1.0
docker logs -f myapp
docker psVerifikasi ukuran image yang dihasilkan:
docker images myappImage multi-stage di atas seharusnya berukuran jauh lebih kecil daripada hasil build satu tahap — ini bukti nyata bahwa praktik tidak hanya soal estetika.
Aplikasi produksi jarang hanya satu service: ada database, cache, message queue. Docker Compose mendefinisikan seluruh lingkungan multi-container dalam satu file compose.yaml:
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://app:secret@db:5432/app
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:docker compose up -d
docker compose ps
docker compose logs app
docker compose downCompose adalah alat yang sempurna untuk environment dev dan staging. Untuk produksi yang perlu scaling, self-healing, dan rolling update, kalian akan memindahkannya ke Kubernetes di episode 8.
-alpine, distroless).ENV dan file .env yang ikut ter-build akan tertanam permanen di image. Pakai inject secrets saat runtime (episode 16)..dockerignore — konteks build membesar dan bisa menyalin node_modules secara tidak sengaja.apt-get update di dalam container yang berjalan; itu sinyal image kalian kurang lengkap.Inti yang harus dibawa pulang:
latest.Di episode 8 selanjutnya kita masuk ke Kubernetes — orkestrator yang mengelola ratusan container di banyak mesin: scheduling, self-healing, scaling, dan networking. Image yang kalian buat di episode ini adalah bahan baku utama yang akan dijalankan cluster Kubernetes.