1. Pengenalan Docker Compose Advanced
Docker Compose adalah tool yang memungkinkan Anda mendefinisikan dan menjalankan aplikasi multi-container menggunakan satu file YAML. Pada level dasar, Compose digunakan untuk menghubungkan beberapa service sederhana. Namun pada level advanced, Compose menjadi tool yang sangat powerful untuk mengelola arsitektur microservices yang kompleks.
Pada tutorial Docker Dasar sebelumnya, kita telah mempelajari konsep dasar Docker. Kali ini kita akan membahas fitur-fitur advanced Docker Compose yang sering digunakan dalam environment production.
Mengapa Docker Compose Advanced?
| Fitur | Compose Dasar | Compose Advanced |
|---|---|---|
| Services | 2-3 service sederhana | 10+ service dengan dependencies kompleks |
| Network | Default network | Custom networks dengan isolasi segment |
| Volumes | Anonymous volumes | Named volumes, bind mounts, driver options |
| Environment | Inline variables | .env files, variable substitution, profiles |
| Security | Tidak terkelola | Secrets, configs, read-only filesystem |
| Monitoring | Tidak ada | Health checks, restart policies, logging |
┌─────────────────────────────────────────────────────────────────┐ │ DOCKER COMPOSE PROJECT │ │ │ │ ┌───────────────────────────────────────────────────────────┐ │ │ │ frontend-network (172.20.0.0/16) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ │ │ Nginx │ │ React │ │ Vue.js │ │ │ │ │ │ (Proxy) │ │ (Web) │ │ (Admin) │ │ │ │ │ │ :80,:443 │ │ :3000 │ │ :3001 │ │ │ │ │ └────┬─────┘ └──────────┘ └──────────┘ │ │ │ └───────┼───────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────┼───────────────────────────────────────────────────┐ │ │ │ ▼ backend-network (172.21.0.0/16) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ │ │ Node.js │ │ Python │ │ Worker │ │ │ │ │ │ (API) │ │ (ML) │ │ (Celery) │ │ │ │ │ │ :4000 │ │ :5000 │ │ │ │ │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ └───────┼─────────────┼─────────────┼──────────────────────┘ │ │ │ │ │ │ │ ┌───────┼─────────────┼─────────────┼──────────────────────┐ │ │ │ ▼ ▼ ▼ data-network │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ │ │ Postgres │ │ Redis │ │ MinIO │ │ │ │ │ │ :5432 │ │ :6379 │ │ :9000 │ │ │ │ │ │ [vol:pg] │ │ [vol:rd] │ │ [vol:mi] │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ └───────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘
Arsitektur di atas menunjukkan sebuah aplikasi production-grade dengan beberapa keunggulan:
- Network Segmentation — frontend, backend, dan data layer terpisah
- Named Volumes — data persisten dengan label yang jelas
- Service Dependencies — backend menunggu database siap
- Health Checks — memastikan service benar-benar ready sebelum diakses
2. Arsitektur Multi-Service
Arsitektur multi-service memungkinkan kita memecah aplikasi menjadi beberapa service yang saling terhubung. Setiap service berjalan di container terisolasi, memiliki image, konfigurasi, dan lifecycle-nya sendiri.
Contoh: Full-Stack Application
# docker-compose.yml — Full-stack multi-service example
# BeebaneLabs - Docker Compose Advanced Tutorial
services:
# ===========================
# REVERSE PROXY (Nginx)
# ===========================
nginx:
image: nginx:1.25-alpine
container_name: app-proxy
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
api:
condition: service_healthy
networks:
- frontend
- backend
restart: unless-stopped
# ===========================
# FRONTEND (React)
# ===========================
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
args:
- VITE_API_URL=/api
container_name: app-frontend
environment:
- NODE_ENV=production
volumes:
- frontend-dist:/app/dist
networks:
- frontend
restart: unless-stopped
# ===========================
# BACKEND API (Node.js)
# ===========================
api:
build:
context: ./backend
dockerfile: Dockerfile
target: production
container_name: app-api
environment:
- DATABASE_URL=postgresql://user:${DB_PASSWORD}@postgres:5432/appdb
- REDIS_URL=redis://redis:6379
- JWT_SECRET=${JWT_SECRET}
- NODE_ENV=production
volumes:
- ./backend/uploads:/app/uploads
- api-logs:/app/logs
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
networks:
- backend
- data
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
# ===========================
# WORKER (Celery/Python)
# ===========================
worker:
build:
context: ./worker
dockerfile: Dockerfile
container_name: app-worker
environment:
- CELERY_BROKER_URL=redis://redis:6379/0
- DATABASE_URL=postgresql://user:${DB_PASSWORD}@postgres:5432/appdb
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
networks:
- backend
- data
restart: unless-stopped
deploy:
replicas: 2
# ===========================
# DATABASE (PostgreSQL)
# ===========================
postgres:
image: postgres:16-alpine
container_name: app-db
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: appdb
volumes:
- postgres-data:/var/lib/postgresql/data
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d appdb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- data
restart: unless-stopped
# ===========================
# CACHE (Redis)
# ===========================
redis:
image: redis:7-alpine
container_name: app-redis
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
networks:
- data
restart: unless-stopped
# ===========================
# NETWORKS
# ===========================
networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
backend:
driver: bridge
ipam:
config:
- subnet: 172.21.0.0/16
data:
driver: bridge
internal: true
ipam:
config:
- subnet: 172.22.0.0/16
# ===========================
# VOLUMES
# ===========================
volumes:
postgres-data:
driver: local
redis-data:
driver: local
frontend-dist:
driver: local
api-logs:
driver: local
Penjelasan Komponen Kunci
| Komponen | Fungsi | Tips |
|---|---|---|
depends_on | Menentukan urutan startup service | Gunakan condition: service_healthy agar benar-benar menunggu |
healthcheck | Memeriksa kesehatan service | Selalu tambahkan untuk service kritis seperti database |
deploy.resources | Batasan CPU dan memory | Penting untuk mencegah satu service menghabiskan semua resource |
restart | Kebijakan restart otomatis | unless-stopped cocok untuk production |
build.target | Multi-stage build target | Gunakan target berbeda untuk dev dan production |
Perintah Dasar Multi-Service
# Jalankan semua service di background docker compose up -d # Jalankan hanya service tertentu docker compose up -d api postgres redis # Lihat status semua service docker compose ps # Lihat log gabungan semua service docker compose logs -f # Lihat log service tertentu docker compose logs -f api # Restart satu service docker compose restart api # Rebuild dan restart setelah perubahan Dockerfile docker compose up -d --build api # Hentikan semua service (pertahankan data) docker compose down # Hentikan dan hapus volumes (HATI-HATI: data hilang!) docker compose down -v # Scale worker menjadi 3 instance docker compose up -d --scale worker=3
Gunakan docker compose up -d --build setiap kali Anda mengubah Dockerfile. Docker Compose tidak otomatis rebuild image jika Dockerfile berubah — Anda harus trigger secara manual.
3. Custom Networks
Secara default, Docker Compose membuat satu network bridge untuk semua service dalam satu file docker-compose.yml. Namun dalam production, Anda perlu memisahkan network berdasarkan layer arsitektur untuk keamanan dan isolasi yang lebih baik.
Jenis Network Driver
| Driver | Fungsi | Penggunaan |
|---|---|---|
bridge | Network default, container bisa saling berkomunikasi | Service yang perlu berinteraksi |
host | Container menggunakan host network langsung | Performance-critical, hindari NAT overhead |
overlay | Network lintas host (Swarm mode) | Multi-node deployment |
none | Tidak ada network | Container yang tidak perlu network |
macvlan | Container mendapat IP langsung dari LAN | Legacy app yang butuh IP sendiri |
Network Segmentation Strategy
# Network segmentation strategy
networks:
# Frontend network — exposed to outside world
# Hanya Nginx dan frontend app
frontend:
driver: bridge
driver_opts:
com.docker.network.bridge.name: br-frontend
ipam:
driver: default
config:
- subnet: 172.20.0.0/16
gateway: 172.20.0.1
labels:
- "app.network=frontend"
- "app.environment=production"
# Backend network — internal API communication
# API, Worker, dan proxy
backend:
driver: bridge
internal: false # bisa akses internet untuk API calls
ipam:
config:
- subnet: 172.21.0.0/16
labels:
- "app.network=backend"
# Data network — TIDAK bisa akses internet
# Database, cache, dan storage saja
data:
driver: bridge
internal: true # TIDAK bisa akses internet!
ipam:
config:
- subnet: 172.22.0.0/16
labels:
- "app.network=data"
Network Aliases
Network aliases memungkinkan service diakses dengan nama yang mudah diingat, bukan nama container:
services:
postgres-primary:
image: postgres:16
networks:
data:
aliases:
- db-primary
- database
- postgres
postgres-replica:
image: postgres:16
networks:
data:
aliases:
- db-replica
api:
environment:
# Service lain bisa pakai alias untuk akses
DATABASE_HOST: db-primary
REPLICA_HOST: db-replica
networks:
- backend
- data # API perlu akses ke data network
Fixed IP Addresses
services:
nginx:
networks:
frontend:
ipv4_address: 172.20.0.10
backend:
ipv4_address: 172.21.0.10
postgres:
networks:
data:
ipv4_address: 172.22.0.10
Hindari menggunakan fixed IP addresses kecuali benar-benar diperlukan (misalnya untuk whitelist di firewall). Gunakan network aliases sebagai gantinya karena lebih fleksibel dan tidak akan conflict saat scaling.
4. Named Volumes & Bind Mounts
Docker menyediakan beberapa cara untuk mengelola data persisten. Pemahaman yang benar tentang volume management sangat penting untuk production deployment.
Perbandingan Tipe Volume
| Tipe | Sintaks | Kelebihan | Kekurangan |
|---|---|---|---|
| Named Volume | postgres-data:/var/lib/pg |
Dikelola Docker, portable, backup mudah | Tidak bisa edit langsung dari host |
| Bind Mount | ./data:/var/lib/pg |
Bisa edit dari host, cocok untuk development | Path absolut, portability terbatas |
| Tmpfs | type: tmpfs, target: /tmp |
Di RAM saja, sangat cepat | Hilang saat container restart |
Advanced Volume Configuration
services:
postgres:
image: postgres:16
volumes:
# Named volume dengan mount options
- type: volume
source: postgres-data
target: /var/lib/postgresql/data
volume:
nocopy: true
# Read-only bind mount untuk config
- type: bind
source: ./postgres/postgresql.conf
target: /etc/postgresql/postgresql.conf
read_only: true
# Init scripts (read-only)
- type: bind
source: ./database/init
target: /docker-entrypoint-initdb.d
read_only: true
api:
image: node:20-alpine
volumes:
# Bind mount untuk development (hot reload)
- type: bind
source: ./backend/src
target: /app/src
# Named volume untuk uploads
- type: volume
source: api-uploads
target: /app/uploads
# tmpfs untuk temporary files (RAM-based)
- type: tmpfs
target: /app/tmp
tmpfs:
size: 100000000 # 100MB limit
mode: 1777
redis:
image: redis:7-alpine
volumes:
- type: volume
source: redis-data
target: /data
# Read-only root filesystem
read_only: true
tmpfs:
- /tmp
# Volume definitions dengan driver options
volumes:
postgres-data:
driver: local
driver_opts:
type: none
o: bind
device: /data/postgres
labels:
com.example.description: "PostgreSQL production data"
com.example.backup: "daily"
redis-data:
driver: local
labels:
com.example.description: "Redis AOF persistence"
api-uploads:
driver: local
Backup & Restore Volumes
# Backup volume ke file tar docker run --rm \ -v myapp_postgres-data:/data:ro \ -v $(pwd)/backups:/backup \ alpine tar czf /backup/postgres-data-$(date +%Y%m%d).tar.gz -C /data . # Restore volume dari file tar docker run --rm \ -v myapp_postgres-data:/data \ -v $(pwd)/backups:/backup \ alpine tar xzf /backup/postgres-data-20260626.tar.gz -C /data # List semua volumes docker volume ls # Inspect volume docker volume inspect myapp_postgres-data # Backup PostgreSQL secara konsisten (lebih aman) docker compose exec postgres pg_dump -U user appdb > backup.sql # Restore PostgreSQL docker compose exec -T postgres psql -U user appdb < backup.sql
Untuk backup database production, jangan backup volume secara langsung karena data mungkin sedang ditulis. Gunakan tool backup database seperti pg_dump atau mysqldump untuk konsistensi data.
5. Docker Compose Profiles
Profiles memungkinkan Anda mengelompokkan service berdasarkan kebutuhan. Misalnya, service monitoring dan debugging hanya diaktifkan saat development, sementara service production hanya dijalankan di server.
Konsep Profiles
Service tanpa profiles akan selalu dijalankan. Service dengan profiles hanya dijalankan ketika profile-nya diaktifkan secara eksplisit.
services:
# ===========================
# SERVICE UTAMA (selalu jalan)
# ===========================
api:
build: ./backend
profiles: [] # Tidak ada profile = selalu jalan
depends_on:
- postgres
- redis
postgres:
image: postgres:16
volumes:
- postgres-data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
# ===========================
# DEVELOPMENT ONLY
# ===========================
frontend-dev:
build:
context: ./frontend
dockerfile: Dockerfile.dev
profiles:
- development
ports:
- "3000:3000"
volumes:
- ./frontend/src:/app/src # Hot reload
environment:
- NODE_ENV=development
mailhog:
image: mailhog/mailhog
profiles:
- development
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
# ===========================
# DEBUGGING / ADMIN TOOLS
# ===========================
pgadmin:
image: dpage/pgadmin4
profiles:
- debug
- admin
environment:
PGADMIN_DEFAULT_EMAIL: admin@beebanelabs.com
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_PASSWORD}
ports:
- "5050:80"
redis-commander:
image: rediscommander/redis-commander
profiles:
- debug
- admin
environment:
REDIS_HOSTS: local:redis:6379
ports:
- "8081:8081"
portainer:
image: portainer/portainer-ce
profiles:
- admin
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer-data:/data
ports:
- "9000:9000"
# ===========================
# MONITORING
# ===========================
prometheus:
image: prom/prometheus
profiles:
- monitoring
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
ports:
- "9090:9090"
grafana:
image: grafana/grafana
profiles:
- monitoring
volumes:
- grafana-data:/var/lib/grafana
- ./monitoring/dashboards:/etc/grafana/provisioning/dashboards:ro
ports:
- "3000:3000"
# ===========================
# TESTING
# ===========================
test-runner:
build:
context: .
dockerfile: Dockerfile.test
profiles:
- test
environment:
- DATABASE_URL=postgresql://test:test@postgres-test:5432/testdb
depends_on:
postgres-test:
condition: service_healthy
postgres-test:
image: postgres:16
profiles:
- test
environment:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
tmpfs:
- /var/lib/postgresql/data # RAM-based untuk test cepat
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test"]
interval: 5s
timeout: 3s
retries: 3
volumes:
postgres-data:
portainer-data:
prometheus-data:
grafana-data:
Cara Menggunakan Profiles
# Jalankan HANYA service default (tanpa profile) docker compose up -d # Aktifkan profile development docker compose --profile development up -d # Aktifkan multiple profiles docker compose --profile debug --profile monitoring up -d # Aktifkan SEMUA profiles docker compose --profile development --profile debug --profile admin --profile monitoring --profile test up -d # Atau gunakan environment variable COMPOSE_PROFILES=development,debug docker compose up -d # Jalankan test dengan profile test docker compose --profile test run --rm test-runner npm test # Lihat service berdasarkan profile docker compose --profile admin ps
Gunakan profile naming yang konsisten: development, staging, production, debug, admin, monitoring, test. Simpan profile combinations di script shell agar tim tidak perlu menghafal flag yang panjang.
6. Environment Variables & .env Files
Pengelolaan environment variables yang baik adalah kunci keamanan dan portabilitas Docker Compose. Jangan pernah hardcode secrets di dalam docker-compose.yml.
Strategi Environment Variables
# .env — untuk Docker Compose variable substitution # JANGAN commit file ini ke git! # Database DB_PASSWORD=super-secret-password-here DB_NAME=appdb DB_USER=appuser # Application JWT_SECRET=my-jwt-secret-key API_KEY=sk-1234567890abcdef # Docker Compose project name COMPOSE_PROJECT_NAME=myapp # Image tags (bisa di-override per environment) API_IMAGE_TAG=latest FRONTEND_IMAGE_TAG=latest
# .env.example — Template environment variables # Copy ke .env dan isi dengan nilai yang sesuai # Database DB_PASSWORD=CHANGE_ME DB_NAME=appdb DB_USER=appuser # Application JWT_SECRET=CHANGE_ME API_KEY=CHANGE_ME # Docker COMPOSE_PROJECT_NAME=myapp API_IMAGE_TAG=latest FRONTEND_IMAGE_TAG=latest
Variable Substitution di docker-compose.yml
services:
api:
image: myapp/api:${API_IMAGE_TAG:-latest}
environment:
# Dari .env file
- DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
- JWT_SECRET=${JWT_SECRET}
# Default values (jika tidak ada di .env)
- NODE_ENV=${NODE_ENV:-production}
- LOG_LEVEL=${LOG_LEVEL:-info}
- PORT=${API_PORT:-4000}
# Literal value (tidak dari .env)
- SERVICE_NAME=api
# Atau gunakan env_file untuk bulk import
env_file:
- path: ./config/common.env
required: true
- path: ./config/secrets.env
required: false # Tidak error jika file tidak ada
Docker Secrets (Swarm Mode)
# Untuk Docker Swarm, gunakan secrets
# Secrets di-mount sebagai file di /run/secrets/
services:
api:
image: myapp/api
secrets:
- db_password
- jwt_secret
- api_key
environment:
# Baca secret dari file
- DB_PASSWORD_FILE=/run/secrets/db_password
- JWT_SECRET_FILE=/run/secrets/jwt_secret
postgres:
image: postgres:16
secrets:
- db_password
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt
jwt_secret:
file: ./secrets/jwt_secret.txt
api_key:
external: true # Sudah ada di Docker Swarm
Selalu tambahkan .env dan secrets/ ke .gitignore. Gunakan .env.example sebagai template. Untuk production, pertimbangkan menggunakan Docker Secrets atau external secrets manager seperti HashiCorp Vault atau AWS Secrets Manager.
7. Health Checks & Dependencies
Health checks memastikan service benar-benar siap menerima request, bukan hanya sekadar container yang sudah running. Ini sangat penting untuk service dependencies seperti database yang perlu waktu untuk inisialisasi.
Health Check Patterns
services:
# PostgreSQL — gunakan pg_isready
postgres:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# MySQL — gunakan mysqladmin
mysql:
image: mysql:8
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 40s
# Redis — gunakan redis-cli ping
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
# MongoDB — gunakan mongosh
mongo:
image: mongo:7
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
# HTTP API — gunakan curl
api:
build: ./backend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
# Node.js dengan custom health script
node-app:
build: ./node-app
healthcheck:
test: ["CMD", "node", "healthcheck.js"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
# Elasticsearch
elasticsearch:
image: elasticsearch:8.10.0
healthcheck:
test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -q '\"status\":\"green\"\\|\"status\":\"yellow\"'"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
Conditional Dependencies
services:
api:
build: ./backend
depends_on:
# Tunggu sampai postgres benar-benar sehat
postgres:
condition: service_healthy
# Tunggu sampai redis benar-benar sehat
redis:
condition: service_healthy
# Tunggu sampai migrasi selesai (service_started + exit)
migrate:
condition: service_completed_successfully
# Database migration (berjalan sekali lalu exit)
migrate:
build: ./backend
command: ["npm", "run", "migrate"]
environment:
- DATABASE_URL=postgresql://user:${DB_PASSWORD}@postgres:5432/appdb
depends_on:
postgres:
condition: service_healthy
profiles:
- migration
Monitoring Health Status
# Cek status health semua container
docker compose ps
# NAME STATUS PORTS
# app-api Up 5 min (healthy) 0.0.0.0:4000->4000/tcp
# app-db Up 5 min (healthy) 5432/tcp
# app-redis Up 5 min (healthy) 6379/tcp
# Detail health check dari satu container
docker inspect --format='{{json .State.Health}}' app-api | jq
# Log health check history
docker inspect --format='{{range .State.Health.Log}}{{.Output}}{{end}}' app-api
8. Scaling Services
Docker Compose memungkinkan Anda menjalankan multiple instance dari satu service untuk menangani beban yang lebih tinggi.
services:
worker:
build: ./worker
# Jangan hardcode container_name jika ingin scale!
# container_name: app-worker ← JANGAN ini
environment:
- REDIS_URL=redis://redis:6379
- WORKER_CONCURRENCY=4
deploy:
replicas: 3
resources:
limits:
cpus: '0.50'
memory: 256M
reservations:
cpus: '0.25'
memory: 128M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
# Scale worker menjadi 5 instance docker compose up -d --scale worker=5 # Scale API menjadi 3 instance docker compose up -d --scale api=3 # Perhatikan: port binding akan conflict jika di-scale! # Solusi: gunakan range port atau Nginx load balancer # ports: # - "4000-4010:4000" ← assign port otomatis
Untuk production scaling, pertimbangkan menggunakan Docker Swarm (docker stack deploy) atau Kubernetes. Docker Compose scaling cocok untuk development dan staging, tetapi tidak memiliki fitur load balancing built-in.
9. Production-Ready Patterns
Multi-File Compose Override
Gunakan file terpisah untuk konfigurasi berbeda:
# Struktur file: # docker-compose.yml ← Base config # docker-compose.override.yml ← Auto-loaded, untuk development # docker-compose.prod.yml ← Production overrides # docker-compose.test.yml ← Testing overrides # Development (auto-load base + override) docker compose up -d # Production (base + prod override) docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d # Testing (base + test override) docker compose -f docker-compose.yml -f docker-compose.test.yml up -d
# docker-compose.prod.yml — Production overrides
services:
api:
restart: always
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
environment:
- NODE_ENV=production
- LOG_LEVEL=warn
nginx:
restart: always
logging:
driver: json-file
options:
max-size: "5m"
max-file: "5"
postgres:
restart: always
command: >
postgres
-c shared_buffers=256MB
-c effective_cache_size=768MB
-c maintenance_work_mem=64MB
-c work_mem=4MB
-c max_connections=200
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
Read-Only Filesystem & Security Hardening
services:
api:
build: ./backend
# Read-only root filesystem
read_only: true
# tmpfs untuk directory yang perlu write
tmpfs:
- /tmp
- /app/tmp
# Non-root user
user: "1000:1000"
# Drop capabilities
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # Hanya jika perlu bind port < 1024
# Security options
security_opt:
- no-new-privileges:true
# Batasi PID (mencegah fork bomb)
pids_limit: 100
postgres:
image: postgres:16
read_only: true
tmpfs:
- /tmp
- /run/postgresql
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- FOWNER
- SETGID
- SETUID
security_opt:
- no-new-privileges:true
10. Troubleshooting & Debugging
Perintah Debugging yang Sering Digunakan
# =========================== # DIAGNOSTICS # =========================== # Cek status semua service docker compose ps -a # Lihat log dengan timestamp docker compose logs -f -t api # Lihat log 100 baris terakhir docker compose logs --tail 100 api # Resource usage (CPU, memory, network) docker compose top docker stats --no-stream # Inspect service configuration (resolved) docker compose config # Validasi compose file tanpa menjalankan docker compose config --quiet # =========================== # DEBUGGING CONTAINER # =========================== # Masuk ke container yang sedang berjalan docker compose exec api /bin/sh # Jalankan command satu kali docker compose exec api env # Masuk ke container baru (tidak ke yang running) docker compose run --rm api /bin/sh # =========================== # NETWORK DEBUGGING # =========================== # List networks docker network ls # Inspect network docker network inspect myapp_frontend # Tes koneksi antar container docker compose exec api ping postgres docker compose exec api nc -zv postgres 5432 # =========================== # CLEANUP # =========================== # Hapus stopped containers docker compose rm -f # Hapus images yang tidak digunakan docker image prune -f # Hapus semua unused resources docker system prune -f --volumes
Common Issues & Solutions
| Masalah | Penyebab | Solusi |
|---|---|---|
| Port already allocated | Port sudah digunakan oleh proses lain | lsof -i :PORT lalu kill proses atau ganti port |
| Connection refused | Service belum siap atau network salah | Tambahkan health check dan depends_on condition |
| Volume permission denied | User ID di container berbeda dengan host | Set user: "UID:GID" atau ubah permission di Dockerfile |
| Out of memory | Container melebihi memory limit | Naikkan deploy.resources.limits.memory |
| Environment variable tidak terbaca | Nama file .env salah atau path berbeda | Gunakan docker compose config untuk verifikasi |
11. Quiz Pemahaman
Uji pemahaman Anda tentang Docker Compose Advanced:
1. Apa fungsi dari condition: service_healthy di depends_on?
2. Apa fungsi internal: true pada network definition?
3. Mengapa container_name TIDAK disarankan saat ingin scaling?
4. Apa perbedaan named volume dan bind mount?
5. Kapan service dengan profiles dijalankan?