← กลับ
DockerDocker ComposeDevOps

Docker Compose ใช้จริงในโปรเจกต์ ทุก feature ที่ควรรู้

ข้าม Hello World ไป — รวม pattern ที่เจอจริงใน production: healthcheck, network, profiles, env_file, dependency พร้อมตัวอย่างเต็ม

2025-10-22อ่าน 6 นาทีใหม่

Compose ดียังไง

ถ้าโปรเจกต์มีหลาย service เช่น app + db + redis + worker — ตอน dev คนใหม่เข้ามาแค่ docker compose up ก็พร้อมทำงาน ไม่ต้องลง Postgres/Redis เอง ไม่ต้องสับสน version

ตัว Compose เกิดมาเพื่อแก้ปัญหา "พิมพ์ docker run แต่ละตัว 7 บรรทัด ทุกคนใน team จดเองไม่เคยเหมือนกัน"

ถ้ายังไม่ได้อ่าน Docker คืออะไร แนะนำให้อ่านก่อน

โครงสร้างไฟล์

docker-compose.yml ในรากของ project:

services:
  app:
    image: myapp:latest
    # หรือ
    # build: .
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

start ทั้งหมด:

docker compose up -d        # background
docker compose logs -f      # log รวมทุก service
docker compose down         # หยุดทุก service
docker compose down -v      # + ลบ volume (ระวัง!)

Network — service เรียกหากันยังไง

ใน Compose service เรียกกันด้วย ชื่อ service เป็น hostname

จาก app ติดต่อ db ใช้ db:5432 — Docker จัดการ DNS ภายในให้:

services:
  app:
    image: myapp
    environment:
      DATABASE_URL: postgresql://postgres:secret@db:5432/myapp
                                                  ^^
                                                  hostname = ชื่อ service

  db:
    image: postgres:16-alpine

ไม่ต้อง expose port 5432 ออก host ก็ได้ — service ใน Compose คุยกันได้เพราะอยู่ network เดียวกัน

ถ้าจะเชื่อมจาก host (เช่น เปิด pgAdmin บนเครื่อง) ต้อง expose:

db:
  ports:
    - "5432:5432"

Volumes — เก็บข้อมูลให้คงอยู่

ทุก container ที่หยุด/ลบ — ข้อมูลในนั้นหายหมด ต้องใช้ volume เก็บข้อมูล

Named volume (Docker จัดการที่อยู่ให้):

services:
  db:
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

Bind mount (ระบุ path บน host):

services:
  app:
    volumes:
      - ./app:/usr/src/app                # แชร์ source code (สำหรับ dev)
      - /usr/src/app/node_modules         # อย่าให้ host มี node_modules ทับ
      - ./logs:/var/log/myapp:ro          # read-only

bind mount ใช้กับ dev เพื่อให้ container เห็นโค้ดเปลี่ยนทันที (hot reload) named volume ใช้กับ data ของ database/cache

Healthcheck — รู้ว่า service ใช้ได้จริง

Container "started" ไม่ได้หมายความว่าใช้งานได้ — Postgres ต้องใช้เวลา 5-10 วินาทีหลัง start ถึงจะรับ connection

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
      start_period: 10s

  app:
    image: myapp
    depends_on:
      db:
        condition: service_healthy   # รอ db พร้อมจริงๆ ก่อน

depends_on แบบเดิม (- db) แค่รอให้ container db start ไม่ได้รอให้ Postgres พร้อมรับ query

Environment ที่จัดการได้สะอาด

แทนที่ฝัง env ใน yml ใช้ไฟล์ .env แยก:

# .env (ไม่ commit เข้า git)
POSTGRES_PASSWORD=verysecret
JWT_SECRET=randomstring1234
STRIPE_KEY=sk_live_...
services:
  app:
    image: myapp
    env_file:
      - .env

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

Compose จะ load .env อัตโนมัติ ใช้ ${VAR} ใน yml ได้เลย

อ่านเพิ่ม: จัดการ Environment Variables

Profiles — เปิด service บางตัวเฉพาะที่ต้องการ

dev อยากรัน admin tool บางครั้ง production ไม่ต้องการ:

services:
  app:
    image: myapp

  db:
    image: postgres

  pgadmin:
    image: dpage/pgadmin4
    profiles: [debug]   # default ไม่ start

  redis-commander:
    image: rediscommander/redis-commander
    profiles: [debug]
docker compose up -d                            # start แค่ app + db
docker compose --profile debug up -d            # + admin tools

Restart Policy

services:
  app:
    restart: unless-stopped

ตัวเลือก:

  • no — default, ไม่ restart
  • always — restart เสมอ (แม้คุณ stop เอง)
  • unless-stopped — restart ยกเว้นหยุดเอง — แนะนำสำหรับ production
  • on-failure[:5] — restart เฉพาะตอน exit code != 0, สูงสุด 5 ครั้ง

Resource Limits

จำกัดไม่ให้ service ตัวหนึ่งกินทรัพยากรเครื่อง:

services:
  worker:
    image: myworker
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 128M

ใน plain compose deploy ส่วนใหญ่ใช้กับ Swarm — แต่ตัว cpus กับ memory ใน limits ก็ใช้ได้กับ docker compose up

Logging

ตั้ง log driver ไม่ให้ log โต disk เต็ม:

services:
  app:
    image: myapp
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

จำกัดที่ 50MB × 3 ไฟล์ = สูงสุด 150MB ต่อ service

ตัวอย่างเต็ม — Web app + Postgres + Redis + Worker

services:
  app:
    build: .
    image: myapp:latest
    ports:
      - "3000:3000"
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

  worker:
    image: myapp:latest
    command: ["node", "worker.js"]
    env_file: .env
    depends_on:
      redis:
        condition: service_started
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    volumes:
      - redisdata:/data
    restart: unless-stopped

  pgadmin:
    image: dpage/pgadmin4
    environment:
      PGADMIN_DEFAULT_EMAIL: admin@local
      PGADMIN_DEFAULT_PASSWORD: admin
    ports:
      - "5050:80"
    profiles: [debug]

volumes:
  pgdata:
  redisdata:

Override สำหรับ dev / production

วิธีปฏิบัติทั่วไป: docker-compose.yml เป็น base, มี docker-compose.override.yml (โหลดอัตโนมัติ) สำหรับ dev เพิ่มของ + docker-compose.prod.yml สำหรับ production

docker-compose.override.yml (dev):

services:
  app:
    build: .
    volumes:
      - ./app:/usr/src/app
      - /usr/src/app/node_modules
    environment:
      NODE_ENV: development

docker-compose.prod.yml:

services:
  app:
    image: ghcr.io/me/myapp:1.2.0
    restart: unless-stopped
# dev (โหลด override อัตโนมัติ)
docker compose up -d

# production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

คำสั่งที่ใช้บ่อย

docker compose up -d                    # start background
docker compose down                     # stop + remove containers
docker compose ps                       # ดู status
docker compose logs -f app              # ตาม log ของ service เดียว
docker compose exec app sh              # เข้า container
docker compose restart app              # restart service เดียว
docker compose pull                     # pull image ใหม่
docker compose build --no-cache         # build ใหม่ไม่ใช้ cache
docker compose run --rm app npm test    # รัน command ครั้งเดียวแล้วลบ container

ปัญหาที่เจอบ่อย

Cannot connect to db:5432 — ตรวจสอบว่า service ชื่อตรงกัน + ใช้ healthcheck ใน depends_on

Volume permission denied — user ใน container ไม่มีสิทธิเขียน volume ที่ mount มา แก้:

services:
  app:
    user: "${UID}:${GID}"

ข้อมูล db หายหลัง down — ใช้ named volume ไม่ใช่ bind mount; อย่าใช้ down -v ใน production

Hot reload ไม่ทำงาน — เพิ่ม CHOKIDAR_USEPOLLING=true ใน environment สำหรับ Node app บน macOS/Windows

สรุป

Docker Compose คือ tool ที่ทุกคนใช้ตอน dev และทีมเล็กๆ ใช้ใน production บน VPS เครื่องเดียวก็ได้

โปรเจกต์ใหญ่ขึ้นค่อยดูเรื่อง Kubernetes แต่ตอนเริ่ม Compose ก็เพียงพอและตั้งง่ายมาก

ลอง spin up stack ที่มี database + cache + queue ภายใน 5 นาที — เริ่มเขียน code ได้ทันที

← ดูบทความอื่น