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, ไม่ restartalways— restart เสมอ (แม้คุณ stop เอง)unless-stopped— restart ยกเว้นหยุดเอง — แนะนำสำหรับ productionon-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 ได้ทันที