Docker Compose คืออะไรทำไมต้องใช้
ถ้าคุณเคยรัน docker run ที่มี option ยาวเป็นหางว่าวแล้วต้องทำซ้ำทุกครั้งที่ต้องการสร้าง container ใหม่ Docker Compose คือทางออกผมใช้ Docker มาตั้งแต่ version 1.0 ปี 2014 และผมบอกได้เลยว่า Docker Compose เปลี่ยนวิธีการทำงานของผมไปอย่างสิ้นเชิงจากที่ต้องจำ command ยาวๆกลายเป็นแค่เขียน YAML file แล้วรัน docker compose up
Docker Compose เป็นเครื่องมือสำหรับ define และ run multi-container Docker applications คุณเขียน YAML file อธิบายว่า application ต้องการ services อะไรบ้างใช้ image ไหนต่อ network ยังไง mount volume ที่ไหนแล้ว Compose จัดการสร้างทุกอย่างให้ในคำสั่งเดียว
Compose V1 vs V2 — ต้องใช้ตัวไหน
ตั้งแต่ปี 2023 Docker Compose V1 (ที่เรียกด้วย docker-compose มี hyphen) ถูก deprecate แล้วตอนนี้ต้องใช้ V2 ที่เรียกด้วย docker compose (ไม่มี hyphen เป็น subcommand ของ docker) V2 เขียนด้วย Go แทน Python ทำให้เร็วกว่าเดิมมากและรองรับ feature ใหม่ๆเช่น profiles, service dependencies ที่ดีขึ้นและ watch mode สำหรับ development
เมื่อไหร่ควรใช้ Compose เมื่อไหร่ควรใช้ Kubernetes
คำถามนี้ผมถูกถามบ่อยมากผมตอบแบบตรงๆเลยว่าถ้า application ของคุณรันบนเครื่องเดียวหรือ 2-3 เครื่องใช้ Compose ถ้าต้อง scale ข้าม 10+ เครื่องต้อง auto-scaling ต้อง rolling update ที่ซับซ้อนถึงค่อยใช้ Kubernetes ผมเจอหลายบริษัทที่ใช้ Kubernetes ทั้งที่มี server แค่ 3 เครื่องมันเหมือนใช้ปืนใหญ่ยิงนก overhead เยอะเกินไป
ติดตั้ง Docker Compose V2
Docker Compose V2 มาพร้อมกับ Docker Desktop อยู่แล้วแต่ถ้าใช้ Docker Engine บน Linux server ต้องติดตั้งแยก
ติดตั้งบน Ubuntu/Debian
# ติดตั้ง Docker Engine (ถ้ายังไม่มี)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Docker Compose V2 มาพร้อม Docker Engine แล้ว
# ตรวจสอบ version
docker compose version
# Docker Compose version v2.27.0
# ถ้าต้องการ update เฉพาะ Compose plugin
sudo apt update
sudo apt install docker-compose-plugin
# ทดสอบ
docker compose version
ติดตั้งบน CentOS/Rocky Linux
# ติดตั้ง Docker repo
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# ติดตั้ง Docker + Compose
sudo dnf install docker-ce docker-ce-cli containerd.io docker-compose-plugin
# เริ่ม Docker service
sudo systemctl enable --now docker
# ตรวจสอบ
docker compose version
เขียน docker-compose.yml ไฟล์แรก
ผมจะเริ่มจากตัวอย่างง่ายๆแล้วค่อยเพิ่มความซับซ้อนทีละนิดวิธีนี้ผมสอนมาหลายรุ่นแล้วเรียนรู้ได้เร็วที่สุด
ตัวอย่าง: Nginx Web Server
# docker-compose.yml
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
# สร้าง HTML file
mkdir html
echo "<h1>Hello from Docker Compose</h1>" > html/index.html
# Start
docker compose up -d
# ดู logs
docker compose logs -f web
# หยุด
docker compose down
ทำความเข้าใจ YAML Structure
ไฟล์ docker-compose.yml มี top-level keys หลักๆดังนี้ services คือ containers ที่ต้องการ run ทุก service จะถูกสร้างเป็น container volumes คือ named volumes สำหรับเก็บ data ถาวร networks คือ custom networks สำหรับ container communication secrets คือ sensitive data เช่น passwords, API keys configs คือ configuration files ที่ mount เข้า container
ตัวอย่าง: Full-Stack Application
# docker-compose.yml — Node.js + PostgreSQL + Redis
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://appuser:secretpass@db:5432/myapp
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
restart: unless-stopped
networks:
- app-network
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: appuser
POSTGRES_PASSWORD: secretpass
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d myapp"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- app-network
cache:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
restart: unless-stopped
networks:
- app-network
volumes:
postgres-data:
redis-data:
networks:
app-network:
driver: bridge
สังเกตว่าผมใช้ depends_on กับ condition: service_healthy ซึ่งเป็น feature ของ Compose V2 ที่ดีมากมันจะรอจนกว่า PostgreSQL จะ ready จริงๆถึงค่อย start app ไม่ใช่แค่รอจน container start เท่านั้น
Networking ใน Docker Compose
เรื่อง Networking เป็นจุดที่หลายคนสับสนผมจะอธิบายให้ชัดเจน
Default Network
เมื่อรัน docker compose up Compose จะสร้าง default network ให้อัตโนมัติชื่อว่า {project_name}_default ทุก service ในไฟล์ compose จะอยู่ใน network นี้และสามารถติดต่อกันได้ผ่าน service name เช่น app สามารถเชื่อมต่อ database ได้โดยใช้ hostname db ตรงๆเลยไม่ต้องรู้ IP address
Custom Networks
# แยก network สำหรับ frontend และ backend
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
networks:
- frontend
app:
build: .
networks:
- frontend
- backend
db:
image: postgres:16-alpine
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # ไม่ให้เข้าถึงจากภายนอก
ในตัวอย่างนี้ nginx เข้าถึง app ได้แต่เข้าถึง db ไม่ได้เพราะอยู่คนละ network ส่วน app เข้าถึงได้ทั้ง nginx และ db เพราะอยู่ทั้ง 2 networks วิธีนี้เพิ่ม security เพราะถ้า nginx ถูก compromise ก็เข้าถึง database ไม่ได้โดยตรง
External Networks
# เชื่อมต่อกับ network ที่มีอยู่แล้ว
# เช่น network ที่สร้างจาก Compose project อื่น
networks:
shared:
external: true
name: monitoring_network
# สร้าง external network ก่อน
# docker network create monitoring_network
Volumes และ Data Persistence
ปัญหาใหญ่ที่สุดของ Container คือเมื่อ container ถูกลบ data ก็หายไปด้วย Volumes คือทางออก
Named Volumes vs Bind Mounts
Named Volumes ถูกจัดการโดย Docker เก็บใน /var/lib/docker/volumes/ เหมาะสำหรับ data ที่ container เป็นคนเขียนเช่น database files ส่วน Bind Mounts คือ mount directory จาก host เข้า container เหมาะสำหรับ source code หรือ config files ที่ต้องการแก้ไขจาก host
services:
db:
image: mysql:8.0
volumes:
# Named volume สำหรับ database data
- mysql-data:/var/lib/mysql
# Bind mount สำหรับ custom config
- ./my.cnf:/etc/mysql/conf.d/custom.cnf:ro
# Bind mount สำหรับ backup
- /backup/mysql:/backup
app:
build: .
volumes:
# Bind mount source code สำหรับ development
- ./src:/app/src
# Named volume สำหรับ node_modules (ไม่ sync กับ host)
- node_modules:/app/node_modules
volumes:
mysql-data:
driver: local
node_modules:
Volume Backup
# Backup named volume
docker run --rm \
-v mysql-data:/source:ro \
-v /backup:/backup \
alpine tar czf /backup/mysql-data-$(date +%Y%m%d).tar.gz -C /source .
# Restore
docker run --rm \
-v mysql-data:/target \
-v /backup:/backup \
alpine tar xzf /backup/mysql-data-20260228.tar.gz -C /target
ตัวอย่างจริง: WordPress + MySQL + Redis
ผมจะแสดง Production-grade Docker Compose สำหรับ WordPress ที่ผมใช้จริงมี MySQL สำหรับ database, Redis สำหรับ object cache และ Nginx เป็น reverse proxy พร้อม SSL
# docker-compose.yml — Production WordPress
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- wordpress-data:/var/www/html:ro
depends_on:
- wordpress
restart: unless-stopped
networks:
- frontend
wordpress:
image: wordpress:6.5-php8.3-fpm-alpine
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_USER: wp_user
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
WORDPRESS_DB_NAME: wordpress
WORDPRESS_CONFIG_EXTRA: |
define('WP_REDIS_HOST', 'redis');
define('WP_REDIS_PORT', 6379);
define('WP_CACHE', true);
define('WP_MEMORY_LIMIT', '256M');
volumes:
- wordpress-data:/var/www/html
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
secrets:
- db_password
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
networks:
- frontend
- backend
mysql:
image: mysql:8.0
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wp_user
MYSQL_PASSWORD_FILE: /run/secrets/db_password
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
volumes:
- mysql-data:/var/lib/mysql
- ./mysql/conf.d:/etc/mysql/conf.d:ro
secrets:
- db_password
- db_root_password
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- backend
redis:
image: redis:7-alpine
command: redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru --save 60 1000
volumes:
- redis-data:/data
restart: unless-stopped
networks:
- backend
volumes:
wordpress-data:
mysql-data:
redis-data:
networks:
frontend:
backend:
internal: true
secrets:
db_password:
file: ./secrets/db_password.txt
db_root_password:
file: ./secrets/db_root_password.txt
Nginx Configuration สำหรับ WordPress
# nginx/conf.d/wordpress.conf
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
root /var/www/html;
index index.php;
client_max_body_size 64M;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass wordpress:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Production-Ready Compose
หลายคนใช้ Docker Compose แค่สำหรับ development แต่จริงๆมันใช้ production ได้ดีถ้าตั้งค่าถูกต้อง
Resource Limits
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
# Compose V2 รองรับ deploy section แม้ไม่ได้ใช้ Swarm
Health Checks ที่ดี
services:
app:
image: myapp:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s # รอ app start ก่อน
Logging Configuration
services:
app:
image: myapp:latest
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
compress: "true"
# หรือส่ง log ไป centralized logging
# ดูบทความ ELK Stack ที่ผมเขียนไว้
สำหรับ centralized logging ผมแนะนำให้อ่านบทความ Elasticsearch ELK Stack ที่ผมเขียนไว้ใช้คู่กับ Docker ได้ดีมาก
Docker Compose Watch (Development)
# Feature ใหม่ของ Compose V2 สำหรับ live development
services:
app:
build: .
develop:
watch:
- action: sync
path: ./src
target: /app/src
- action: rebuild
path: ./package.json
# รัน development mode
docker compose watch
# เมื่อแก้ไฟล์ใน ./src จะ sync เข้า container อัตโนมัติ
# เมื่อแก้ package.json จะ rebuild image ใหม่
Profiles สำหรับแยก Environment
services:
app:
image: myapp:latest
# รันเสมอ ไม่ต้องระบุ profile
debug-tools:
image: nicolaka/netshoot
profiles: ["debug"]
# รันเฉพาะเมื่อระบุ profile
monitoring:
image: prom/prometheus
profiles: ["monitoring"]
# รัน default services
docker compose up -d
# รัน พร้อม debug tools
docker compose --profile debug up -d
# รัน ทุก profiles
docker compose --profile debug --profile monitoring up -d
Docker Compose กับ Podman Compose ต่างกันยังไง?
Podman Compose เป็น third-party tool ที่พยายามให้ compatible กับ docker-compose.yml แต่ยังไม่ support ทุก feature เท่า Docker Compose V2 เช่น watch mode, profiles บาง option ยังไม่ทำงานถ้าใช้ Podman ผมแนะนำให้ใช้ podman-compose สำหรับ simple setups แต่ถ้า compose file ซับซ้อนอาจจะต้องใช้ Docker แทนหรือใช้ Podman กับ Kubernetes YAML แทน
ทำไม container start แล้ว exit ทันที?
ปัญหานี้ผมเจอบ่อยมากสาเหตุหลักคือ process ใน container ไม่ได้รันแบบ foreground ดู logs ด้วย docker compose logs service_name ก่อนเลยอีกสาเหตุคือ healthcheck fail ทำให้ container ถูก restart วนลูปให้ลอง comment healthcheck ออกก่อนเพื่อ debug และที่พบบ่อยอีกอย่างคือ environment variable ผิดเช่น database password ไม่ตรง
docker compose up กับ docker compose start ต่างกันยังไง?
docker compose up จะสร้าง container ใหม่ (ถ้ายังไม่มี) สร้าง network สร้าง volume แล้ว start ทุกอย่างส่วน docker compose start จะ start container ที่มีอยู่แล้วเท่านั้นไม่สร้างใหม่ในทางปฏิบัติผมใช้ up -d เกือบทุกครั้งเพราะมัน idempotent ถ้า container มีอยู่แล้วก็แค่ start ไม่ได้สร้างใหม่
จะ update image ยังไงโดยไม่ downtime?
ใช้คำสั่ง docker compose pull เพื่อดึง image ใหม่แล้วตามด้วย docker compose up -d Compose จะ recreate เฉพาะ container ที่ image เปลี่ยนช่วง recreate จะมี downtime สั้นๆประมาณ 1-2 วินาทีถ้าต้องการ zero-downtime ต้องใช้ load balancer หน้า container หรือใช้ Docker Swarm / Kubernetes แทน
สรุป
Docker Compose เป็นเครื่องมือที่ทุกู้คืนที่ใช้ Docker ต้องรู้จักมันช่วยจัดการ multi-container applications ได้ง่ายดายผ่าน YAML file เดียวตั้งแต่ development จนถึง production สิ่งสำคัญที่ผมอยากเน้นคืออย่าลืมตั้ง health checks, resource limits, logging configuration และ secrets management เพราะสิ่งเหล่านี้คือสิ่งที่แยก development setup กับ production-ready setup ออกจากกัน
Docker Compose ไม่ใช่คู่แข่งของ Kubernetes แต่เป็นเครื่องมือคนละระดับใช้ Compose สำหรับ single-host deployments และ development ใช้ Kubernetes สำหรับ multi-host orchestration ที่ต้องการ auto-scaling เลือกใช้ให้เหมาะกับขนาดของ application
อ่านเพิ่มเติม: สอนเทรด Forex | XM Signal | IT Hardware | อาชีพ IT