ในการพัฒนา Application สมัยใหม่ แทบจะเป็นไปไม่ได้ที่จะรันแค่ Container เดียว Application จริงๆ ประกอบด้วยหลาย Service เช่น Web Server, API, Database, Cache, Message Queue และอีกมากมาย การจัดการ Container หลายตัวด้วยคำสั่ง docker run ทีละตัวนั้นยุ่งยากและผิดพลาดง่าย Docker Compose จึงเป็นเครื่องมือที่ขาดไม่ได้สำหรับนักพัฒนาทุกคน
บทความนี้จะสอน Docker Compose ตั้งแต่พื้นฐานจนถึงเทคนิคขั้นสูง ครอบคลุมทุกหัวข้อที่คุณต้องรู้เพื่อจัดการ Multi-Container Application ทั้งใน Development และ Production ในปี 2026
Docker Compose คืออะไร?
Docker Compose คือเครื่องมือสำหรับกำหนดและรัน Multi-Container Docker Application ด้วยไฟล์ YAML เพียงไฟล์เดียว คุณเขียนทุก Service, Network, Volume ที่ต้องการในไฟล์ compose.yml แล้วใช้คำสั่งเดียว docker compose up ก็สามารถรันทุกอย่างได้พร้อมกัน
Docker Compose ถูกสร้างขึ้นเพื่อแก้ปัญหาหลักสามข้อ ข้อแรกคือการจัดการหลาย Container พร้อมกันโดยไม่ต้องรันคำสั่งทีละตัว ข้อสองคือการตั้งค่า Networking ระหว่าง Container ให้คุยกันได้อัตโนมัติ และข้อสามคือการจัดการ Data Persistence ผ่าน Volumes ที่กำหนดไว้ในที่เดียว
Docker Compose vs Dockerfile vs Kubernetes
| เครื่องมือ | หน้าที่ | ใช้เมื่อ |
|---|---|---|
| Dockerfile | สร้าง Image สำหรับ 1 Container | ต้องการ Custom Image |
| Docker Compose | จัดการหลาย Container บน 1 เครื่อง | Development, Small Production |
| Kubernetes | จัดการ Container ข้ามหลายเครื่อง | Large Scale Production |
Docker Compose ไม่ได้มาแทน Dockerfile แต่ใช้ร่วมกัน Dockerfile สร้าง Image ส่วน Compose จัดการว่า Image เหล่านั้นจะรันอย่างไร เชื่อมต่อกันอย่างไร และตั้งค่าอะไรบ้าง
compose.yml Syntax พื้นฐาน
ไฟล์ compose.yml (หรือ docker-compose.yml) เป็นหัวใจของ Docker Compose ประกอบด้วย Top-Level Keys หลักคือ services, networks, volumes, configs และ secrets
# compose.yml — ตัวอย่าง Full-Stack Application
services:
# Web Application (React/Next.js)
web:
build: ./frontend
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_URL=http://api:8000
depends_on:
- api
networks:
- frontend
# API Server (Python FastAPI)
api:
build: ./backend
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- frontend
- backend
volumes:
- ./backend:/app
# PostgreSQL Database
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 10s
timeout: 5s
retries: 5
# Redis Cache
redis:
image: redis:7-alpine
command: redis-server --appendonly yes --maxmemory 256mb
volumes:
- redis_data:/data
ports:
- "6379:6379"
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
volumes:
postgres_data:
redis_data:
compose.yml และ docker-compose.yml แต่ในปี 2026 แนะนำให้ใช้ compose.yml ซึ่งเป็นชื่อมาตรฐานใหม่
คำสั่ง Docker Compose ที่ใช้บ่อย
# รัน Services ทั้งหมด (foreground)
docker compose up
# รัน Services ทั้งหมด (background)
docker compose up -d
# Build images ใหม่ก่อนรัน
docker compose up -d --build
# หยุด Services ทั้งหมด
docker compose down
# หยุดและลบ Volumes ด้วย (ระวัง! ข้อมูลหาย)
docker compose down -v
# ดูสถานะ Services
docker compose ps
# ดู Logs
docker compose logs # ทุก Service
docker compose logs api # เฉพาะ api
docker compose logs -f api # Follow logs (real-time)
# รัน Command ใน Container
docker compose exec api bash
docker compose exec db psql -U user -d myapp
# รัน One-off Command
docker compose run --rm api python manage.py migrate
# Restart Service เดียว
docker compose restart api
# Scale Service (สร้างหลาย Instance)
docker compose up -d --scale api=3
# ดู Resource Usage
docker compose top
docker compose stats
Building Custom Images ใน Compose
คุณสามารถ Build Custom Image จาก Dockerfile ได้โดยตรงในไฟล์ Compose โดยระบุ build context แทนการใช้ image
# compose.yml
services:
api:
# วิธีที่ 1: ระบุ Path ที่มี Dockerfile
build: ./backend
# วิธีที่ 2: ระบุ Context + Dockerfile ชื่ออื่น
build:
context: ./backend
dockerfile: Dockerfile.prod
args:
- NODE_ENV=production
- BUILD_DATE=${BUILD_DATE}
target: production # สำหรับ Multi-stage build
# ตั้งชื่อ Image ที่ Build
image: myapp-api:latest
# Cache จาก Registry
build:
context: .
cache_from:
- myregistry/myapp:cache
ตัวอย่าง Multi-Stage Dockerfile
# backend/Dockerfile
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER appuser
EXPOSE 8000
CMD ["node", "dist/server.js"]
# Stage 3: Development
FROM node:20-alpine AS development
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8000
CMD ["npm", "run", "dev"]
Environment Variables และ .env Files
Docker Compose รองรับหลายวิธีในการจัดการ Environment Variables ทำให้คุณแยก Configuration จาก Code ได้ตามหลัก 12-Factor App
# วิธีที่ 1: กำหนดตรงใน compose.yml
services:
api:
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
- NODE_ENV=production
- LOG_LEVEL=info
# วิธีที่ 2: ใช้ .env file
services:
api:
env_file:
- .env
- .env.local # override ค่าใน .env
# วิธีที่ 3: ใช้ Variable Substitution จาก Shell
services:
api:
image: myapp:${APP_VERSION:-latest}
environment:
- DATABASE_URL=${DATABASE_URL}
- SECRET_KEY=${SECRET_KEY:?SECRET_KEY is required}
# .env file (อยู่ที่เดียวกับ compose.yml)
POSTGRES_USER=myuser
POSTGRES_PASSWORD=supersecret123
POSTGRES_DB=myapp
APP_VERSION=2.1.0
REDIS_MAXMEMORY=512mb
# .env.local (สำหรับ override ในเครื่อง dev)
POSTGRES_PASSWORD=devpassword
LOG_LEVEL=debug
.env ที่มี Password จริงเข้า Git ให้ใส่ .env ใน .gitignore และสร้าง .env.example เป็นตัวอย่างแทน
Networking ใน Docker Compose
Default Bridge Network
เมื่อคุณรัน docker compose up Docker จะสร้าง Network เริ่มต้นให้อัตโนมัติ โดยทุก Service จะอยู่ใน Network เดียวกันและสามารถเรียกหากันด้วยชื่อ Service ได้เลย
# ทุก Service คุยกันด้วย Service Name เป็น hostname
# api สามารถเชื่อมต่อ Database ได้ด้วย:
DATABASE_URL=postgresql://user:pass@db:5432/myapp
# ^^ ← ชื่อ Service
# Redis
REDIS_URL=redis://redis:6379
# ^^^^^ ← ชื่อ Service
Custom Networks
การแยก Network ช่วยเรื่อง Security เพราะ Service ที่ไม่จำเป็นต้องคุยกันจะแยก Network กัน เช่น Frontend ไม่ควรเข้าถึง Database โดยตรง
services:
# Nginx อยู่ใน frontend network เท่านั้น
nginx:
image: nginx:alpine
networks:
- frontend
ports:
- "80:80"
# API อยู่ทั้ง frontend และ backend
api:
build: ./api
networks:
- frontend # รับ request จาก nginx
- backend # เชื่อมต่อ database
# Database อยู่ใน backend เท่านั้น
db:
image: postgres:16
networks:
- backend # nginx เข้าถึงไม่ได้
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # ไม่เปิดให้เข้าจากภายนอก
Service Discovery
Docker Compose มี Built-in DNS ที่ทำให้ Service ค้นหากันได้ด้วยชื่อ ถ้า Scale Service เป็นหลาย Instance Docker จะทำ Round-Robin DNS ให้อัตโนมัติ
# Scale api เป็น 3 instances
docker compose up -d --scale api=3
# nginx สามารถใช้ชื่อ "api" ได้
# Docker จะ round-robin ระหว่าง 3 instances
upstream api {
server api:8000; # จะ resolve เป็น 3 IP อัตโนมัติ
}
Volumes — จัดการ Data Persistence
Named Volumes
Named Volumes ถูกจัดการโดย Docker เอง เหมาะสำหรับ Data ที่ต้องการ Persist เช่น Database Files
services:
db:
image: postgres:16
volumes:
- postgres_data:/var/lib/postgresql/data # Named Volume
redis:
image: redis:7
volumes:
- redis_data:/data # Named Volume
volumes:
postgres_data:
driver: local
redis_data:
driver: local
driver_opts:
type: none
o: bind
device: /path/on/host
Bind Mounts
Bind Mounts Map โฟลเดอร์จาก Host เข้าไปใน Container เหมาะสำหรับ Development ที่ต้องการเห็น Code Change ทันที
services:
api:
build: ./backend
volumes:
# Bind Mount — sync code จาก host
- ./backend:/app
# Bind Mount แบบ Read-Only
- ./config/nginx.conf:/etc/nginx/nginx.conf:ro
# Anonymous Volume — ป้องกัน node_modules ถูก override
- /app/node_modules
tmpfs Mounts
tmpfs Mount เก็บข้อมูลใน Memory เท่านั้น หายเมื่อ Container หยุด เหมาะสำหรับข้อมูลชั่วคราวที่ต้องการความเร็วสูง
services:
api:
tmpfs:
- /tmp
- /run:size=64M # จำกัดขนาด 64MB
Health Checks
Health Checks ช่วยให้ Docker รู้ว่า Service พร้อมใช้งานจริงหรือยัง ไม่ใช่แค่ Process รันอยู่ ซึ่งสำคัญมากสำหรับ depends_on และ Orchestration
services:
api:
build: ./backend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s # ตรวจทุก 30 วินาที
timeout: 10s # Timeout ต่อครั้ง
retries: 3 # ลองกี่ครั้งก่อนถือว่า unhealthy
start_period: 40s # รอกี่วินาทีก่อนเริ่มตรวจ
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
mysql:
image: mysql:8
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
depends_on และ Startup Ordering
เมื่อ Service ต้องรอ Service อื่นพร้อมก่อน คุณใช้ depends_on กับ condition เพื่อควบคุมลำดับการ Start
services:
api:
build: ./backend
depends_on:
db:
condition: service_healthy # รอ DB healthy ก่อน
restart: true # restart ถ้า DB restart
redis:
condition: service_started # รอแค่ Redis start
migrations:
condition: service_completed_successfully # รอ migration เสร็จ
migrations:
build: ./backend
command: python manage.py migrate
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready"]
interval: 5s
timeout: 3s
retries: 10
depends_on แบบเก่า (ไม่มี condition) แค่ควบคุมลำดับการ Start ไม่รอให้ Service พร้อมจริง ควรใช้ condition: service_healthy เสมอเมื่อทำได้
Profiles — Environment-Specific Services
Profiles ช่วยให้คุณกำหนดว่า Service ไหนจะรันใน Environment ไหน เช่น บาง Service รันเฉพาะตอน Development หรือเฉพาะตอน Testing
services:
# Service หลัก — รันเสมอ
api:
build: ./backend
ports:
- "8000:8000"
db:
image: postgres:16
# Debug Tools — รันเฉพาะ dev
pgadmin:
image: dpage/pgadmin4
ports:
- "5050:80"
profiles:
- dev
- debug
# Mailhog — รันเฉพาะ dev
mailhog:
image: mailhog/mailhog
ports:
- "8025:8025"
profiles:
- dev
# Test Runner — รันเฉพาะตอน test
test:
build: ./backend
command: pytest
profiles:
- test
# Monitoring — รันเฉพาะ production
prometheus:
image: prom/prometheus
profiles:
- monitoring
- production
# รัน Services หลัก (ไม่มี profile)
docker compose up -d
# รัน Services หลัก + dev tools
docker compose --profile dev up -d
# รัน Tests
docker compose --profile test run --rm test
# รัน production + monitoring
docker compose --profile production --profile monitoring up -d
# ตั้ง Profile ผ่าน Environment Variable
COMPOSE_PROFILES=dev docker compose up -d
Extending และ Overriding Configs
Docker Compose รองรับการแยกไฟล์ Config ตาม Environment ทำให้คุณมี Base Config แล้ว Override เฉพาะส่วนที่ต่างกัน
# compose.yml — Base config
services:
api:
build: ./backend
environment:
- NODE_ENV=production
networks:
- app-network
db:
image: postgres:16
volumes:
- postgres_data:/var/lib/postgresql/data
# compose.override.yml — Auto-loaded ใน Development
services:
api:
build:
target: development
volumes:
- ./backend:/app # Bind mount สำหรับ hot-reload
ports:
- "8000:8000" # เปิด port สำหรับ debug
environment:
- NODE_ENV=development
- DEBUG=true
db:
ports:
- "5432:5432" # เปิด port สำหรับ local access
# compose.prod.yml — Production overrides
services:
api:
build:
target: production
deploy:
replicas: 3
resources:
limits:
cpus: '1.0'
memory: 512M
restart: always
db:
restart: always
# ไม่เปิด port — เข้าถึงจาก internal network เท่านั้น
# Development (auto-loads compose.override.yml)
docker compose up -d
# Production (ระบุไฟล์เอง)
docker compose -f compose.yml -f compose.prod.yml up -d
# หรือใช้ COMPOSE_FILE environment variable
COMPOSE_FILE=compose.yml:compose.prod.yml docker compose up -d
Compose Watch — File Sync สำหรับ Dev
Docker Compose Watch เป็นฟีเจอร์ใหม่ที่ช่วยให้ Development Workflow ดีขึ้นมาก โดยจะ Watch ไฟล์และ Sync เข้า Container หรือ Rebuild อัตโนมัติเมื่อไฟล์เปลี่ยน
services:
api:
build: ./backend
develop:
watch:
# Sync ไฟล์เข้า Container (เร็ว, ไม่ rebuild)
- action: sync
path: ./backend/src
target: /app/src
ignore:
- node_modules/
- "*.test.js"
# Rebuild เมื่อ Dependencies เปลี่ยน
- action: rebuild
path: ./backend/package.json
# Sync + Restart Container
- action: sync+restart
path: ./backend/config
target: /app/config
frontend:
build: ./frontend
develop:
watch:
- action: sync
path: ./frontend/src
target: /app/src
- action: rebuild
path: ./frontend/package.json
# เริ่ม Watch Mode
docker compose watch
# หรือรวมกับ up
docker compose up --watch
Production Deployment Patterns
Pattern 1: Single Server with Compose
# compose.prod.yml
services:
api:
image: myregistry/myapp-api:${APP_VERSION}
restart: always
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 256M
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
db:
image: postgres:16-alpine
restart: always
volumes:
- postgres_data:/var/lib/postgresql/data
deploy:
resources:
limits:
memory: 2G
nginx:
image: nginx:alpine
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./certbot/conf:/etc/letsencrypt:ro
depends_on:
- api
Pattern 2: Compose กับ Traefik Reverse Proxy
Traefik เป็น Reverse Proxy ที่ออกแบบมาสำหรับ Container โดยเฉพาะ มันอ่าน Docker Labels แล้วตั้งค่า Routing อัตโนมัติ รองรับ HTTPS ด้วย Let's Encrypt ในตัว
services:
traefik:
image: traefik:v3.0
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entryPoints.web.address=:80"
- "--entryPoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
api:
build: ./backend
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.example.com`)"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
- "traefik.http.services.api.loadbalancer.server.port=8000"
web:
build: ./frontend
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=Host(`example.com`)"
- "traefik.http.routers.web.tls.certresolver=letsencrypt"
volumes:
letsencrypt:
Logging Configuration
การตั้งค่า Logging ที่ดีสำคัญมากใน Production เพื่อไม่ให้ Log Files กิน Disk จนเต็ม และเพื่อให้สามารถ Debug ปัญหาได้
services:
api:
# จำกัดขนาด Log
logging:
driver: "json-file"
options:
max-size: "10m" # สูงสุด 10MB ต่อไฟล์
max-file: "5" # เก็บสูงสุด 5 ไฟล์ (50MB total)
compress: "true" # บีบอัดไฟล์เก่า
# ส่ง Log ไปยัง External Service
worker:
logging:
driver: "syslog"
options:
syslog-address: "tcp://logserver:514"
tag: "myapp-worker"
# ปิด Log (ไม่แนะนำ)
noisy-service:
logging:
driver: "none"
Resource Limits
การจำกัด Resource ป้องกันไม่ให้ Container ตัวหนึ่งกิน Resource จนหมด ทำให้ Container อื่นทำงานไม่ได้
services:
api:
deploy:
resources:
# กำหนดค่าสูงสุด
limits:
cpus: '1.0' # สูงสุด 1 CPU core
memory: 512M # สูงสุด 512MB RAM
pids: 100 # จำนวน Process สูงสุด
# กำหนดค่าขั้นต่ำ (reserved)
reservations:
cpus: '0.25' # สงวน 0.25 CPU
memory: 128M # สงวน 128MB RAM
db:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
# Shared Memory สำหรับ PostgreSQL
shm_size: 256mb
Configs และ Secrets
Docker Compose รองรับ Configs สำหรับ Configuration Files และ Secrets สำหรับข้อมูลลับ เช่น Password, API Keys, Certificates
services:
api:
image: myapp-api
configs:
- source: api_config
target: /app/config.json
secrets:
- db_password
- api_key
db:
image: postgres:16
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
configs:
api_config:
file: ./config/api.json
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
environment: "API_KEY" # อ่านจาก Environment Variable
Docker Compose กับ Multi-Stage Builds
การใช้ Multi-Stage Build ร่วมกับ Compose ช่วยให้คุณ Build Image สำหรับ Dev และ Production จาก Dockerfile เดียวกัน
# Dockerfile
FROM node:20-alpine AS base
WORKDIR /app
COPY package*.json ./
FROM base AS development
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
FROM base AS production
RUN npm ci --only=production
COPY . .
RUN npm run build
CMD ["node", "dist/server.js"]
# compose.yml (development)
services:
api:
build:
context: ./backend
target: development # ใช้ Stage "development"
volumes:
- ./backend:/app
- /app/node_modules
# compose.prod.yml (production)
services:
api:
build:
context: ./backend
target: production # ใช้ Stage "production"
Real-World ตัวอย่าง: WordPress + MySQL + phpMyAdmin
# compose.yml — WordPress Stack
services:
wordpress:
image: wordpress:6-apache
restart: always
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_USER: wp_user
WORDPRESS_DB_PASSWORD: wp_secret
WORDPRESS_DB_NAME: wordpress
volumes:
- wp_data:/var/www/html
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: root_secret
MYSQL_DATABASE: wordpress
MYSQL_USER: wp_user
MYSQL_PASSWORD: wp_secret
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
phpmyadmin:
image: phpmyadmin:latest
restart: always
ports:
- "8081:80"
environment:
PMA_HOST: mysql
PMA_USER: root
PMA_PASSWORD: root_secret
profiles:
- dev
depends_on:
- mysql
volumes:
wp_data:
mysql_data:
Real-World ตัวอย่าง: Node.js + MongoDB + Mongo Express
# compose.yml — MERN Stack
services:
api:
build: ./backend
ports:
- "5000:5000"
environment:
MONGODB_URI: mongodb://root:secret@mongo:27017/myapp?authSource=admin
JWT_SECRET: ${JWT_SECRET}
NODE_ENV: development
volumes:
- ./backend:/app
- /app/node_modules
depends_on:
mongo:
condition: service_healthy
develop:
watch:
- action: sync
path: ./backend/src
target: /app/src
- action: rebuild
path: ./backend/package.json
frontend:
build: ./frontend
ports:
- "3000:3000"
volumes:
- ./frontend:/app
- /app/node_modules
environment:
REACT_APP_API_URL: http://localhost:5000
mongo:
image: mongo:7
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: secret
volumes:
- mongo_data:/data/db
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
mongo-express:
image: mongo-express:latest
ports:
- "8081:8081"
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: secret
ME_CONFIG_MONGODB_URL: mongodb://root:secret@mongo:27017/
profiles:
- dev
depends_on:
- mongo
volumes:
mongo_data:
Docker Compose vs Docker Swarm
| เกณฑ์ | Docker Compose | Docker Swarm |
|---|---|---|
| จำนวนเครื่อง | 1 เครื่อง | หลายเครื่อง (Cluster) |
| Use Case | Development, Small Production | Medium Production |
| Scaling | จำกัดใน 1 เครื่อง | ข้ามเครื่องได้ |
| High Availability | ไม่มี | มี (Manager nodes) |
| Learning Curve | ง่าย | ปานกลาง |
| Config File | compose.yml | compose.yml (เดียวกัน!) |
| สถานะในปี 2026 | ยังนิยมมาก | ลดความนิยมลง |
Migration จาก Compose ไป Kubernetes
เมื่อ Application เติบโตจนต้องการ High Availability, Auto-scaling หรือ Multi-node Deployment คุณอาจต้องย้ายจาก Docker Compose ไป Kubernetes คุณสามารถใช้เครื่องมือช่วย Convert ได้
# ใช้ Kompose เพื่อ Convert compose.yml → K8s manifests
# ติดตั้ง Kompose
curl -L https://github.com/kubernetes/kompose/releases/latest/download/kompose-linux-amd64 -o kompose
chmod +x kompose
sudo mv kompose /usr/local/bin/
# Convert
kompose convert -f compose.yml
# จะได้ไฟล์:
# api-deployment.yaml
# api-service.yaml
# db-deployment.yaml
# db-service.yaml
# postgres-data-persistentvolumeclaim.yaml
# Deploy ไป K8s
kubectl apply -f .
# หรือ Convert เป็น Helm Chart
kompose convert -c -f compose.yml
Best Practices สำหรับ Docker Compose
- ใช้ Named Volumes สำหรับ Data: อย่าใช้ Bind Mount สำหรับ Database Data ใน Production เพราะ Performance แย่กว่า
- ตั้ง Health Checks เสมอ: โดยเฉพาะ Database และ Service ที่ใช้เวลา Start นาน เพื่อให้ depends_on ทำงานถูกต้อง
- แยกไฟล์ Config ตาม Environment: ใช้ compose.yml + compose.override.yml สำหรับ Dev และ compose.prod.yml สำหรับ Production
- อย่าเปิด Port ที่ไม่จำเป็น: Database ไม่ควรเปิด Port ใน Production ให้เข้าถึงผ่าน Internal Network เท่านั้น
- จำกัด Resources: ตั้ง Memory Limit และ CPU Limit เพื่อป้องกัน Container กิน Resource ทั้งเครื่อง
- ใช้ .env สำหรับ Secrets: ไม่ Hard-code Password ใน compose.yml และอย่า Commit .env เข้า Git
- ตั้ง Logging Limits: ไม่งั้น Log จะกิน Disk จนเต็ม
- ใช้ restart: always ใน Production: เพื่อให้ Container กลับมาทำงานอัตโนมัติเมื่อ Crash
- Pin Image Versions: ใช้
postgres:16-alpineไม่ใช่postgres:latestเพื่อความแน่นอนของ Build
Troubleshooting ปัญหาที่พบบ่อย
# ปัญหา: Container เริ่มไม่ได้
docker compose logs service_name
docker compose events
# ปัญหา: Port ชน
docker compose ps # ดู port ที่ใช้อยู่
lsof -i :8000 # ดู process ที่ใช้ port นั้น
# ปัญหา: Volume Permission
# เพิ่มใน Dockerfile:
RUN chown -R appuser:appgroup /app
USER appuser
# ปัญหา: Network ไม่เชื่อมกัน
docker compose exec api ping db # ทดสอบ connectivity
docker network ls # ดู networks
docker network inspect project_default # ดูรายละเอียด
# ปัญหา: Build Cache เก่า
docker compose build --no-cache
docker compose up -d --force-recreate
# ปัญหา: Disk เต็ม
docker system df # ดูการใช้ disk
docker system prune -a # ลบทุกอย่างที่ไม่ได้ใช้
docker volume prune # ลบ volumes ที่ไม่ได้ใช้
สรุป
Docker Compose เป็นเครื่องมือที่ขาดไม่ได้สำหรับการพัฒนา Application ที่มีหลาย Service ด้วยไฟล์ compose.yml เพียงไฟล์เดียว คุณสามารถกำหนดทุกอย่างตั้งแต่ Services, Networks, Volumes จนถึง Health Checks และ Resource Limits
ในปี 2026 Docker Compose มาพร้อมฟีเจอร์ใหม่ที่ทรงพลัง เช่น Compose Watch สำหรับ Development, Profiles สำหรับแยก Environment, Configs และ Secrets สำหรับ Security และ Production Deployment Patterns ที่ใช้งานได้จริง
เริ่มต้นวันนี้ สร้างไฟล์ compose.yml ในโปรเจกต์ของคุณ กำหนด Services ที่ต้องการ รัน docker compose up แล้วคุณจะเข้าใจว่าทำไม Docker Compose ถึงเป็นเครื่องมือมาตรฐานของนักพัฒนาทั่วโลก ไม่ว่าจะเป็นการพัฒนาบนเครื่องตัวเองหรือ Deploy ขึ้น Production Server
