Multi-Container #

Salah satu kekuatan utama Docker Compose adalah kemampuannya mengelola banyak container sebagai satu kesatuan yang kohesif. Saat aplikasimu punya beberapa service yang saling terkait — web frontend, API backend, database, cache, message broker — Compose memungkinkan kamu mendefinisikan, menjalankan, dan menghentikan semuanya dengan satu perintah.

Artikel ini membahas pola-pola multi-container yang umum, cara Compose mengelola networking dan dependency antar service, serta best practice untuk stack yang bersih dan maintainable.

Pola Arsitektur Multi-Container #

Ada beberapa pola arsitektur yang umum dijumpai di aplikasi multi-container. Masing-masing punya karakteristik dan use case yang berbeda.

Three-Tier Architecture #

Pola paling klasik: presentation tier (web), application tier (API), data tier (database). Setiap tier adalah service terpisah.

flowchart LR
    U[User] --> W[Web Frontend]
    W --> A[API Backend]
    A --> D[(Database)]
    A --> C[(Cache)]
services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
    depends_on:
      - api

  api:
    build: ./api
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/myapp
      - REDIS_URL=redis://cache:6379
    depends_on:
      - db
      - cache

  db:
    image: postgres:16-alpine
    volumes:
      - db-data:/var/lib/postgresql/data

  cache:
    image: redis:7-alpine

volumes:
  db-data:

Pola ini jelas pemisahan tanggung jawabnya. Web layer scalable terpisah dari API. Database terisolasi. Cache opsional.

Microservices #

Aplikasi besar dipecah menjadi banyak service kecil, masing-masing dengan tanggung jawab tunggal. Compose bisa manage stack microservice untuk development, meski production biasanya butuh Kubernetes.

flowchart TB
    GW[API Gateway] --> U[user-service]
    GW --> O[order-service]
    GW --> P[product-service]
    GW --> Pay[payment-service]
    U --> UD[(user-db)]
    O --> OD[(order-db)]
    P --> PD[(product-db)]
    Pay --> PayDB[(payment-db)]

Untuk 5+ service, Compose masih bisa handle, tapi monitoring, scaling, dan deployment butuh orchestrator yang lebih advanced.

Frontend + Backend #

Pola umum di mana frontend dan backend di-deploy terpisah.

services:
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    environment:
      - API_URL=http://api:8080

  backend:
    build: ./backend
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/myapp

  db:
    image: postgres:16-alpine
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

Frontend pakai port 3000 (atau 5173 untuk Vite), backend 8080. Compose otomatis buat network internal sehingga frontend bisa komunikasi dengan backend via hostname api (nama service).

Worker + Queue #

Aplikasi yang punya worker asynchronous processing message dari queue.

services:
  api:
    build: ./api
    environment:
      - QUEUE_URL=amqp://rabbitmq:5672

  worker:
    build: ./worker
    environment:
      - QUEUE_URL=amqp://rabbitmq:5672
    depends_on:
      - rabbitmq

  rabbitmq:
    image: rabbitmq:3-management
    ports:
      - "15672:15672"

Worker dan API share konfigurasi queue yang sama. Scaling worker mudah: docker compose up -d --scale worker=5.

Networking Antar Service #

Salah satu hal paling powerful dari Compose adalah automatic service discovery di network internal. Service di file Compose yang sama otomatis bisa saling resolve via hostname = nama service.

services:
  api:
    image: myapi
    environment:
      - DATABASE_URL=postgres://postgres:secret@db:5432/myapp

Pada environment variable di atas, db adalah hostname. Compose otomatis membuat DNS entry di internal network sehingga api bisa resolve db ke IP container database.

Network Default #

Jika kamu tidak mendefinisikan network eksplisit, Compose membuat satu network default yang dinamai seperti <project>_default. Semua service otomatis join ke network ini.

# Lihat network yang dibuat Compose
docker network ls

# Inspect network
docker network inspect myapp_default

Multiple Networks #

Untuk kontrol lebih, definisikan beberapa network dan assign service sesuai perannya.

services:
  web:
    networks:
      - frontend
      - backend
  
  api:
    networks:
      - frontend
      - backend
  
  db:
    networks:
      - backend

networks:
  frontend:
  backend:

Pada konfigurasi ini, db hanya ada di network backend. Service web punya interface di kedua network. Service db tidak bisa di-reach dari luar backend.

Ini pattern umum untuk defense in depth — service kritis dipisah dari frontend network.

Aliases #

Service bisa punya beberapa hostname dalam satu network.

services:
  api:
    networks:
      frontend:
        aliases:
          - api-service
          - myapi

Service api sekarang bisa di-reach via api, api-service, atau myapi di network frontend.

External Network #

Kamu bisa pakai network yang sudah ada di Docker (bukan yang dibuat Compose).

services:
  api:
    networks:
      - shared-network

networks:
  shared-network:
    external: true
    name: my-shared-network

Berguna untuk menghubungkan stack Compose yang berbeda atau service yang di-manage di luar Compose (mis. database managed).

Dependency Management #

Compose mendukung dua jenis dependency: depends_on (startup order) dan links (legacy, jarang dipakai).

Depends On: Startup Order #

services:
  web:
    depends_on:
      - api
  
  api:
    depends_on:
      - db

Dalam contoh, db start dulu, lalu api, lalu web. Tapi depends_on hanya menunggu container start, bukan service siap menerima request.

Untuk benar-benar menunggu service siap, tambahkan healthcheck dan condition.

services:
  api:
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started

  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

condition bisa berupa:

  • service_started (default) — container sudah start.
  • service_healthy — healthcheck pass.
  • service_completed_successfully — container exit dengan code 0.

Long Startup Dependencies #

Beberapa service butuh waktu lama untuk siap (database besar, JVM warm-up, lazy initialization). Healthcheck + retry adalah kombinasi yang tepat.

services:
  api:
    depends_on:
      db:
        condition: service_healthy
    restart: on-failure

  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d myapp"]
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 30s

start_period memberi service waktu 30 detik tanpa healthcheck dianggap gagal — berguna untuk service yang butuh waktu inisialisasi panjang.

Sharing Data Antar Service #

Container biasanya terisolasi, tapi kadang perlu berbagi data. Compose mendukung ini via volumes.

Shared Volume #

services:
  web:
    volumes:
      - uploads:/var/www/uploads
  
  processor:
    volumes:
      - uploads:/var/lib/processor

volumes:
  uploads:

Web meng-upload file ke /var/www/uploads, processor membaca dari /var/lib/processor. Volume uploads adalah shared storage.

Read-Only Mount #

Untuk container yang hanya butuh baca:

services:
  web:
    volumes:
      - configs:/etc/app:ro

:ro membuat mount read-only. Service hanya bisa baca, tidak bisa tulis.

Init Container Pattern #

Container yang jalan duluan untuk setup data, lalu exit.

services:
  app:
    volumes:
      - data:/app/data
    depends_on:
      - init-data
  
  init-data:
    image: alpine
    volumes:
      - data:/app/data
    command: sh -c "echo '$(date)' > /app/data/init.txt"
    restart: "no"

Init container jalan sekali, tulis data, exit. Service app bisa pakai data yang sudah disiapkan.

Service Scaling #

Compose mendukung scaling service ke beberapa instance dengan flag --scale.

# Scale web ke 3 instance
docker compose up -d --scale web=3

# Scale worker ke 5 instance
docker compose up -d --scale worker=5

Setiap instance adalah container terpisah. Mereka share konfigurasi tapi punya state terpisah (filesystem, proses ID, dll).

Load Balancing #

Untuk service yang di-scale, kamu butuh load balancer di depan. Compose sendiri tidak punya internal load balancing.

services:
  web:
    build: ./web
    expose:
      - "3000"
  
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - web

expose (bukan ports) membuat port hanya accessible di internal network, bukan host. Nginx di depan jadi satu-satunya entry point.

Scaling punya limit. --scale tidak bisa di pakai untuk service yang punya container_name (nama harus unik). Juga tidak kompatibel dengan ports shorthand (gunakan long form target: published agar tiap container dapet published port unik).

Environment-Specific Configuration #

Untuk setup yang sama dipakai di development, staging, dan production, pakai multiple Compose files.

# docker-compose.yml (base)
services:
  web:
    build: .
    environment:
      - NODE_ENV=production

# docker-compose.override.yml (development, auto-loaded)
services:
  web:
    environment:
      - NODE_ENV=development
    volumes:
      - ./src:/app/src  # hot reload
    ports:
      - "3000:3000"

Saat docker compose up di development, base + override di-merge otomatis. Untuk production, jalankan hanya base:

docker compose -f docker-compose.yml up -d

Best Practice #

Pisahkan Konfigurasi per Environment #

Gunakan file terpisah untuk development, staging, production. Secret management via secrets: (untuk compose di Swarm) atau .env (untuk development).

Beri Nama Service yang Deskriptif #

Nama service jadi hostname internal. Pilih nama yang menjelaskan fungsinya.

# BAGUS
services:
  web:
    ...
  api:
    ...
  db:
    ...
  cache:
    ...

# BURUK
services:
  container1:
    ...
  myservice:
    ...
  app:
    ...

Gunakan Healthcheck untuk Dependency Kritis #

Service yang punya dependency wajib yang harus siap sebelum dependent start, tambahkan healthcheck dan condition: service_healthy.

Hindari Hardcoded Secret #

Gunakan .env file atau secrets: (untuk Swarm). Jangan hardcode password di YAML.

Volume untuk Data Persisten #

Selalu. Database, upload, log penting. Container bisa di-recreate tanpa kehilangan data.

Image Tag Eksplisit #

image: postgres:16-alpine    # BAGUS
image: postgres:latest       # BURUK - surprise update

latest tag bisa berubah tanpa kamu sadari. Selalu pin versi.

Resource Limits #

Untuk mencegah satu service menghabiskan semua resource, set limits.

services:
  api:
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 1G
Kunci sukses multi-container: clear dependency graph, healthcheck untuk service yang lambat start, network isolation sesuai peran, dan resource limits agar satu service tidak mengganggu yang lain. Mulai dengan stack kecil, tambahkan service satu per satu, dan selalu test scaling dan restart behavior.

Contoh Praktis: Stack E-Commerce Lengkap #

Stack e-commerce nyata biasanya punya 6-10 service. Contoh di bawah ini menunjukkan pola yang clean.

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/certs:/etc/nginx/certs:ro
    depends_on:
      - web
    networks:
      - frontend
    restart: unless-stopped

  web:
    build: ./web
    environment:
      - API_URL=http://api:8080
    depends_on:
      - api
    networks:
      - frontend
      - backend
    restart: unless-stopped

  api:
    build: ./api
    environment:
      - DATABASE_URL=postgres://shop:secret@db:5432/shop
      - REDIS_URL=redis://cache:6379
      - RABBITMQ_URL=amqp://guest:guest@mq:5672
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_healthy
      mq:
        condition: service_healthy
    networks:
      - backend
    restart: unless-stopped

  worker:
    build: ./worker
    environment:
      - DATABASE_URL=postgres://shop:secret@db:5432/shop
      - REDIS_URL=redis://cache:6379
      - RABBITMQ_URL=amqp://guest:guest@mq:5672
    depends_on:
      - api
    networks:
      - backend
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=shop
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=shop
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U shop -d shop"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - backend
    restart: unless-stopped

  cache:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
    networks:
      - backend
    restart: unless-stopped

  mq:
    image: rabbitmq:3-management
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - backend
    restart: unless-stopped

volumes:
  db-data:

networks:
  frontend:
  backend:

Stack ini punya 7 service, 2 network, dan 1 volume. nginx adalah satu-satunya service yang di-expose ke host. Semua komunikasi internal terjadi via network backend yang tidak di-expose.

Observability #

Saat stack multi-container dijalankan, kamu butuh visibility ke setiap service.

Logs — Compose punya built-in log aggregation:

# Semua log
docker compose logs

# Follow log service tertentu
docker compose logs -f api

# Tail 100 baris terakhir
docker compose logs --tail 100

# Dengan timestamp
docker compose logs -t

Health status — lihat status semua service:

docker compose ps

Resource usage — lihat CPU dan memory per service:

docker stats

Untuk production-grade observability (metrics, distributed tracing), kamu butuh tool tambahan: Prometheus + Grafana, Datadog, New Relic, atau platform equivalent. Compose sendiri tidak menyediakan observability built-in.

Migration ke Production #

Saat stack siap untuk production, ada beberapa hal yang perlu diubah.

1. External database — Jangan jalankan database production di Compose di single host. Gunakan managed database (RDS, Cloud SQL) atau setup multi-host dengan replication.

2. Secret management — Ganti .env dengan secret manager proper: AWS Secrets Manager, HashiCorp Vault, atau Kubernetes Secrets.

3. Reverse proxy + TLS — Di production, selalu terminate TLS di reverse proxy. Cert-manager di Kubernetes atau Caddy/Traefik bisa otomatis manage sertifikat Let’s Encrypt.

4. Resource limits — Set CPU dan memory limit agar satu service tidak mengganggu service lain.

5. Log aggregation — Kirim log ke centralized logging (CloudWatch, Stackdriver, ELK, Loki) untuk retention dan searchability.

6. Health monitoring — Integrate dengan alerting (PagerDuty, OpsGenie) untuk notifikasi insiden.

7. Backup strategy — Database harus di-backup regular ke storage terpisah. Test restore procedure.

Setelah semua ini, baru pertimbangkan migrasi ke Kubernetes atau platform managed jika skala sudah melampaui single host.

Compose di production single host OK untuk traffic rendah-menengah. Untuk high traffic, multi-host, high availability, atau zero-downtime deploy, butuh orchestrator yang lebih powerful. Jangan paksakan Compose untuk workload yang butuh auto-scaling atau self-healing.

Ringkasan #

  • Multi-container adalah kekuatan utama Docker Compose. Compose mengelola networking, dependency, dan lifecycle beberapa service sebagai satu stack.
  • Pola umum: three-tier, microservices, frontend+backend, worker+queue. Pilih sesuai kompleksitas aplikasi.
  • Automatic service discovery — service di Compose yang sama bisa saling resolve via hostname = nama service, tanpa setup DNS manual.
  • Multiple networks untuk defense in depth — pisahkan service publik (frontend) dari yang sensitif (backend).
  • Depends_on hanya menunggu container start. Untuk benar-benar menunggu service siap, pakai healthcheck + condition: service_healthy.
  • Shared volume untuk data sharing antar service. init container pattern untuk setup data sebelum service utama start.
  • --scale untuk multiple instance, tapi butuh reverse proxy/load balancer di depan.
  • Multi-file Compose (-f override.yml) memungkinkan konfigurasi base yang sama untuk development, staging, production.
  • Best practice: clear dependency graph, healthcheck untuk service kritis, image tag eksplisit, resource limits, secret via .env/secrets:, volume untuk data persisten.
  • Compose cocok untuk dev/test/small prod. Untuk multi-host dan high availability, butuh Kubernetes atau platform managed.

← Sebelumnya: Struktur File   Berikutnya: Environment Variable →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact