Server

Tutorial Docker Compose - Deploy Aplikasi Web Multi-Container

Pertama kali saya coba deploy aplikasi web yang butuh PHP, Nginx, dan MySQL, saya langsung pusing. Install satu-satu di VPS, konfigurasi manual, belum lagi kalau mau pindah server. Proses yang makan waktu berjam-jam itu akhirnya selesai dalam hitungan menit setelah kenal Docker Compose.

Docker Compose itu tools buat define dan jalanin multiple container sekaligus pakai satu file YAML. Bayangin kamu punya aplikasi yang butuh web server, database, dan cache -- semuanya bisa di-setup dalam satu command: docker compose up. Nggak perlu install manual, nggak perlu pusing dependency conflict.

Tutorial ini bakal nge-cover cara pakai Docker Compose dari nol, sampai kamu bisa deploy aplikasi web production-ready. Cocok buat developer yang mau simplifikasi workflow deployment.

Apa itu Docker Compose dan Kenapa Kamu Butuh Ini?

Docker Compose adalah tool yang memungkinkan kamu menjalankan beberapa container Docker sekaligus menggunakan konfigurasi yang didefinisikan dalam satu file docker-compose.yml. Tanpa Compose, kamu harus jalankan perintah docker run berulang kali untuk setiap service, dan harus atur networking antar container secara manual.

Keunggulan utama Docker Compose:

  • Reproducible environment -- Tim kamu bisa punya environment yang identik, dari laptop developer sampai production server
  • Single command deployment -- docker compose up -d langsung jalanin semua service
  • Version control -- File docker-compose.yml bisa di-commit ke git, jadi perubahan infrastruktur ter-track
  • Easy teardown -- docker compose down berhentikan dan hapus semua container sekaligus

Buat yang belum familiar dengan Docker sama sekali, cek dulu tulisan saya tentang troubleshooting Nginx 502/504 -- di situ ada penjelasan soal containerization yang relate sama topik ini.

Instalasi Docker dan Docker Compose di Ubuntu

Sebelum mulai, pastikan Docker sudah terinstall. Di Ubuntu 24.04, prosesnya cukup straightforward:

# Update package index
sudo apt update

# Install dependencies
sudo apt install -y ca-certificates curl gnupg

# Add Docker GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add Docker repository
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker Engine + Compose plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verifikasi instalasi:

# Cek versi Docker
docker --version
# Docker version 27.x.x, build xxxxxxxx

# Cek versi Compose
docker compose version
# Docker Compose version v2.x.x

Supaya nggak perlu sudo setiap kali jalankan Docker, tambahkan user kamu ke group docker:

sudo usermod -aG docker $USER
# Logout dan login ulang supaya efektif

Anatomi File docker-compose.yml

File docker-compose.yml adalah jantung dari Docker Compose. Di sini kamu define semua service, network, dan volume yang dibutuhkan aplikasi. Berikut struktur dasarnya:

version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./app:/var/www/html
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php
    networks:
      - app-network

  php:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./app:/var/www/html
    networks:
      - app-network

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: myapp
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppass
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    networks:
      - app-network

volumes:
  db-data:

networks:
  app-network:
    driver: bridge

Penjelasan komponen-komponen di atas:

  • services -- Setiap service merepresentasikan satu container (web server, PHP, database, cache)
  • image -- Docker image yang digunakan. Bisa dari Docker Hub atau custom build
  • ports -- Mapping port container ke host (format: host:container)
  • volumes -- Mount file/folder dari host ke container, atau persistent storage
  • depends_on -- Urutan startup container (PHP harus jalan sebelum Nginx)
  • networks -- Container yang sama di network bisa saling komunikasi pakai nama service

Contoh Nyata: Deploy Aplikasi PHP + Nginx + MySQL

Mari kita bikin setup yang realistis. Misalnya kamu punya aplikasi PHP (bisa CodeIgniter, Laravel, atau plain PHP) yang butuh Nginx dan MySQL.

Struktur project:

myproject/
  docker-compose.yml
  Dockerfile
  nginx/
    default.conf
  app/
    index.php
    .env

Dockerfile untuk PHP-FPM:

FROM php:8.3-fpm-alpine

# Install ekstensi PHP yang dibutuhkan
RUN docker-php-ext-install pdo pdo_mysql mysqli opcache

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Set working directory
WORKDIR /var/www/html

# Install dependencies kalau ada composer.json
# RUN composer install --no-dev --optimize-autoloader

Konfigurasi Nginx (nginx/default.conf):

server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}

Kalau kamu pakai CodeIgniter 4, konfigurasi Nginx di atas sudah benar -- root-nya ke /public. Buat yang butuh setup Nginx lebih lengkap termasuk SSL dan load balancing, baca tulisan tentang Nginx Reverse Proxy setup.

docker-compose.yml final:

version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./app:/var/www/html
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php
    restart: unless-stopped
    networks:
      - app-network

  php:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./app:/var/www/html
    restart: unless-stopped
    networks:
      - app-network

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: myapp
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppass
    ports:
      - "3306:3306"
    volumes:
      - db-data:/var/lib/mysql
    restart: unless-stopped
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    networks:
      - app-network

volumes:
  db-data:

networks:
  app-network:
    driver: bridge

Perintah Docker Compose yang Wajib Dikuasai

Setelah file docker-compose.yml siap, kamu bisa mulai jalankan semua service:

# Jalankan semua container (background mode)
docker compose up -d

# Lihat status container
docker compose ps

# Lihat log semua container (real-time)
docker compose logs -f

# Lihat log satu container tertentu
docker compose logs -f web

# Masuk ke dalam container (shell)
docker compose exec php sh

# Jalankan artisan/migration di container PHP
docker compose exec php php artisan migrate

# Stop semua container
docker compose down

# Stop + hapus volume (database terhapus!)
docker compose down -v

# Rebuild image setelah ubah Dockerfile
docker compose up -d --build

Environment Variables dan Keamanan

Jangan pernah hardcode password atau API key langsung di docker-compose.yml. Gunakan file .env yang terpisah:

# .env file (jangan di-commit ke git!)
MYSQL_ROOT_PASSWORD=supersecretrootpass
MYSQL_DATABASE=myapp
MYSQL_USER=appuser
MYSQL_PASSWORD=strongpassword123
REDIS_PASSWORD=redispass

Lalu di docker-compose.yml, referensikan pakai variabel:

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}

Tambahkan .env ke .gitignore supaya nggak ke-commit. Untuk production, gunakan Docker secrets atau environment variable dari server.

Data Persistence dengan Volume

Salah satu kesalahan pemula yang paling sering: database hilang setelah docker compose down. Ini terjadi kalau kamu nggak define volume. Named volume akan persist data meski container dihapus:

volumes:
  db-data:
    driver: local
  redis-data:
    driver: local

Untuk development, kamu juga bisa bind mount folder lokal supaya perubahan file langsung ter-refleksi di container:

services:
  php:
    volumes:
      - ./app:/var/www/html  # Bind mount (sync realtime)
      - vendor-data:/var/www/html/vendor  # Named volume (persist)

Kalau kamu pakai macOS atau Windows, bind mount bisa lambat karena Docker harus sync file system. Pakai volume untuk folder vendor atau node_modules yang berisi banyak file kecil.

Networking antar Container

Container di network yang sama bisa saling komunikasi pakai nama service sebagai hostname. Misalnya dari container PHP, kamu bisa koneksi ke MySQL pakai:

// Di dalam container PHP
$pdo = new PDO('mysql:host=db;port=3306;dbname=myapp', 'apppuser', 'apppass');
// 'db' = nama service di docker-compose.yml

Nggak perlu pakai IP address atau localhost. Docker Compose otomatis bikin DNS internal yang resolve nama service ke IP container.

Docker Compose Profiles untuk Environment Berbeda

Seringkali kamu butuh konfigurasi berbeda untuk development dan production. Docker Compose profiles bisa handle ini:

services:
  php:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./app:/var/www/html
    profiles:
      - dev

  php-prod:
    build:
      context: .
      dockerfile: Dockerfile.prod
    profiles:
      - prod

  adminer:
    image: adminer
    ports:
      - "8080:8080"
    profiles:
      - dev

Jalankan untuk development:

docker compose --profile dev up -d

Container yang nggak punya profiles akan selalu dijalankan (seperti db, redis). Container dengan profiles hanya jalan kalau profile-nya diaktifkan.

Health Check dan Restart Policy

Untuk production, tambahkan health check supaya Docker tahu kapan container benar-benar siap menerima request:

services:
  db:
    image: mysql:8.0
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    restart: unless-stopped

  web:
    image: nginx:alpine
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/"]
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped

Dengan restart: unless-stopped, container otomatis restart kalau crash. Tapi container tetap stop kalau kamu jalankan docker compose down secara manual.

Gunakan depends_on dengan kondisi health check supaya Nginx nggak start sebelum MySQL benar-benar ready:

services:
  web:
    depends_on:
      db:
        condition: service_healthy
      php:
        condition: service_started

Tips Optimasi untuk Production

Beberapa hal yang sering dilupakan saat deploy Docker Compose ke production:

  • Multi-stage build -- Gunakan multi-stage Dockerfile supaya image size lebih kecil. Build dependencies di stage pertama, copy hasilnya ke stage kedua yang bersih
  • Resource limits -- Set memory dan CPU limit supaya satu container nggak makan semua resource
services:
  php:
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 256M
  • Log rotation -- Tanpa log rotation, file log bisa makan disk space sampai habis
services:
  web:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
  • Read-only filesystem -- Untuk container yang nggak perlu write ke disk, set read-only supaya lebih aman
services:
  web:
    read_only: true
    tmpfs:
      - /tmp

Troubleshooting Umum Docker Compose

Beberapa masalah yang sering muncul dan solusinya:

Port sudah dipakai:

# Cari proses yang pakai port 80
sudo lsof -i :80
# Kill prosesnya atau ganti port mapping di compose file

Container gagal start:

# Cek log error
docker compose logs nama-service

# Cek apakah image berhasil di-build
docker compose build --no-cache

Database connection refused:

# Pastikan MySQL sudah selesai startup
docker compose exec db mysqladmin ping -h localhost -u root -p

# Cek apakah container ada di network yang sama
docker network ls
docker network inspect nama-project_app-network

Kalau kamu sering kena masalah server saat deploy, cek juga tulisan tentang debug Nginx 502/504 yang sering muncul kalau PHP-FPM belum ready saat Nginx sudah menerima request.

Kesimpulan

Docker Compose mengubah cara kita deploy aplikasi web. Dari yang dulunya butuh berjam-jam setup manual, sekarang cukup satu file YAML dan satu command. Untuk developer yang kerja dengan stack PHP + Nginx + MySQL, Docker Compose adalah game changer yang bikin workflow deployment jauh lebih predictable dan reproducible.

Mulai dari setup sederhana di development, lalu tambahkan health check, resource limits, dan log rotation saat mau deploy ke production. Jangan lupa pakai .env file untuk rahasia dan commit docker-compose.yml ke version control.

Kamu udah coba Docker Compose di project sendiri? Atau masih lebih suka install manual di VPS? Share pengalaman kamu di kolom komentar!


You may also like


0 Comments


Leave a Reply

Comments with links or spam keywords will be rejected.
Scroll to Top