DOCKER DOCKER-COMPOSE CONTAINERS DEVOPS BEST-PRACTICES DEVELOPMENT DEPLOYMENT MICROSERVICES ORCHESTRATION YAML

Docker Compose 101: Best Practices untuk Lingkungan Dev & Produksi Ringan

⏱️ 14 menit baca
👨‍💻

Docker Compose 101: Best Practices untuk Lingkungan Dev & Produksi Ringan

1. Pendahuluan

Pernahkah kamu merasa kewalahan saat harus menjalankan beberapa layanan sekaligus untuk proyek webmu? Mungkin ada aplikasi backend, database, cache, dan worker yang semuanya perlu jalan bersamaan. Menjalankan mereka satu per satu di terminal yang berbeda bisa jadi sangat merepotkan dan rawan kesalahan. Nah, di sinilah Docker Compose datang sebagai pahlawan!

Docker Compose adalah alat dari Docker yang memungkinkan kamu mendefinisikan dan menjalankan aplikasi multi-container Docker dengan satu perintah saja. Bayangkan kamu punya sebuah orkestra, dan Docker Compose adalah konduktornya yang memastikan setiap musisi (container) bermain sesuai partitur (konfigurasi) yang diberikan.

Artikel ini akan membawa kamu menyelami Docker Compose, dari dasar-dasar hingga praktik terbaik untuk menggunakannya secara efektif. Kita akan fokus pada bagaimana Docker Compose bisa memudahkan workflow pengembanganmu, serta memberikan tips untuk lingkungan produksi skala kecil atau menengah yang lebih robust. Siap menyederhanakan manajemen container-mu? Mari kita mulai! 🚀

2. Memulai dengan Docker Compose: Contoh Sederhana

📌 Apa itu Docker Compose? Docker Compose memungkinkan kamu mendefinisikan services (container), networks, dan volumes dalam satu file YAML (docker-compose.yml). Setelah terdefinisi, kamu bisa meluncurkan, menghentikan, dan mengelola seluruh aplikasi multi-container hanya dengan beberapa perintah.

Mari kita lihat contoh sederhana: sebuah aplikasi backend Node.js yang berkomunikasi dengan database PostgreSQL.

# docker-compose.yml
services:
  web: # Nama service untuk aplikasi backend kita
    build: . # Build image dari Dockerfile di direktori saat ini
    ports:
      - "3000:3000" # Map port 3000 host ke port 3000 container
    environment:
      DATABASE_URL: postgres://user:password@db:5432/mydatabase
    depends_on:
      - db # Service 'web' bergantung pada 'db'
    volumes:
      - .:/app # Bind mount kode lokal ke dalam container untuk hot-reloading (development)

  db: # Nama service untuk database PostgreSQL
    image: postgres:13 # Menggunakan image PostgreSQL versi 13 dari Docker Hub
    environment:
      POSTGRES_DB: mydatabase
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db_data:/var/lib/postgresql/data # Named volume untuk persistensi data database

volumes:
  db_data: # Definisi named volume

Penjelasan Singkat:

Cara Menjalankan:

  1. Pastikan kamu memiliki Dockerfile untuk aplikasi Node.js-mu di direktori yang sama dengan docker-compose.yml.
  2. Buka terminal di direktori tersebut.
  3. Jalankan: docker-compose up -d
    • -d (detached mode) akan menjalankan container di background.
  4. Untuk menghentikan dan menghapus container beserta network-nya: docker-compose down

Selamat! Kamu baru saja menjalankan aplikasi multi-container pertamamu dengan Docker Compose. Mudah, bukan?

3. Struktur File Compose yang Terorganisir: Dev vs. Prod

💡 Tips Penting: Untuk proyek yang lebih kompleks, mendefinisikan semua konfigurasi dalam satu file docker-compose.yml bisa jadi kurang fleksibel. Konfigurasi untuk pengembangan (misalnya hot-reloading, debugging) seringkali berbeda dengan konfigurasi untuk produksi (misalnya resource limits, health checks).

Solusinya adalah menggunakan beberapa file Compose dan menggabungkannya. Docker Compose memungkinkan kamu melakukan ini dengan opsi -f atau --file.

Struktur File yang Disarankan:

my-app/
├── docker-compose.yml          # Konfigurasi dasar (shared across environments)
├── docker-compose.dev.yml      # Override untuk pengembangan
├── docker-compose.prod.yml     # Override untuk produksi
├── Dockerfile                  # Dockerfile untuk aplikasi web
└── app/                        # Kode aplikasi
    └── ...

Contoh docker-compose.yml (Dasar):

# docker-compose.yml
services:
  web:
    build: .
    environment:
      # Variabel lingkungan yang sama untuk dev dan prod
      NODE_ENV: production # Default ke production, bisa di-override di dev.yml
      DATABASE_URL: postgres://user:password@db:5432/mydatabase
    depends_on:
      - db

  db:
    image: postgres:13
    environment:
      POSTGRES_DB: mydatabase
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

Contoh docker-compose.dev.yml (Override untuk Pengembangan):

# docker-compose.dev.yml
services:
  web:
    ports:
      - "3000:3000" # Map port untuk akses lokal
    volumes:
      - .:/app # Bind mount kode lokal untuk hot-reloading
      - /app/node_modules # Hindari bind mount node_modules dari host (bisa konflik)
    environment:
      NODE_ENV: development # Override NODE_ENV
      DEBUG_MODE: "true" # Tambahkan variabel debug

Contoh docker-compose.prod.yml (Override untuk Produksi Ringan):

# docker-compose.prod.yml
services:
  web:
    ports:
      - "80:3000" # Map port 80 host ke port 3000 container
    deploy: # Konfigurasi deployment (untuk mode swarm, tapi bisa dipakai di Compose)
      resources:
        limits:
          cpus: "0.5" # Batasi penggunaan CPU
          memory: 512M # Batasi penggunaan memori
      restart_policy:
        condition: on-failure # Restart jika container gagal

  db:
    ports:
      - "5432:5432" # Optional: Jika ingin akses DB dari luar host Docker
    deploy:
      resources:
        limits:
          cpus: "0.25"
          memory: 256M

Cara Menggunakan Multiple Files:

Dengan pendekatan ini, file docker-compose.yml utama tetap bersih dengan konfigurasi yang umum, sementara konfigurasi spesifik lingkungan diisolasi di file terpisah. Ini membuat manajemen lebih mudah dan mengurangi risiko kesalahan.

4. Mengelola Data dengan Volumes

Data adalah aset paling berharga dari aplikasi kita. Dalam konteks container, data bisa hilang saat container dihapus. Untuk mengatasi ini, Docker menyediakan Volumes.

Ada dua jenis utama volumes yang sering digunakan:

  1. Bind Mounts:

    • Memetakan (mount) direktori atau file dari sistem host langsung ke dalam container.
    • Kapan digunakan? Ideal untuk pengembangan, di mana kamu ingin kode sumbermu di host langsung tercermin di container (misalnya untuk hot-reloading).
    • Contoh: - .:/app (memetakan direktori saat ini di host ke /app di container).
  2. Named Volumes:

    • Dikelola sepenuhnya oleh Docker, disimpan di bagian khusus dari sistem file host Docker (biasanya /var/lib/docker/volumes/).
    • Kapan digunakan? Sangat direkomendasikan untuk data persisten seperti database, cache, atau upload pengguna. Data ini akan tetap ada meskipun container yang menggunakannya dihapus atau dibuat ulang.
    • Contoh: db_data:/var/lib/postgresql/data (memetakan named volume db_data ke direktori data PostgreSQL di container).

🎯 Best Practice Volumes:

# Contoh untuk menghindari bind mount node_modules
services:
  web:
    # ...
    volumes:
      - .:/app
      - /app/node_modules # Ini akan membuat anonymous volume untuk node_modules

5. Jaringan Antar Layanan (Networking)

Secara default, Docker Compose membuat jaringan bridge khusus untuk aplikasi kamu. Semua services dalam file docker-compose.yml yang sama akan terhubung ke jaringan ini dan dapat berkomunikasi satu sama lain menggunakan nama service sebagai hostname.

Misalnya, dalam contoh Node.js + PostgreSQL kita: Aplikasi Node.js (service web) dapat mengakses database PostgreSQL (service db) menggunakan hostname db dan port 5432. Itulah mengapa DATABASE_URL kita adalah postgres://user:password@db:5432/mydatabase.

💡 Kapan perlu Custom Networks? Jika kamu memiliki beberapa aplikasi Docker Compose yang perlu berkomunikasi satu sama lain, atau kamu ingin mengisolasi beberapa services dalam satu aplikasi ke jaringan terpisah untuk keamanan, kamu bisa mendefinisikan custom networks.

# docker-compose.yml
services:
  web:
    # ...
    networks:
      - app_network # Terhubung ke 'app_network'

  db:
    # ...
    networks:
      - app_network # Terhubung ke 'app_network'
      - db_admin_network # Jika ada service admin DB terpisah

  adminer: # Contoh service admin DB
    image: adminer
    ports:
      - "8080:8080"
    networks:
      - db_admin_network # Hanya terhubung ke jaringan admin DB

networks:
  app_network:
    driver: bridge
  db_admin_network:
    driver: bridge

Dalam contoh di atas, web dan db berada di app_network, sementara adminer dan db berada di db_admin_network. adminer tidak bisa langsung mengakses web karena mereka tidak berbagi jaringan yang sama, meningkatkan isolasi.

6. Lingkungan Produksi Ringan dengan Docker Compose (Tips & Trik)

Meskipun Docker Compose lebih sering digunakan untuk pengembangan lokal, ia juga bisa menjadi solusi yang powerful untuk deployment produksi skala kecil atau menengah, atau untuk staging environment yang sederhana. Berikut adalah beberapa praktik terbaik untuk “produksi ringan”:

6.1. ✅ Health Checks

Masalah: Bagaimana kita tahu jika sebuah service sudah benar-benar siap dan berfungsi, bukan hanya “berjalan”? Misalnya, database butuh waktu untuk startup. Jika aplikasi backend mencoba terhubung terlalu cepat, ia bisa gagal.

Solusi: Gunakan healthcheck untuk memberi tahu Docker Compose kapan sebuah service benar-benar “sehat”.

services:
  web:
    # ...
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"] # Perintah cek kesehatan
      interval: 30s # Cek setiap 30 detik
      timeout: 10s # Timeout setelah 10 detik
      retries: 3 # Coba 3 kali sebelum dianggap gagal
      start_period: 20s # Beri waktu 20 detik untuk startup awal

  db:
    # ...
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydatabase"]
      interval: 5s
      timeout: 5s
      retries: 5

Dengan healthcheck, kamu bisa menambahkan depends_on: service: { condition: service_healthy } di service lain untuk memastikan sebuah service hanya dimulai setelah dependensinya sehat.

6.2. ✅ Restart Policies

Masalah: Apa yang terjadi jika container tiba-tiba crash? Kita tidak ingin aplikasi kita down selamanya.

Solusi: Gunakan restart_policy untuk menginstruksikan Docker bagaimana merespons ketika container berhenti.

services:
  web:
    # ...
    restart: unless-stopped # Restart selalu kecuali jika dihentikan secara manual
  db:
    # ...
    restart: always # Selalu restart, bahkan jika dihentikan secara manual (hati-hati!)

Pilihan restart:

6.3. ⚠️ Environment Variables & .env File

Masalah: Mengelola kredensial database, kunci API, atau konfigurasi sensitif lainnya langsung di docker-compose.yml adalah praktik yang buruk dan tidak aman.

Solusi: Gunakan .env file untuk menyimpan variabel lingkungan sensitif dan memuatnya ke Docker Compose.

  1. Buat file .env di root direktori proyekmu:
    # .env
    DB_USER=mysecureuser
    DB_PASSWORD=verystrongpassword
    API_KEY=your_api_key_here
  2. Modifikasi docker-compose.yml untuk menggunakan variabel dari .env:
    services:
      web:
        # ...
        environment:
          DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@db:5432/mydatabase
          API_KEY: ${API_KEY}
      db:
        # ...
        environment:
          POSTGRES_USER: ${DB_USER}
          POSTGRES_PASSWORD: ${DB_PASSWORD}
  3. PENTING: Tambahkan .env ke .gitignore agar tidak ter-commit ke repository!

6.4. 🎯 Resource Limits

Masalah: Container yang tidak terkontrol bisa mengonsumsi semua CPU atau memori host, menyebabkan masalah performa untuk container lain atau bahkan seluruh sistem.

Solusi: Batasi penggunaan sumber daya untuk setiap service di lingkungan produksi.

services:
  web:
    # ...
    deploy:
      resources:
        limits:
          cpus: "0.5" # Batasi hingga 50% dari 1 CPU core
          memory: 512M # Batasi hingga 512 MB RAM
        reservations: # Alokasikan minimal
          cpus: "0.25"
          memory: 256M

Ini membantu menjaga stabilitas host dan mencegah satu container “menguasai” semua sumber daya.

6.5. 📈 Logging

Secara default, Docker Compose mengumpulkan output stdout dan stderr dari semua container dan menampilkannya di terminal. Ini sangat berguna untuk debugging.

Untuk produksi, pertimbangkan untuk mengintegrasikan dengan sistem logging terpusat (seperti ELK Stack, Grafana Loki, atau layanan cloud). Docker Compose mendukung berbagai logging drivers yang bisa kamu konfigurasikan.

services:
  web:
    # ...
    logging:
      driver: "json-file" # Driver default
      options:
        max-size: "10m"
        max-file: "3"

Kesimpulan

Docker Compose adalah alat yang sangat kuat dan fleksibel untuk mendefinisikan, menjalankan, dan mengelola aplikasi multi-container. Dari menyederhanakan workflow pengembangan lokal hingga menjadi solusi deployment yang robust untuk produksi skala kecil, Docker Compose adalah skill wajib bagi setiap developer modern.

Dengan memahami dan menerapkan praktik terbaik yang telah kita bahas—mulai dari struktur file yang terorganisir, manajemen volume yang cerdas, konfigurasi jaringan yang tepat, hingga health checks dan resource limits—kamu bisa membangun dan menjalankan aplikasi Docker-mu dengan lebih efisien, aman, dan dapat diandalkan.

Ingat, meskipun Docker Compose sangat mumpuni, untuk aplikasi skala besar dengan kebutuhan orkestrasi yang kompleks (seperti auto-scaling, load balancing tingkat lanjut, self-healing otomatis), kamu mungkin perlu beralih ke orkestrator yang lebih canggih seperti Kubernetes. Namun, untuk banyak kasus, Docker Compose lebih dari cukup!

Selamat mencoba dan jangan ragu untuk bereksperimen dengan konfigurasi Compose-mu sendiri. Happy containerizing! 🐳

🔗 Baca Juga