Docker Compose DevOps Containers

Docker Compose — จัดการหลาย Containers ด้วยไฟล์เดียว 🐳

By Anirach Mingkhwan DevOps & Vibe Coding 2026
Docker Compose — captain dog orchestrating containers

ในโพสต์ Docker vs VMs เราเรียนรู้ว่า Docker สร้าง container ได้ง่าย แต่ app จริงมักใช้หลาย services — web server, API, database, cache, queue...

ถ้าต้อง docker run ทีละตัว ตั้ง network เอง ตั้ง volume เอง... จะบ้าตาย! 😵

Docker Compose แก้ปัญหานี้ — กำหนดทุก service ในไฟล์ YAML เดียว แล้วรันด้วยคำสั่งเดียว! 🐳🐕


Docker Compose คืออะไร?

Docker Compose คือเครื่องมือที่ช่วย กำหนด + รันหลาย containers พร้อมกัน ด้วยไฟล์ docker-compose.yml (หรือ compose.yml)

📝 เขียน YAML
กำหนด services
🚀 docker compose up
รันทุก service
✅ ทำงานร่วมกัน!
network + volumes พร้อม
💡 จำง่ายๆ: Dockerfile = สูตรทำ 1 container | Docker Compose = สูตรทำทั้งร้านอาหาร (หลาย containers)

ตัวอย่างแรก — Web App + Database

มาเริ่มจากตัวอย่างง่ายที่สุด — web app + PostgreSQL database แค่ 2 services แต่ถ้าไม่มี Compose ต้อง: สร้าง network เอง, รัน postgres container ตั้ง password + volume, รัน app container ตั้ง env vars + เชื่อม network... รวม 5-6 คำสั่ง! Docker Compose ทำให้เหลือแค่ docker compose up:

# docker-compose.yml
services:
  # Web Application
  app:
    build: .                     # build จาก Dockerfile ใน directory นี้
    ports:
      - "3000:3000"              # host:container
    environment:
      - DATABASE_URL=postgres://myuser:mypass@db:5432/mydb
      - NODE_ENV=development
    volumes:
      - .:/app                   # mount code เข้าไป (dev mode)
      - /app/node_modules        # ไม่ mount node_modules
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

  # PostgreSQL Database
  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=myuser
      - POSTGRES_PASSWORD=mypass
      - POSTGRES_DB=mydb
    volumes:
      - pgdata:/var/lib/postgresql/data    # persist data
    ports:
      - "5432:5432"
    healthcheck:
      test: pg_isready -U myuser
      interval: 10s
      retries: 5

volumes:
  pgdata:                        # named volume สำหรับ DB data
# รันทุก service
docker compose up -d
# [+] Running 3/3
#  ✔ Network myapp_default  Created
#  ✔ Container myapp-db-1   Started
#  ✔ Container myapp-app-1  Started

# ดูสถานะ
docker compose ps
# NAME           SERVICE   STATUS    PORTS
# myapp-app-1    app       running   0.0.0.0:3000->3000/tcp
# myapp-db-1     db        running   0.0.0.0:5432->5432/tcp

# ดู logs
docker compose logs -f app      # follow logs ของ app
docker compose logs              # logs ทุก service

# หยุดทุก service
docker compose down              # หยุด + ลบ containers
docker compose down -v           # + ลบ volumes ด้วย (⚠️ data หาย!)

Full Stack — React + API + DB + Redis + Nginx

ตัวอย่างจริงที่ใกล้ production — 5 services ทำงานร่วมกัน: React frontend, Node.js API, PostgreSQL database, Redis cache, และ Nginx reverse proxy สังเกตการใช้ depends_on กำหนดลำดับการ start, healthcheck ตรวจว่า service พร้อมจริงก่อน start ตัวที่พึ่ง, volumes สำหรับ persistent data, และ networks แยก frontend/backend traffic:

Docker Compose Stack
Nginx
:80 → frontend
/api → backend
↓ reverse proxy ↓
Frontend
React :3000
Backend
Node.js :8080
PostgreSQL
:5432
Redis
:6379
# docker-compose.yml — Full Stack
services:
  # ─── Nginx Reverse Proxy ───
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - frontend
      - backend
    restart: always

  # ─── React Frontend ───
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    environment:
      - REACT_APP_API_URL=/api
    expose:
      - "3000"                   # expose ให้ nginx เห็น (ไม่เปิด host)

  # ─── Node.js Backend ───
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    environment:
      - DATABASE_URL=postgres://app:secret@db:5432/appdb
      - REDIS_URL=redis://redis:6379
      - JWT_SECRET=${JWT_SECRET}  # อ่านจาก .env file
    expose:
      - "8080"
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped

  # ─── PostgreSQL ───
  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=app
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=appdb
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: pg_isready -U app
      interval: 10s
      retries: 5
    restart: unless-stopped

  # ─── Redis Cache ───
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redisdata:/data
    restart: unless-stopped

volumes:
  pgdata:
  redisdata:

Nginx Config

Nginx ทำหน้าที่เป็น reverse proxy — รับ traffic ทั้งหมดแล้วส่งต่อไปยัง frontend หรือ API ตาม URL path ข้อดีคือ expose port เดียว (80) แทนที่ต้องเปิดหลาย ports และสามารถ cache, gzip, load balance ได้:

# nginx/default.conf
upstream frontend {
    server frontend:3000;
}

upstream backend {
    server backend:8080;
}

server {
    listen 80;

    # Frontend
    location / {
        proxy_pass http://frontend;
        proxy_set_header Host $host;
    }

    # API → Backend
    location /api/ {
        proxy_pass http://backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # WebSocket support
    location /ws {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

🔧 Docker Compose คำสั่งสำคัญ

คำสั่ง Docker Compose ที่ใช้บ่อยในชีวิตประจำวัน — ตั้งแต่ start/stop services, ดู logs, exec เข้าไปใน container, ไปจนถึง rebuild images หลังแก้ Dockerfile สังเกตว่า docker compose (ไม่มี hyphen) เป็น v2 ที่แนะนำ แทน docker-compose v1:

# ─── Lifecycle ───
docker compose up                # รัน (foreground)
docker compose up -d             # รัน (background) ⭐
docker compose down              # หยุด + ลบ containers
docker compose down -v           # + ลบ volumes
docker compose restart           # restart ทุก service
docker compose stop              # หยุด (ไม่ลบ)
docker compose start             # เริ่มจาก stop

# ─── Build ───
docker compose build             # build ทุก service
docker compose build --no-cache  # build ใหม่ ไม่ใช้ cache
docker compose up --build        # build แล้วรันเลย ⭐

# ─── Status & Logs ───
docker compose ps                # ดูสถานะ
docker compose logs              # ดู logs ทั้งหมด
docker compose logs -f backend   # follow logs ของ backend
docker compose top               # ดู processes ในแต่ละ container

# ─── Execute ───
docker compose exec backend sh   # เข้าไปใน container ⭐
docker compose exec db psql -U app appdb   # เข้า PostgreSQL
docker compose run --rm backend npm test   # รันแล้วลบ container

# ─── Scale ───
docker compose up -d --scale backend=3   # รัน backend 3 ตัว!

📁 .env File — แยก Config ออกจาก YAML

.env file ช่วยแยก sensitive config (passwords, API keys) ออกจาก docker-compose.yml — ทำให้สามารถ commit compose file เข้า Git ได้โดยไม่ leak secrets เพราะ .env อยู่ใน .gitignore Docker Compose อ่าน .env อัตโนมัติ:

# .env (อยู่ข้างๆ docker-compose.yml)
POSTGRES_USER=app
POSTGRES_PASSWORD=supersecret
POSTGRES_DB=myapp
JWT_SECRET=my-jwt-secret-key
NODE_ENV=development
APP_PORT=3000
# docker-compose.yml — ใช้ตัวแปรจาก .env
services:
  backend:
    build: ./backend
    ports:
      - "${APP_PORT}:${APP_PORT}"
    environment:
      - DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
      - JWT_SECRET=${JWT_SECRET}
      - NODE_ENV=${NODE_ENV}
      - PORT=${APP_PORT}

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
🐕 สำคัญ! อย่าลืมใส่ .env ใน .gitignore — ไม่ commit secrets เข้า Git!

🔀 Multi-Environment — Dev vs Production

Dev environment ต้องการ hot-reload, debug tools, volume mounts สำหรับ live coding ส่วน production ต้อง optimize image size, set resource limits, ปิด debug mode จะจัดการ config ต่างๆ ยังไงให้ไม่ต้อง maintain 2 ไฟล์ที่ซ้ำกัน? Docker Compose มี 2 วิธีหลัก:

วิธีที่ 1: Override Files

Override files คือการแยก base config (docker-compose.yml) กับ environment-specific config (docker-compose.override.yml หรือ docker-compose.prod.yml) Docker Compose จะ merge ทั้ง 2 ไฟล์เข้าด้วยกัน — base กำหนด services ทั้งหมด, override เพิ่ม/แก้เฉพาะค่าที่ต่างกัน:

# docker-compose.yml (base — shared config)
services:
  backend:
    build: ./backend
    environment:
      - DATABASE_URL=postgres://app:pass@db:5432/mydb
    depends_on:
      - db
  db:
    image: postgres:16-alpine

# docker-compose.override.yml (dev — auto-loaded!)
services:
  backend:
    volumes:
      - ./backend:/app           # hot reload
    ports:
      - "8080:8080"
      - "9229:9229"              # debug port
    environment:
      - NODE_ENV=development
    command: npm run dev

# docker-compose.prod.yml (production)
services:
  backend:
    restart: always
    environment:
      - NODE_ENV=production
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'
# Development (auto-loads override)
docker compose up

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

วิธีที่ 2: Profiles

Profiles (Docker Compose v2) ช่วยให้ group services ตาม use case — เช่น debug tools, monitoring stack, seeding scripts ที่ไม่ต้องรันทุกครั้ง แค่ docker compose --profile debug up จะ start เฉพาะ services ที่ tag ด้วย profile "debug":

services:
  backend:
    build: ./backend
    # ไม่มี profile = รันเสมอ

  db:
    image: postgres:16-alpine
    # ไม่มี profile = รันเสมอ

  # Dev tools — รันเฉพาะ dev
  adminer:
    image: adminer
    ports:
      - "8888:8080"
    profiles:
      - dev                      # รันเมื่อ --profile dev

  mailhog:
    image: mailhog/mailhog
    ports:
      - "8025:8025"
    profiles:
      - dev

  # Monitoring — รันเฉพาะ monitoring
  prometheus:
    image: prom/prometheus
    profiles:
      - monitoring
# รันแค่ base services
docker compose up -d

# รัน + dev tools
docker compose --profile dev up -d

# รัน + monitoring
docker compose --profile monitoring up -d

# รันทั้งหมด
docker compose --profile dev --profile monitoring up -d

💾 Volumes — เก็บข้อมูลถาวร

โดย default เมื่อ container หยุด ข้อมูลข้างในหายหมด! Volumes แก้ปัญหานี้โดยเก็บข้อมูลไว้นอก container — database data, uploaded files, logs จะไม่หายแม้ docker compose down มี 2 แบบ: named volumes (Docker จัดการ path ให้ เหมาะกับ data) และ bind mounts (mount directory จาก host เข้า container เหมาะกับ development):

services:
  db:
    image: postgres:16-alpine
    volumes:
      # Named volume — Docker จัดการให้ (production)
      - pgdata:/var/lib/postgresql/data

      # Bind mount — map folder จาก host (dev)
      - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql

  backend:
    build: ./backend
    volumes:
      # Bind mount — hot reload สำหรับ dev
      - ./backend/src:/app/src

      # Anonymous volume — ป้องกัน overwrite
      - /app/node_modules

volumes:
  pgdata:                        # ประกาศ named volume
    driver: local
# ดู volumes
docker volume ls

# ลบ volumes ที่ไม่ใช้
docker volume prune

# Backup database
docker compose exec db pg_dump -U app mydb > backup.sql

# Restore database
cat backup.sql | docker compose exec -T db psql -U app mydb

🌐 Networks — Container คุยกันยังไง

Docker Compose สร้าง default network ให้ทุก service อัตโนมัติ — containers สื่อสารกันโดยใช้ service name เป็น hostname เช่น API container เรียก database ด้วย postgres://db:5432 ไม่ต้องใช้ IP address สำหรับ security ที่ดีขึ้น ควรสร้าง networks แยก — frontend network เฉพาะ web+nginx, backend network เฉพาะ API+DB ทำให้ frontend เข้าถึง DB โดยตรงไม่ได้:

services:
  frontend:
    networks:
      - frontend-net             # เข้าถึงได้จาก nginx

  backend:
    networks:
      - frontend-net             # nginx เข้าถึงได้
      - backend-net              # เข้าถึง db ได้

  db:
    networks:
      - backend-net              # เฉพาะ backend เข้าถึง ✅
                                 # frontend เข้าไม่ได้ ✅ (security!)

networks:
  frontend-net:
  backend-net:
💡 Security Tip: แยก network เพื่อ จำกัดการเข้าถึง — frontend ไม่ควรเข้า DB ตรงได้!

❤️ Health Checks — ตรวจสุขภาพ

Health checks ตรวจว่า service ทำงานจริง ไม่ใช่แค่ "process ยังรันอยู่" — เช่น PostgreSQL container อาจ start แล้วแต่ยังรับ connection ไม่ได้ ถ้า API start ก่อน DB พร้อม จะ crash! depends_on + condition: service_healthy แก้ปัญหานี้โดย wait จน health check ผ่านก่อนค่อย start service ที่พึ่ง:

services:
  backend:
    build: ./backend
    healthcheck:
      test: curl -f http://localhost:8080/health || exit 1
      interval: 30s              # ตรวจทุก 30 วินาที
      timeout: 10s               # timeout 10 วินาที
      retries: 3                 # ลองใหม่ 3 ครั้ง
      start_period: 40s          # รอ 40 วินาทีก่อนเริ่มตรวจ
    depends_on:
      db:
        condition: service_healthy   # รอจน db healthy ก่อน!

  db:
    image: postgres:16-alpine
    healthcheck:
      test: pg_isready -U app
      interval: 10s
      retries: 5

  redis:
    image: redis:7-alpine
    healthcheck:
      test: redis-cli ping
      interval: 10s
      retries: 3

🚀 Production Tips

Docker Compose ใช้ใน production ได้สำหรับ small-medium apps (ถ้ายังไม่ต้องการ Kubernetes) แต่ต้องตั้งค่าให้ production-ready — restart policies, resource limits, logging, security hardening เคล็ดลับสำคัญ:

# docker-compose.prod.yml
services:
  backend:
    image: myregistry.com/myapp:v2.1.0   # ใช้ image จาก registry (ไม่ build)
    restart: always
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'
        reservations:
          memory: 256M
    logging:
      driver: json-file
      options:
        max-size: "10m"          # จำกัดขนาด log file
        max-file: "3"            # เก็บแค่ 3 ไฟล์
    read_only: true              # filesystem read-only (security)
    tmpfs:
      - /tmp                     # ให้เขียนได้แค่ /tmp
    security_opt:
      - no-new-privileges:true

Docker Compose vs Kubernetes

คำถามที่พบบ่อย — "ควรใช้ Docker Compose หรือ Kubernetes?" คำตอบง่ายๆ: Compose สำหรับ single-host (1 server, dev environment, small apps), Kubernetes สำหรับ multi-host (หลาย servers, auto-scaling, high availability, large teams) เริ่มด้วย Compose แล้วย้ายไป K8s เมื่อ scale จนจำเป็น:

FeatureDocker ComposeKubernetes
เหมาะกับDev, small productionLarge-scale production
เครื่องเครื่องเดียวหลายเครื่อง (cluster)
Auto-scaling❌ Manual scale✅ HPA auto-scale
Self-healing⚠️ restart policy✅ Full self-healing
Rolling update⚠️ Basic✅ Zero-downtime
ความซับซ้อนง่าย (1 YAML file)ซับซ้อน (many manifests)
Learning curve30 นาที30 วัน+
💡 กฎง่ายๆ: เครื่องเดียว + ทีมเล็ก = Docker Compose | หลายเครื่อง + scale = Kubernetes

สรุป

Docker Compose เป็นเครื่องมือที่ทุก developer ควรรู้ — ทำให้การ setup development environment เหลือแค่ docker compose up ไม่ต้อง install dependencies บนเครื่อง ทุกคนในทีมได้ environment เดียวกัน "works on my machine" หมดไป:

บทความจากซีรีส์ DevOps & Vibe Coding 2026
← Previous
Software Testing — ทดสอบยังไงให้มั่นใจก่อน Deploy
Next →
GitOps & ArgoCD — Git-driven Deployments