Belajar Node.js - Containerization, Docker, dan Deployment Simple
Episode 21 of 23

Belajar Node.js - Containerization, Docker, dan Deployment Simple

Episode ini mengemas aplikasi Node.js ke dalam container: Dockerfile multi-stage yang ramping, .dockerignore, dependensi produksi dengan npm ci, menjalankan image dengan docker run, serta orchestrasi sederhana dengan docker compose.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

"Berjalan di mesin saya" adalah mimpi buruk setiap tim. Container menyelesaikannya: aplikasi dikemas bersama runtime, dependensi, dan konfigurasinya sehingga berjalan identik di laptop, server staging, dan cloud. Docker adalah cara paling umum untuk melakukannya.

Episode 21 mengemas aplikasi Node.js yang sudah kalian bangun ke dalam container: menulis Dockerfile multi-stage, membersihkan konteks build dengan .dockerignore, memasang dependensi produksi dengan npm ci, menjalankan image, dan mengatur layanan dengan docker compose.

Dockerfile Multi-Stage untuk Node.js

Prinsip Dua Stage

Dockerfile multi-stage memisahkan stage build dari stage runtime. Stage build berisi semua yang dibutuhkan untuk memasang dependensi, sedangkan stage runtime hanya membawa hasilnya — image jadi lebih kecil dan aman:

Dockerfile multi-stage
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
 
FROM node:22-alpine
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/app.js ./
COPY --from=build /app/package.json ./
USER node
EXPOSE 3000
CMD ["node", "app.js"]

node:22-alpine adalah base image resmi yang ringan. Stage build memasang dependensi dengan npm ci, lalu stage runtime menyalin node_modules hasil produksi. USER node menjalankan aplikasi sebagai user non-root — praktik keamanan dari episode 20. EXPOSE 3000 bersifat dokumentatif; pemetaan port sebenarnya terjadi saat docker run -p 3000:3000.

Menyalin Dependensi Lebih Dulu

Urutan COPY package.json package-lock.json ./ lalu RUN npm ci, kemudian COPY . . bukan kebetulan. Docker me-cache setiap layer: selama package.json dan lockfile tidak berubah, layer npm ci dipakai ulang tanpa menjalankan install ulang — build berikutnya jadi jauh lebih cepat.

.dockerignore dan Dependensi Produksi

Menjaga Konteks Build Tetap Kecil

Konteks build adalah seluruh file yang dikirim ke Docker. Tanpa filter, folder seperti node_modules dan .env ikut terkirim — besar dan berisiko. Batasi dengan .dockerignore:

.dockerignore
node_modules
.env
.git
*.log
coverage

Setiap baris di .dockerignore mengecualikan file atau folder dari konteks build. node_modules dikecualikan karena dipasang ulang di dalam image oleh npm ci, dan .env dikecualikan agar rahasia tidak tertanam di image.

npm ci vs npm install

Gunakan npm ci di dalam Dockerfile, bukan npm install. npm ci memasang dependensi persis sesuai lockfile tanpa mengubahnya — deterministik, lebih cepat, dan gagal jika lockfile tidak cocok dengan package.json. Inilah pilihan yang tepat untuk build yang reprodusibel.

Build dan Run Container

Membangun dan Menjalankan Image

Setelah Dockerfile siap, bangun image dan jalankan:

Build dan jalankan image
docker build -t api-nodejs .
docker run -d -p 3000:3000 --name api api-nodejs
docker logs api

docker build -t api-nodejs . memberi nama api-nodejs pada image. docker run -d -p 3000:3000 menjalankannya di latar belakang dan memetakan port 3000 host ke port 3000 container. Verifikasi dengan curl http://localhost:3000 dan periksa output lewat docker logs api.

Menghentikan dan Membersihkan

Kelola siklus hidup container dengan perintah sederhana:

Kelola container
docker stop api
docker rm api
docker images

docker stop api mengirim sinyal berhenti (yang dipakai aplikasi untuk graceful shutdown — episode 22), docker rm api menghapus container, dan docker images menampilkan image yang tersimpan. Untuk image yang tidak terpakai, docker image prune membersihkannya.

Orchestrasi Sederhana dengan Compose

Mendefinisikan Layanan dalam YAML

Aplikasi nyata tidak berjalan sendirian — butuh database, cache, dan layanan lain. Docker Compose mendeskripsikan seluruh layanan dalam satu file YAML dan menjalankannya bersama:

compose.yaml
services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      DATABASE_URL: postgresql://app:rahasia@db:5432/app
    depends_on:
      - db
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: rahasia
      POSTGRES_DB: app
    volumes:
      - db-data:/var/lib/postgresql/data
 
volumes:
  db-data:

File di atas mendefinisikan dua layanan: api yang dibangun dari Dockerfile, dan db yang memakai image PostgreSQL. DATABASE_URL menunjuk host db — Compose membuat network internal sehingga layanan saling menemukan lewat nama. Named volume db-data membuat data database bertahan walau container di-recreate.

Menjalankan Stack

Jalankan stack dengan Compose
docker compose up -d
docker compose ps
docker compose logs -f api

docker compose up -d membangun image dan memulai seluruh layanan di latar belakang. docker compose ps menampilkan status, dan docker compose logs -f api mengikuti log API secara real-time. Stack dihentikan dengan docker compose down.

Strategi Deployment Sederhana

Dari Container ke Server

Container memudahkan deployment ke server mana pun: kirim image ke registry, tarik di server, lalu jalankan dengan Compose. Sederhananya:

  • Build image di mesin CI dan push ke registry (Docker Hub, GHCR).
  • Di server, tarik image dan jalankan dengan Compose atau platform seperti Portainer.
  • Environment variable konfigurasi disuntikkan saat runtime, tidak dikunci dalam image.

Di episode 22, seluruh langkah ini akan diotomatiskan oleh pipeline CI/CD sehingga setiap push ke cabang utama langsung di-deploy.

Penutup

Inti yang harus dibawa pulang:

  • Dockerfile multi-stage memisahkan build dari runtime.
  • npm ci memasang dependensi persis sesuai lockfile.
  • .dockerignore mengecualikan node_modules dan .env dari build.
  • USER node menjalankan aplikasi sebagai user non-root.
  • Docker Compose mengatur beberapa layanan dalam satu file YAML.
  • Named volume menjaga data database tetap bertahan.

Di episode 22 selanjutnya — episode terakhir series ini — kita akan membahas CI/CD, monitoring, dan observability — pipeline GitHub Actions, otomasi rilis, log dan metrik terstruktur, health check, serta merangkum seluruh materi dalam menyusun aplikasi Node.js yang siap produksi dan maintainable.