Aplikasi Node.js di produksi jarang berdiri sendirian. Aplikasi tugas kita butuh database; mungkin juga proxy terbalik di depannya, dan pekerja latar belakang di belakangnya. Menjalankan tiap komponen dengan docker run terpisah bisa saja dilakukan, tetapi daftar flagnya panjang dan harus diingat urutannya: buat jaringan dulu, nyalakan database tunggu sampai siap, baru nyalakan aplikasi dengan alamat yang benar. Docker Compose memindahkan seluruh pengaturan itu ke satu berkas YAML — lalu satu perintah menyalakan semuanya dalam urutan yang benar. Pada chapter ini kita susun aplikasi plus database dalam satu berkas deklaratif, pelajari bagaimana container saling menemukan, dan lihat siklus hidup perintahnya.

Apa yang Dikelola Compose #

Bandingkan dua cara menjalankan pasangan aplikasi-database:

Aspekdocker run manualDocker Compose
Definisitersebar di riwayat shellsatu berkas docker-compose.yml di repo
Jaringan antar-containerdibuat dan disambungkan manualsatu jaringan default untuk semua service
Urutan penyalaankita menunggu manualdepends_on dengan kondisi kesehatan
Data databaseflag -v yang harus konsistenvolume bernama dideklarasikan sekali
Menyalakan semuanyaulangi seluruh perintahdocker compose up -d

Selain keringkasan, perbedaan terbesarnya bersifat sosial: berkas compose terbaca seperti dokumentasi. Rekan tim yang membuka repo langsung tahu komponen apa saja yang dibutuhkan aplikasi dan bagaimana keduanya tersambung.

Berkas docker-compose.yml #

Aplikasi contoh kita memakai PostgreSQL — driver dan pola koneksinya sudah dibahas di chapter Koneksi PostgreSQL dengan pg. Berkas berikut mendefinisikan keduanya:

yaml docker-compose.yml
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      PORT: 3000
      DATABASE_URL: postgres://tugas:rahasia-contoh@db:5432/tugas
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: tugas
      POSTGRES_PASSWORD: rahasia-contoh
      POSTGRES_DB: tugas
    volumes:
      - data-db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U tugas"]
      interval: 5s
      retries: 5
      start_period: 5s

volumes:
  data-db:

Mari kita bedah bagian-bagiannya:

  1. services: setiap kunci di bawahnya adalah satu service — unit yang akan dijalankan sebagai container. Di sini ada app dan db; nama inilah yang nanti dipakai sebagai alamat jaringan.
  2. build: .: service app dibangun dari Dockerfile di folder yang sama — persis Dockerfile Chapter Docker untuk Node.js. Service db memakai image saja karena tidak perlu dibangun; PostgreSQL resmi tinggal dipakai.
  3. ports: memetakan port komputer ke port container, sama seperti flag -p — hanya service yang perlu diakses dari luar yang memetakan port; database cukup hidup di jaringan internal.
  4. environment: menyuntikkan environment variable ke container. Perhatikan host pada DATABASE_URL: namanya db, bukan localhost — penjelasannya di bagian berikutnya.
  5. depends_on dengan condition: service_healthy: aplikasi menunggu sampai database dinyatakan sehat, bukan sekadar menyala. Tanpa kondisi ini, app bisa terbangun saat db baru berjalan tetapi belum siap menerima koneksi — aplikasi kita sendiri yang harus mengulang koneksi berkali-kali.
  6. volumes: data-db:...: data PostgreSQL disimpan pada volume bernama data-db sehingga data bertahan ketika container database dibuat ulang. Deklarasi volumes: di akar berkas mendaftarkan volume tersebut.
  7. healthcheck: cara Compose menilai “sehat” untuk service db — perintah pg_isready diperiksa setiap interval, boleh gagal sebanyak retries, dengan start_period sebagai masa penyesuaian awal.
  8. restart: unless-stopped: kebijakan penyalaan ulang — container yang berhenti karena galat dihidupkan kembali, tetapi tidak dipaksa bila memang dihentikan secara sengaja; perilaku yang sejalan dengan semangat process manager pada Chapter Process Manager dengan PM2.

Container Saling Menemukan Lewat Nama Service #

Semua service dalam satu berkas compose otomatis bergabung ke satu jaringan yang sama, dan setiap container dapat ditemukan oleh container lain memakai nama servicenya. Itulah alamat DATABASE_URL di atas menunjuk ke host db: dari dalam jaringan compose, nama tersebut dirujuk ke container database — sedangkan localhost di dalam container app berarti container app itu sendiri, tempat tidak ada PostgreSQL. Perilaku jaringan default ini dijelaskan pada panduan jaringan Docker Compose , dan seluruh kunci berkas compose — termasuk env_file, depends_on, dan healthcheck — terdokumentasi di referensi berkas compose .

Siklus Hidup Perintah #

Empat perintah menutup siklus hidup seluruh stack:

bash Mengelola seluruh stack dengan satu perintah
docker compose up -d      # bangun image bila perlu, lalu nyalakan semua service
docker compose ps         # status seluruh service
docker compose logs -f app  # ikuti log aplikasi
docker compose down       # hentikan dan hapus seluruh container serta jaringan

Perintah up -d membangun image app bila belum ada atau bila Dockerfile berubah, membuat jaringan, menyalakan db, menunggu healthcheck-nya lulus, baru menyalakan app — seluruh urutan yang tadinya kita kerjakan manual. Opsi -f pada logs mengikuti aliran log secara langsung; tambahkan nama service untuk memfilter salah satunya. Perintah down membersihkan semuanya, tetapi volume data-db sengaja dipertahankan agar data bertahan — tambahkan flag -v pada down bila kita memang ingin menghapus datanya juga.

Kapan Compose Cukup #

Compose bersinar di tiga tempat. Pertama, lingkungan pengembangan: satu perintah menghadirkan aplikasi beserta database tanpa memasang apa pun di komputer selain Docker. Kedua, server kecil hingga menengah: satu berkas yang mudah dibaca seringkali lebih sehat daripada orkestrasi yang berlebihan. Ketiga, pipeline CI — bab berikutnya akan menunjukkan pipeline GitHub Actions yang memakai Compose untuk menyediakan database uji. Untuk klaster besar lintas mesin, kebutuhannya bergeser ke orchestrator seperti Kubernetes; batas praktisnya bukan angka container, melainkan saat kita mulai menjalankan banyak mesin.

Pengelolaan proses, pengemasan, dan orkestrasi kini lengkap. Dua chapter berikutnya menutup kebutuhan operasional yang tersisa: membuat aplikasi yang bisa dipantau (logging dan metrik) dan aplikasi yang berhenti dengan sopan (graceful shutdown).