Docker untuk Developer: Containerize Aplikasi Node.js
Docker membantu developer menjalankan aplikasi dalam container. Container berisi runtime, dependency, konfigurasi dasar, dan proses aplikasi sehingga lingkungan development, testing, dan deployment dapat dibuat lebih konsisten.
Masalah klasik dalam pengembangan aplikasi adalah "jalan di laptop saya, tetapi error di laptop orang lain". Docker tidak menghapus semua masalah, tetapi membantu membuat lingkungan lebih eksplisit dan dapat direplikasi.
Tujuan Belajar
Setelah membaca tutorial ini, kamu diharapkan mampu:
- Menjelaskan perbedaan image dan container.
- Membuat
Dockerfileuntuk aplikasi Node.js sederhana. - Menjalankan container dengan port dan environment variable.
- Memahami volume untuk data yang perlu bertahan.
- Memakai Docker Compose untuk menjalankan aplikasi dan database bersama.
- Membaca log container dan melakukan debugging dasar.
- Menghindari kesalahan umum seperti memasukkan secret ke image.
Image dan Container
Image adalah blueprint. Container adalah proses yang berjalan dari image. Analogi sederhananya: image seperti resep, container seperti masakan yang dibuat dari resep tersebut.
| Istilah | Makna |
|---|---|
| Image | Paket berisi filesystem, runtime, dependency, dan instruksi menjalankan aplikasi. |
| Container | Instance berjalan dari image. |
| Dockerfile | File instruksi untuk membangun image. |
| Registry | Tempat menyimpan dan mengambil image, misalnya Docker Hub atau registry privat. |
Satu image dapat dipakai untuk membuat banyak container.
Aplikasi Node.js Sederhana
Misalkan kita punya aplikasi Express kecil.
{
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.0"
}
}File server.js:
const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
app.get("/", (_req, res) => {
res.json({
message: "Aplikasi berjalan di Docker",
time: new Date().toISOString(),
});
});
app.listen(port, () => {
console.log(`Server berjalan di port ${port}`);
});Aplikasi ini membaca PORT dari environment variable. Pola ini penting karena port dan konfigurasi sering berbeda antara development dan production.
Dockerfile Dasar
Dockerfile berikut membangun image untuk aplikasi Node.js.
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["npm", "start"]Penjelasan singkat:
FROMmenentukan base image.WORKDIRmenentukan folder kerja di dalam container.COPY package*.json ./menyalin manifest dependency lebih dulu agar cache build lebih efektif.RUN npm ci --omit=devmenginstal dependency production.COPY . .menyalin source code aplikasi.EXPOSEmendokumentasikan port yang dipakai aplikasi.CMDmenentukan command default saat container berjalan.
.dockerignore
Jangan menyalin semua file lokal ke image. Buat .dockerignore.
node_modules
npm-debug.log
.git
.env
.next
coverageFile .env sebaiknya tidak masuk image. Secret diberikan saat runtime melalui environment variable atau secret manager.
Build dan Run
Bangun image:
docker build -t node-docker-demo .Jalankan container:
docker run --rm -p 3000:3000 node-docker-demoFormat -p 3000:3000 berarti port 3000 di host diarahkan ke port 3000 di container.
Menjalankan dengan environment variable:
docker run --rm -p 8080:8080 -e PORT=8080 node-docker-demoLog dan Debugging Dasar
Melihat container berjalan:
docker psMelihat log:
docker logs <container-id>Masuk ke shell container:
docker exec -it <container-id> shPada image Alpine, shell yang tersedia biasanya sh, bukan bash.
Volume: Data yang Bertahan
Container bersifat disposable. Jika container dihapus, perubahan file di dalam container bisa ikut hilang. Volume dipakai untuk data yang perlu bertahan, misalnya data database.
docker volume create pgdataContoh menjalankan PostgreSQL dengan volume:
docker run --rm \
--name demo-postgres \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=app_db \
-v pgdata:/var/lib/postgresql/data \
-p 5432:5432 \
postgres:16Pada Windows PowerShell, multiline command bisa berbeda. Jika bingung, tulis command dalam satu baris terlebih dahulu.
Network: Komunikasi Antarcontainer
Jika aplikasi dan database berjalan sebagai container terpisah, keduanya perlu berada di network yang sama.
docker network create app-networkDatabase:
docker run -d \
--name db \
--network app-network \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=app_db \
postgres:16Aplikasi:
docker run -d \
--name app \
--network app-network \
-p 3000:3000 \
-e DATABASE_URL=postgresql://postgres:secret@db:5432/app_db \
node-docker-demoPerhatikan host database memakai db, yaitu nama container database dalam network yang sama.
Docker Compose
Docker Compose membuat definisi multi-container lebih rapi.
services:
app:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://postgres:secret@db:5432/app_db
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: app_db
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:Jalankan:
docker compose up --buildHentikan:
docker compose downJika ingin menghapus volume juga, gunakan opsi -v dengan hati-hati karena data database akan ikut terhapus.
Development vs Production
Dockerfile production biasanya menginstal dependency production dan menjalankan build final. Untuk development, kita sering butuh hot reload dan bind mount.
Contoh Compose development:
services:
app:
image: node:20-alpine
working_dir: /app
command: sh -c "npm install && npm run dev"
ports:
- "3000:3000"
volumes:
- .:/app
- node_modules:/app/node_modules
environment:
NODE_ENV: development
volumes:
node_modules:Pola development perlu disesuaikan dengan framework yang digunakan. Tujuannya adalah feedback cepat, bukan image sekecil mungkin.
Kesalahan Umum
| Kesalahan | Dampak | Perbaikan |
|---|---|---|
Menyalin .env ke image | Secret bisa bocor | Masukkan .env ke .dockerignore. |
| Tidak memakai volume untuk database | Data hilang saat container dihapus | Gunakan named volume. |
Menganggap localhost selalu menunjuk host | Di container, localhost adalah container itu sendiri | Gunakan nama service/container di network. |
| Image terlalu besar | Build dan pull lambat | Gunakan base image sesuai kebutuhan dan .dockerignore. |
| Menghapus volume tanpa sadar | Data hilang | Baca command sebelum memakai docker compose down -v. |
Coba Sendiri
Buat aplikasi Node.js kecil dengan endpoint berikut:
GET /mengembalikan pesan aplikasi berjalan.GET /healthmengembalikan{ "status": "ok" }.- Port dibaca dari environment variable
PORT.
Kemudian:
- Buat
Dockerfile. - Buat
.dockerignore. - Build image.
- Jalankan container di port 3000.
- Ubah port menjadi 8080 melalui environment variable.
- Tambahkan Compose file dengan service
appdandb.
Catat command yang kamu jalankan dan error yang muncul jika ada. Dokumentasi kecil seperti ini akan sangat membantu saat deployment.
Cek Pemahaman
Kuis
Apa perbedaan paling tepat antara image dan container?
Kuis
Mengapa database dalam container biasanya memakai volume?
Ringkasan
Docker membantu membuat lingkungan aplikasi lebih konsisten. Pahami image, container, Dockerfile, .dockerignore, port, environment variable, volume, network, dan Compose. Untuk aplikasi produksi, jaga image tetap bersih dan jangan memasukkan secret. Untuk development, susun workflow yang memberi feedback cepat tanpa mengorbankan pemahaman terhadap container.