Skip to content
← কন্টেইনার · প্রাথমিক · 10 মিনিট · 03 / 06 EN

Docker Compose

Multi-service লোকাল এনভায়রনমেন্ট, dependency ordering, networking, এবং যেসব প্যাটার্ন compose-কে আসলেই কাজের করে তোলে।

docker composenetworkingvolumesdepends_onenvironment

বাস্তব জীবনের উদাহরণ

একজন stage manager-এর call sheet: একটা ডকুমেন্ট যা বলে দেয় শো শুরুর আগে কাকে কখন কোথায় থাকতে হবে — orchestra pit-এ, actor backstage-এ, light প্রস্তুত। Docker Compose হলো আপনার সার্ভিসগুলোর call sheet: একটা file, একটা command, সব অংশ ঠিক order-এ চালু হয়।

গল্পে বুঝি

ফাতিমা আল-ফিহরি একটা নাটকের মঞ্চ ব্যবস্থাপক (stage manager)। শো শুরুর আগে তাকে একসঙ্গে অনেককে ঠিক জায়গায় আনতে হয় — মূল অভিনেতা ইবনে সিনা, বাদ্যযন্ত্রীর দলের প্রধান আল-খোয়ারিজমি, আর আলো নিয়ন্ত্রণের ক্রু। আগে সে প্রত্যেককে আলাদা করে ডেকে আনত: একজনকে ডাকে, সে আসে, তারপর পরেরজন — এলোমেলো, সময়সাপেক্ষ, আর একজন ভুল সময়ে ঢুকে পড়লেই পুরো দৃশ্য মাটি।

এখন ফাতিমার হাতে একটাই cue-sheet। সেই এক কাগজে লেখা আছে কে কে থাকবে, কে কার সাথে কথা বলবে, আর কে আগে ঢুকবে কে পরে — আলো তৈরি না হওয়া পর্যন্ত অভিনেতা মঞ্চে উঠবে না। শো শুরুর সময় ফাতিমা একবার “সবাই যার যার জায়গায়” বলে ডাক দেয়, গোটা কাস্ট সঠিক ক্রমে মঞ্চে চলে আসে। শো শেষে একটাই ডাক — “সবাই নামো” — আর সবাই একসঙ্গে বিদায়। কাউকে আলাদা করে ধরে আনা-নেওয়ার ঝামেলা নেই।

এই গল্পটাই আসলে Docker Compose। ওই একটা cue-sheet হলো একটা declarative Compose file — যেখানে আপনার multi-container অ্যাপের সব service (web, database, cache) একসঙ্গে লেখা থাকে। কে কার সাথে কথা বলবে সেই তারের সংযোগটাই Compose-এর network, আর “আলো আগে, অভিনেতা পরে” — এই ক্রম বজায় রাখাটাই depends_on। “সবাই জায়গায়” মানে docker compose up (সব container একসঙ্গে চালু), আর “সবাই নামো” মানে docker compose down (সব একসঙ্গে বন্ধ)। বাস্তবে ঠিক এভাবেই ডেভেলপাররা লোকাল মেশিনে API + Postgres + Redis একটামাত্র command দিয়ে দাঁড় করান এবং কাজ শেষে একসাথে থামান — প্রতিটা container হাতে হাতে আলাদা করে চালানোর বদলে।

Compose যে সমস্যা সমাধান করে

একটা আধুনিক অ্যাপ্লিকেশন লোকালি চালানো মানে সাধারণত চালু করা: আপনার API server, একটা database, একটা cache, একটা queue, হয়তো একটা worker process। এটা ম্যানুয়ালি করা মানে একাধিক টার্মিনাল উইন্ডো, ভঙ্গুর shell script, আর “works on my machine” ডিবাগিং।

Compose এই সবকিছু একটা declarative file-এ সংজ্ঞায়িত করে — docker-compose.yml — আর docker compose up দিয়ে সবকিছু চালু করে।

একটা সম্পূর্ণ উদাহরণ

# docker-compose.yml
services:
  api:
    build: . # build from local Dockerfile
    ports:
      - '3000:3000'
    environment:
      DATABASE_URL: postgres://app:secret@db:5432/mydb
      REDIS_URL: redis://redis:6379
      NODE_ENV: development
    volumes:
      - ./src:/app/src # mount source for hot reload
    depends_on:
      db:
        condition: service_healthy # wait for DB to be healthy, not just started
      redis:
        condition: service_started

  worker:
    build: .
    command: node dist/worker.js # override CMD from Dockerfile
    environment:
      DATABASE_URL: postgres://app:secret@db:5432/mydb
      REDIS_URL: redis://redis:6379
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: mydb
    volumes:
      - pgdata:/var/lib/postgresql/data # persist across restarts
      - ./migrations:/docker-entrypoint-initdb.d # run on first start
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U app -d mydb']
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    volumes:
      - redisdata:/data

volumes:
  pgdata:
  redisdata:
# Start everything
docker compose up

# Start in background
docker compose up -d

# View logs
docker compose logs -f api

# Run a one-off command (migrations)
docker compose run --rm api node dist/migrate.js

# Stop everything (keep volumes)
docker compose down

# Stop and remove volumes (reset state)
docker compose down -v

Networking

একটা Compose file-এর সব সার্ভিস একটা ডিফল্ট network শেয়ার করে। সার্ভিসগুলো একে অপরের কাছে সার্ভিস নাম দিয়ে পৌঁছায়:

services:
  api:
    environment:
      # Use service name 'db', not 'localhost' — they're on the same Docker network
      DATABASE_URL: postgres://app:secret@db:5432/mydb
      #                                    ^^
      #                                    service name
# Inspect the network
docker network ls
# NETWORK ID   NAME               DRIVER
# abc123       myproject_default  bridge

# From inside the api container, 'db' resolves to the postgres container's IP
docker compose exec api ping db
# PING db (172.20.0.3): 56 data bytes

Isolation-এর জন্য custom network:

services:
  api:
    networks:
      - frontend
      - backend

  db:
    networks:
      - backend # not exposed to frontend services

  nginx:
    networks:
      - frontend # not connected to backend

networks:
  frontend:
  backend:

depends_on আর Startup Order

depends_on startup order নিয়ন্ত্রণ করে কিন্তু readiness নয় — একটা container “started” হয়েও এখনও connection নিতে না-ও পারে। সঠিক ordering-এর জন্য health check ব্যবহার করুন:

services:
  api:
    depends_on:
      db:
        condition: service_healthy # wait until healthcheck passes
      redis:
        condition: service_started # just wait for container to start

  db:
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U postgres']
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 10s # grace period before failures count

Health check ছাড়া: আপনার অ্যাপ চালু হয়, postgres-এ connect করার চেষ্টা করে, postgres এখনও initialize হচ্ছে বলে ব্যর্থ হয়, আর ক্র্যাশ করে। Health check থাকলে: postgres healthy রিপোর্ট না করা পর্যন্ত api অপেক্ষা করে।

Environment Variable

Environment variable পাস করার তিনটা উপায়:

services:
  api:
    # Inline (fine for non-secrets)
    environment:
      NODE_ENV: development
      PORT: "3000"

    # From .env file (don't commit this file)
    env_file:
      - .env

    # Reference host environment
    environment:
      AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}    # from shell
      API_KEY: ${API_KEY:-default-value}          # with fallback
# .env file (gitignored)
DATABASE_URL=postgres://app:secret@db:5432/mydb
REDIS_URL=redis://redis:6379
JWT_SECRET=dev-secret-not-for-production

Compose প্রজেক্ট ডিরেক্টরির .env স্বয়ংক্রিয়ভাবে লোড করে। .env-এর variable-গুলো compose file-এ ${VAR} হিসেবে পাওয়া যায় — কিন্তু সেগুলো compose configuration-এর জন্য, আপনি সুনির্দিষ্টভাবে রেফারেন্স না করলে সেগুলো স্বয়ংক্রিয়ভাবে container-এ পাস হয় না।

Override File

Compose একাধিক file মার্জ করে — environment-specific config-এর জন্য এটা ব্যবহার করুন:

# docker-compose.yml (base — committed)
services:
  api:
    image: myapp:latest
    ports:
      - "3000:3000"

# docker-compose.dev.yml (development — committed)
services:
  api:
    build: .             # override: build locally instead of pull
    volumes:
      - ./src:/app/src   # hot reload
    environment:
      NODE_ENV: development

# docker-compose.override.yml (auto-loaded in dev — often gitignored)
# Docker Compose automatically merges this with docker-compose.yml
# Development (auto-merges docker-compose.override.yml)
docker compose up

# Production (explicit files)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# CI (explicit)
docker compose -f docker-compose.yml -f docker-compose.ci.yml up --abort-on-container-exit

কাজের প্যাটার্ন

অ্যাপ চালু করার আগে database migration চালান:

services:
  migrate:
    build: .
    command: node dist/migrate.js
    depends_on:
      db:
        condition: service_healthy
    restart: 'no' # run once, don't restart

  api:
    build: .
    depends_on:
      migrate:
        condition: service_completed_successfully
      db:
        condition: service_healthy

একটা সার্ভিস scale করুন:

docker compose up --scale worker=3
# Starts 3 worker containers, all pulling from the same queue

File পরিবর্তন লক্ষ্য করে rebuild করুন:

# Docker Compose Watch (v2.22+)
docker compose watch
services:
  api:
    build: .
    develop:
      watch:
        - action: sync # sync files without rebuild
          path: ./src
          target: /app/src
        - action: rebuild # rebuild on dependency changes
          path: package.json

Optional Service-এর জন্য Profiles

services:
  api:
    build: .

  db:
    image: postgres:16

  mailhog:
    image: mailhog/mailhog
    profiles: [dev] # only started when --profile dev is passed
    ports:
      - '8025:8025'

  adminer:
    image: adminer
    profiles: [dev, tools]
    ports:
      - '8080:8080'
# Start without optional dev tools
docker compose up

# Start with dev profile
docker compose --profile dev up

এটা ডিফল্ট compose startup-কে minimal রাখে আর একই সাথে optional সার্ভিস সহজে activate করতে দেয়।