Hướng Dẫn Cài Đặt và Sử Dụng Docker Compose
Giới Thiệu Docker Compose
Docker Compose là tool để define và run multi-container Docker applications. Với một file YAML duy nhất, bạn có thể cấu hình tất cả services, networks và volumes, sau đó start toàn bộ stack với một command.
Use Cases:
- 🚀 Development environments (local LAMP/MEAN stack)
- 🧪 Automated testing (spin up test environment)
- 🎯 Single-host deployments (small production apps)
- 📦 CI/CD pipelines
- 🔄 Microservices orchestration
Docker vs Docker Compose:
Docker:
$ docker run -d --name db mysql
$ docker run -d --name web nginx
$ docker run -d --name app node
(Phải quản lý từng container riêng)
Docker Compose:
$ docker-compose up -d
(Quản lý toàn bộ stack với 1 command)
![Sơ đồ Docker Compose architecture - Đặt ảnh tại /static/img/software/docker-compose-arch.png]
Thời gian thực hiện: 60-90 phút
Độ khó: Trung bình
Yêu cầu: Docker đã cài đặt, kiến thức Linux cơ bản
Phần 1: Cài Đặt Docker Compose
Method 1: Binary Installation (Khuyến Nghị)
# Check latest version: https://github.com/docker/compose/releases
COMPOSE_VERSION="2.24.0"
# Download binary
sudo curl -L "https://github.com/docker/compose/releases/download/v${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/local/bin/docker-compose
# Make executable
sudo chmod +x /usr/local/bin/docker-compose
# Create symbolic link (optional, cho command ngắn hơn)
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
# Verify installation
docker-compose --version
# Output: Docker Compose version v2.24.0
Method 2: Install via pip (Python)
# Install pip nếu chưa có
sudo apt update
sudo apt install python3-pip -y
# Install docker-compose
sudo pip3 install docker-compose
# Verify
docker-compose --version
Method 3: Docker Desktop (macOS/Windows)
Docker Compose đã được tích hợp sẵn trong Docker Desktop.
# Check version
docker compose version
⚠️ Note: Docker Desktop sử dụng docker compose (space) thay vì docker-compose (hyphen).
Phần 2: Docker Compose File Basics
Cấu Trúc File docker-compose.yml
version: '3.8' # Compose file version
services: # Define containers
service1:
# Service configuration
service2:
# Service configuration
networks: # Custom networks (optional)
network1:
volumes: # Named volumes (optional)
volume1:
configs: # Configuration files (optional)
secrets: # Sensitive data (optional)
Example 1: Simple Web Server
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
Test:
# Create project directory
mkdir nginx-compose && cd nginx-compose
# Create HTML file
echo "<h1>Hello from Docker Compose!</h1>" > html/index.html
# Create docker-compose.yml
nano docker-compose.yml # paste config above
# Start service
docker-compose up -d
# Check status
docker-compose ps
# Access
curl http://localhost
# View logs
docker-compose logs -f web
# Stop
docker-compose down
Phần 3: Complete Examples
Example 2: WordPress + MySQL
version: '3.8'
services:
# MySQL Database
db:
image: mysql:8.0
container_name: wordpress_db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppassword
volumes:
- db_data:/var/lib/mysql
networks:
- wordpress_net
command: '--default-authentication-plugin=mysql_native_password'
# WordPress Application
wordpress:
depends_on:
- db
image: wordpress:latest
container_name: wordpress_app
restart: unless-stopped
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppassword
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress_data:/var/www/html
networks:
- wordpress_net
# Named volumes
volumes:
db_data:
driver: local
wordpress_data:
driver: local
# Custom network
networks:
wordpress_net:
driver: bridge
Deploy:
mkdir wordpress && cd wordpress
nano docker-compose.yml # paste config
# Start stack
docker-compose up -d
# Check logs
docker-compose logs -f
# Access WordPress
# http://localhost:8080
# List volumes
docker volume ls
# Inspect network
docker network inspect wordpress_wordpress_net
![Screenshot WordPress running - Đặt ảnh tại /static/img/software/docker-compose-wordpress.png]
Example 3: MEAN Stack (MongoDB + Express + Angular + Node)
version: '3.8'
services:
# MongoDB Database
mongodb:
image: mongo:6.0
container_name: mean_mongodb
restart: unless-stopped
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: admin123
MONGO_INITDB_DATABASE: meandb
volumes:
- mongo_data:/data/db
- ./mongo-init:/docker-entrypoint-initdb.d:ro
networks:
- mean_net
ports:
- "27017:27017"
# Node.js Backend API
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: mean_backend
restart: unless-stopped
depends_on:
- mongodb
environment:
NODE_ENV: production
MONGODB_URI: mongodb://admin:admin123@mongodb:27017/meandb?authSource=admin
JWT_SECRET: your_jwt_secret_key
PORT: 3000
ports:
- "3000:3000"
volumes:
- ./backend:/app
- /app/node_modules
networks:
- mean_net
command: npm start
# Angular Frontend
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: mean_frontend
restart: unless-stopped
depends_on:
- backend
ports:
- "4200:80"
networks:
- mean_net
# Nginx Reverse Proxy
nginx:
image: nginx:alpine
container_name: mean_nginx
restart: unless-stopped
depends_on:
- frontend
- backend
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
networks:
- mean_net
volumes:
mongo_data:
networks:
mean_net:
driver: bridge
Backend Dockerfile:
# backend/Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Frontend Dockerfile:
# frontend/Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build --prod
FROM nginx:alpine
COPY --from=builder /app/dist/* /usr/share/nginx/html/
EXPOSE 80
Nginx Config:
# nginx/nginx.conf
http {
upstream backend {
server backend:3000;
}
server {
listen 80;
server_name localhost;
# Frontend
location / {
proxy_pass http://frontend:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# API
location /api {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
Example 4: Microservices with Redis & RabbitMQ
version: '3.8'
services:
# Redis Cache
redis:
image: redis:7-alpine
container_name: micro_redis
restart: unless-stopped
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- micro_net
command: redis-server --appendonly yes
# RabbitMQ Message Queue
rabbitmq:
image: rabbitmq:3-management-alpine
container_name: micro_rabbitmq
restart: unless-stopped
environment:
RABBITMQ_DEFAULT_USER: admin
RABBITMQ_DEFAULT_PASS: admin123
ports:
- "5672:5672" # AMQP
- "15672:15672" # Management UI
volumes:
- rabbitmq_data:/var/lib/rabbitmq
networks:
- micro_net
# PostgreSQL Database
postgres:
image: postgres:15-alpine
container_name: micro_postgres
restart: unless-stopped
environment:
POSTGRES_USER: microuser
POSTGRES_PASSWORD: micropass
POSTGRES_DB: microdb
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- micro_net
ports:
- "5432:5432"
# User Service
user-service:
build: ./services/user-service
container_name: user_service
restart: unless-stopped
depends_on:
- postgres
- redis
- rabbitmq
environment:
DATABASE_URL: postgresql://microuser:micropass@postgres:5432/microdb
REDIS_URL: redis://redis:6379
RABBITMQ_URL: amqp://admin:admin123@rabbitmq:5672
networks:
- micro_net
ports:
- "3001:3000"
# Order Service
order-service:
build: ./services/order-service
container_name: order_service
restart: unless-stopped
depends_on:
- postgres
- redis
- rabbitmq
environment:
DATABASE_URL: postgresql://microuser:micropass@postgres:5432/microdb
REDIS_URL: redis://redis:6379
RABBITMQ_URL: amqp://admin:admin123@rabbitmq:5672
networks:
- micro_net
ports:
- "3002:3000"
# API Gateway
api-gateway:
build: ./services/api-gateway
container_name: api_gateway
restart: unless-stopped
depends_on:
- user-service
- order-service
environment:
USER_SERVICE_URL: http://user-service:3000
ORDER_SERVICE_URL: http://order-service:3000
networks:
- micro_net
ports:
- "8080:8080"
volumes:
redis_data:
rabbitmq_data:
postgres_data:
networks:
micro_net:
driver: bridge
Phần 4: Advanced Configurations
Environment Variables
Method 1: .env File
# .env
MYSQL_ROOT_PASSWORD=supersecret
MYSQL_DATABASE=myapp
MYSQL_USER=appuser
MYSQL_PASSWORD=apppass
APP_PORT=8080
# docker-compose.yml
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
app:
image: myapp:latest
ports:
- "${APP_PORT}:80"
environment:
DB_HOST: db
DB_NAME: ${MYSQL_DATABASE}
Method 2: env_file
services:
app:
image: myapp:latest
env_file:
- ./config/app.env
- ./config/secrets.env
Health Checks
services:
web:
image: nginx
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
Resource Limits
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
# Alternative syntax (compose v2)
cpus: 2.0
mem_limit: 1g
mem_reservation: 512m
Logging Configuration
services:
app:
image: myapp:latest
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
labels: "production"
nginx:
image: nginx
logging:
driver: "syslog"
options:
syslog-address: "tcp://192.168.0.100:514"
tag: "nginx"
Depends On với Conditions
version: '3.8'
services:
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
app:
image: myapp:latest
depends_on:
db:
condition: service_healthy
Profiles (Conditional Services)
services:
# Always run
web:
image: nginx
ports:
- "80:80"
# Only run in development
debug:
image: myapp:debug
profiles: ["dev"]
ports:
- "9229:9229"
# Only run in production
monitoring:
image: prometheus
profiles: ["prod"]
Usage:
# Dev environment
docker-compose --profile dev up
# Production
docker-compose --profile prod up
# Multiple profiles
docker-compose --profile dev --profile test up
Phần 5: Docker Compose Commands
Essential Commands
# Start services
docker-compose up # Foreground
docker-compose up -d # Background (detached)
docker-compose up --build # Rebuild images
docker-compose up --force-recreate # Force recreate containers
# Stop services
docker-compose stop # Stop containers
docker-compose down # Stop và remove containers
docker-compose down -v # Also remove volumes
docker-compose down --rmi all # Also remove images
# View status
docker-compose ps # List containers
docker-compose ps -a # Include stopped
docker-compose top # Show running processes
# Logs
docker-compose logs # All logs
docker-compose logs -f # Follow logs
docker-compose logs -f web # Specific service
docker-compose logs --tail=100 web # Last 100 lines
# Execute commands
docker-compose exec web sh # Interactive shell
docker-compose exec db mysql -u root -p
docker-compose run --rm app npm test # Run one-off command
# Build
docker-compose build # Build all images
docker-compose build --no-cache web # Rebuild without cache
docker-compose build --pull # Pull latest base images
# Scale services
docker-compose up -d --scale web=3 # Run 3 instances of web
# Restart services
docker-compose restart # Restart all
docker-compose restart web # Restart specific service
# Validate config
docker-compose config # Validate và view config
docker-compose config --services # List services
# Remove stopped containers
docker-compose rm # Interactive
docker-compose rm -f # Force remove
Advanced Commands
# Pause/Unpause
docker-compose pause # Pause all services
docker-compose unpause # Unpause
# Events
docker-compose events # Stream events
# Images
docker-compose images # List images
docker-compose pull # Pull latest images
# Port mapping
docker-compose port web 80 # Show mapped port
# Create services without starting
docker-compose create
# Pushimages
docker-compose push # Push to registry
Phần 6: Production Best Practices
1. Multi-Stage Builds
# Production-optimized Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Production image
FROM node:18-alpine
RUN apk add --no-cache dumb-init
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
USER node
EXPOSE 3000
CMD ["dumb-init", "node", "dist/server.js"]
2. Security Configuration
version: '3.8'
services:
app:
image: myapp:latest
# Security options
security_opt:
- no-new-privileges:true
# Read-only root filesystem
read_only: true
# Tmpfs for writable dirs
tmpfs:
- /tmp
- /var/run
# Drop capabilities
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
# User namespace
user: "1000:1000"
# Resource limits
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
3. Secrets Management
version: '3.8'
services:
app:
image: myapp:latest
secrets:
- db_password
- api_key
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
API_KEY_FILE: /run/secrets/api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
external: true
4. Production docker-compose.yml
version: '3.8'
services:
web:
image: myapp:${VERSION:-latest}
restart: always
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=Host(`example.com`)"
- "traefik.http.routers.web.entrypoints=websecure"
- "traefik.http.routers.web.tls.certresolver=letsencrypt"
networks:
- frontend
- backend
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "5"
networks:
frontend:
external: true
backend:
internal: true
5. CI/CD Integration
# .gitlab-ci.yml
stages:
- build
- test
- deploy
build:
stage: build
script:
- docker-compose build
- docker-compose push
test:
stage: test
script:
- docker-compose run --rm app npm test
- docker-compose down
deploy:
stage: deploy
script:
- docker-compose pull
- docker-compose up -d
- docker-compose logs -f
only:
- main
Phần 7: Monitoring và Debugging
Monitoring Stack
version: '3.8'
services:
# Prometheus
prometheus:
image: prom/prometheus:latest
container_name: prometheus
volumes:
- ./prometheus:/etc/prometheus
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
ports:
- "9090:9090"
networks:
- monitoring
# Grafana
grafana:
image: grafana/grafana:latest
container_name: grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
ports:
- "3000:3000"
networks:
- monitoring
# Node Exporter
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
ports:
- "9100:9100"
networks:
- monitoring
# cAdvisor (Container metrics)
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- "8080:8080"
networks:
- monitoring
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
driver: bridge
Debugging Tips
# Check container logs
docker-compose logs -f --tail=100 service_name
# Inspect container
docker-compose exec service_name sh
docker inspect $(docker-compose ps -q service_name)
# Check network connectivity
docker-compose exec service1 ping service2
docker-compose exec service1 nslookup service2
# View environment variables
docker-compose exec service_name env
# Check disk usage
docker system df
docker volume ls
# Clean up everything
docker-compose down -v --rmi all --remove-orphans
docker system prune -a --volumes
Troubleshooting Common Issues
Issue 1: Port Already in Use
# Find process using port
sudo lsof -i :80
sudo netstat -tulpn | grep :80
# Kill process
sudo kill -9 PID
# Or change port in docker-compose.yml
ports:
- "8080:80" # Map to different host port
Issue 2: Container Exiting Immediately
# Check logs
docker-compose logs service_name
# Run with shell to debug
docker-compose run --rm service_name sh
# Check health
docker-compose ps
Issue 3: Volume Permissions
# Fix permissions
sudo chown -R 1000:1000 ./data
# Or in docker-compose.yml
services:
app:
user: "1000:1000"
volumes:
- ./data:/app/data
Tài Liệu Tham Khảo
Kết Luận
Bạn đã nắm vững Docker Compose từ cơ bản đến nâng cao. Giờ bạn có thể:
✅ Deploy multi-container applications
✅ Manage complex microservices stacks
✅ Configure production-ready environments
✅ Implement CI/CD với Docker Compose
✅ Monitor và troubleshoot containers
Next steps:
- Học Docker Swarm hoặc Kubernetes cho container orchestration
- Implement service mesh (Istio, Linkerd)
- Advanced networking và security
Tags: #docker #docker-compose #containers #microservices #devops #orchestration #deployment
Cập nhật lần cuối: 19/12/2025
Tác giả: BacPV Docs Team