Chuyển tới nội dung chính

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