Docker Phần 3 - Docker Compose, dàn nhạc giao hưởng của các container! 🎼
Xin chào các "cơ trưởng Docker"! 🧑✈️
Sau khi đã học cách build container đơn lẻ trong phần 1 và quản lý image với Registry trong phần 2, hôm nay chúng ta sẽ cùng khám phá Docker Compose - công cụ tuyệt vời giúp điều phối cả một "dàn nhạc giao hưởng" các container! 🎼
Hãy tưởng tượng bạn đang xây dựng một ứng dụng web hoàn chỉnh: frontend React, backend Node.js, database PostgreSQL, Redis cache, và Nginx làm reverse proxy. Thay vì phải docker run
từng thằng một (và nhớ hàng tá parameters), Docker Compose sẽ giúp bạn start tất cả chỉ với một câu lệnh đơn giản! ✨
🎭 Docker Compose là "ai" và làm "gì"?
1.1. Định nghĩa đơn giản (cho người mới bắt đầu)
Docker Compose giống như một file kịch bản (docker-compose.yml) mô tả cách các container hoạt động cùng nhau. Thay vì:
# Cách cũ - phải chạy từng lệnh một (mệt mỏi quá!)
docker network create myapp-network
docker run -d --name postgres --network myapp-network -e POSTGRES_DB=myapp postgres:13
docker run -d --name redis --network myapp-network redis:6
docker run -d --name backend --network myapp-network -p 3000:3000 myapp-backend
docker run -d --name frontend --network myapp-network -p 80:80 myapp-frontend
Bạn chỉ cần:
# Cách mới - một lệnh rule them all! 🔥
docker-compose up
1.2. Docker Compose vs Docker thường
Tiêu chí | Docker thường | Docker Compose |
---|---|---|
Số container | Một container mỗi lần | Nhiều container cùng lúc |
Cấu hình | Qua command line | Qua file YAML |
Mạng | Phải tạo manually | Tự động tạo và kết nối |
Phụ thuộc | Không quản lý | Quản lý thứ tự khởi động |
Quản lý | Từng container riêng lẻ | Toàn bộ stack cùng lúc |
Phù hợp | App đơn giản, test nhanh | App phức tạp, nhiều service |
🏗️ Cấu trúc file docker-compose.yml
Trước khi đi vào ví dụ, hãy hiểu cấu trúc cơ bản của file docker-compose.yml
:
version: '3.8' # Phiên bản Docker Compose
services: # Định nghĩa các container
service1:
# Cấu hình cho container 1
service2:
# Cấu hình cho container 2
networks: # Định nghĩa mạng (tùy chọn)
# Cấu hình network
volumes: # Định nghĩa volume (tùy chọn)
# Cấu hình lưu trữ
2.1. Các thuộc tính quan trọng của services
Thuộc tính | Mô tả | Ví dụ |
---|---|---|
image | Image Docker để sử dụng | image: nginx:latest |
build | Build image từ Dockerfile | build: ./backend |
ports | Map port host:container | ports: ["3000:3000"] |
environment | Biến môi trường | environment: NODE_ENV=production |
volumes | Mount volume hoặc bind mount | volumes: ["./data:/var/lib/mysql"] |
depends_on | Phụ thuộc vào service khác | depends_on: ["database"] |
networks | Kết nối vào network | networks: ["app-network"] |
restart | Chính sách restart | restart: unless-stopped |
🚀 Ví dụ thực tế: Blog App hoàn chỉnh
Hãy cùng xây dựng một blog app với architecture sau:
- Frontend: React app (port 3000)
- Backend: Node.js API (port 5000)
- Database: PostgreSQL (port 5432)
- Cache: Redis (port 6379)
- Proxy: Nginx (port 80)
3.1. Cấu trúc thư mục
my-blog-app/
├── docker-compose.yml
├── frontend/
│ ├── Dockerfile
│ ├── package.json
│ └── src/...
├── backend/
│ ├── Dockerfile
│ ├── package.json
│ └── src/...
├── nginx/
│ └── nginx.conf
└── data/
└── postgres/ # Sẽ được tạo tự động
3.2. File docker-compose.yml siêu chi tiết
version: '3.8'
services:
# 📊 Database - PostgreSQL
database:
image: postgres:13
container_name: blog-postgres
restart: unless-stopped
environment:
POSTGRES_DB: blog_db
POSTGRES_USER: blog_user
POSTGRES_PASSWORD: super_secret_password
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres_data:/var/lib/postgresql/data
# Mount init script nếu cần
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432" # Expose để có thể kết nối từ bên ngoài (dev purpose)
networks:
- blog-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U blog_user -d blog_db"]
interval: 30s
timeout: 10s
retries: 3
# 🗄️ Cache - Redis
redis:
image: redis:6-alpine
container_name: blog-redis
restart: unless-stopped
command: redis-server --appendonly yes --requirepass redis_password
volumes:
- redis_data:/data
ports:
- "6379:6379"
networks:
- blog-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
# 🛠️ Backend API - Node.js
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: blog-backend
restart: unless-stopped
environment:
NODE_ENV: production
DATABASE_URL: postgresql://blog_user:super_secret_password@database:5432/blog_db
REDIS_URL: redis://:redis_password@redis:6379
JWT_SECRET: your_jwt_secret_here
PORT: 5000
volumes:
# Mount source code khi development
- ./backend:/usr/src/app
- /usr/src/app/node_modules # Prevent overwriting node_modules
ports:
- "5000:5000"
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
networks:
- blog-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 30s
timeout: 10s
retries: 3
# 🎨 Frontend - React
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
args:
REACT_APP_API_URL: http://localhost:5000/api
container_name: blog-frontend
restart: unless-stopped
volumes:
# Development mode
- ./frontend:/usr/src/app
- /usr/src/app/node_modules
ports:
- "3000:3000"
depends_on:
- backend
networks:
- blog-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
# 🌐 Reverse Proxy - Nginx
nginx:
image: nginx:alpine
container_name: blog-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443" # HTTPS nếu có SSL
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro # SSL certificates
- nginx_logs:/var/log/nginx
depends_on:
- frontend
- backend
networks:
- blog-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
# 🌐 Networks
networks:
blog-network:
driver: bridge
name: blog-network
# 💾 Volumes
volumes:
postgres_data:
name: blog-postgres-data
redis_data:
name: blog-redis-data
nginx_logs:
name: blog-nginx-logs
3.3. Dockerfile cho Backend (Node.js)
# backend/Dockerfile
FROM node:16-alpine
# Tạo app directory
WORKDIR /usr/src/app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# Change ownership
RUN chown -R nextjs:nodejs /usr/src/app
USER nextjs
# Expose port
EXPOSE 5000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
# Start application
CMD ["npm", "start"]
3.4. Dockerfile cho Frontend (React)
# frontend/Dockerfile
# Multi-stage build cho production
FROM node:16-alpine AS builder
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM nginx:alpine
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 3000
CMD ["nginx", "-g", "daemon off;"]
3.5. Cấu hình Nginx
# nginx/nginx.conf
events {
worker_connections 1024;
}
http {
upstream backend {
server backend:5000;
}
upstream frontend {
server frontend:3000;
}
server {
listen 80;
server_name localhost;
# Serve frontend
location / {
proxy_pass http://frontend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Proxy API requests to backend
location /api/ {
proxy_pass http://backend/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
}
🎮 Các lệnh Docker Compose quan trọng
4.1. Lệnh cơ bản - "Bộ tứ siêu anh hùng"
# 🚀 Start tất cả services (background mode)
docker-compose up -d
# 📊 Xem trạng thái các services
docker-compose ps
# 📝 Xem logs của tất cả services
docker-compose logs
# 🛑 Stop tất cả services
docker-compose down
4.2. Lệnh nâng cao - "Ninja skills"
# 🔨 Build lại images trước khi start
docker-compose up --build
# 🧹 Stop và xóa volumes (CẢNH BÁO: sẽ mất data!)
docker-compose down -v
# 📊 Scale service (chạy nhiều instance)
docker-compose up --scale backend=3
# 🔍 Xem logs của service cụ thể
docker-compose logs backend
# 🔄 Restart service cụ thể
docker-compose restart backend
# 🚪 Exec vào container
docker-compose exec backend bash
# 📦 Pull images mới nhất
docker-compose pull
# ⚡ Start services theo thứ tự phụ thuộc
docker-compose up --no-deps backend
4.3. Troubleshooting commands
# 🔍 Debug network issues
docker-compose exec backend ping database
# 📊 Check resource usage
docker-compose top
# 🗂️ Xem config được render
docker-compose config
# 🧪 Validate file docker-compose.yml
docker-compose config --quiet
# 📋 List tất cả networks
docker network ls | grep $(basename $PWD)
# 💾 List volumes
docker volume ls | grep $(basename $PWD)
🎯 Các patterns hay sử dụng
5.1. Environment Variables với .env file
Tạo file .env
:
# .env
# Database settings
POSTGRES_DB=blog_db
POSTGRES_USER=blog_user
POSTGRES_PASSWORD=super_secret_password
# Redis settings
REDIS_PASSWORD=redis_password
# Backend settings
NODE_ENV=production
JWT_SECRET=your_jwt_secret_here
# Frontend settings
REACT_APP_API_URL=http://localhost:5000/api
Sử dụng trong docker-compose.yml:
services:
database:
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
5.2. Multiple environments
# Development
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
# Production
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
# Testing
docker-compose -f docker-compose.yml -f docker-compose.test.yml up
docker-compose.dev.yml:
version: '3.8'
services:
backend:
volumes:
- ./backend:/usr/src/app
environment:
NODE_ENV: development
command: npm run dev # Hot reload
frontend:
volumes:
- ./frontend:/usr/src/app
command: npm start # Development server
docker-compose.prod.yml:
version: '3.8'
services:
backend:
restart: always
environment:
NODE_ENV: production
frontend:
restart: always
# Thêm monitoring trong production
monitoring:
image: prom/prometheus
ports:
- "9090:9090"
5.3. Health checks và dependencies
services:
database:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
backend:
depends_on:
database:
condition: service_healthy # Đợi DB healthy mới start
redis:
condition: service_started # Chỉ cần Redis start là được
🛠️ Ví dụ cho các framework khác
6.1. Full-stack JavaScript (MEAN Stack)
version: '3.8'
services:
# MongoDB
mongodb:
image: mongo:5
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: password
volumes:
- mongo_data:/data/db
ports:
- "27017:27017"
# Express.js Backend
backend:
build: ./backend
environment:
MONGODB_URI: mongodb://admin:password@mongodb:27017/myapp?authSource=admin
NODE_ENV: production
depends_on:
- mongodb
ports:
- "5000:5000"
# Angular Frontend
frontend:
build: ./frontend
ports:
- "4200:4200"
depends_on:
- backend
volumes:
mongo_data:
6.2. Spring Boot + MySQL + Redis
version: '3.8'
services:
# MySQL Database
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: springapp
MYSQL_USER: springuser
MYSQL_PASSWORD: springpass
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
# Redis Cache
redis:
image: redis:6-alpine
ports:
- "6379:6379"
# Spring Boot App
app:
build: .
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/springapp
SPRING_DATASOURCE_USERNAME: springuser
SPRING_DATASOURCE_PASSWORD: springpass
SPRING_REDIS_HOST: redis
SPRING_REDIS_PORT: 6379
depends_on:
- mysql
- redis
volumes:
mysql_data:
📚 Best Practices - "Bí kíp thành công"
7.1. Tổ chức code và config
# Cấu trúc thư mục recommended
my-project/
├── docker-compose.yml # Main compose file
├── docker-compose.dev.yml # Development overrides
├── docker-compose.prod.yml # Production overrides
├── .env # Environment variables
├── .env.example # Template cho team members
├── .dockerignore # Ignore files khi build
├── services/
│ ├── frontend/
│ │ ├── Dockerfile
│ │ └── nginx.conf
│ ├── backend/
│ │ └── Dockerfile
│ └── database/
│ └── init.sql
└── volumes/ # Local volumes (gitignore)
├── postgres/
└── redis/
7.2. Security best practices
# ✅ Tốt
services:
database:
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
# Không expose port ra ngoài trừ khi cần thiết
# ports:
# - "5432:5432" # Bỏ dòng này!
secrets:
db_password:
file: ./secrets/db_password.txt
# ❌ Không tốt
services:
database:
environment:
POSTGRES_PASSWORD: hardcoded_password # Nguy hiểm!
ports:
- "5432:5432" # Expose DB ra internet!
7.3. Performance tips
services:
app:
# Sử dụng init để handle signals đúng cách
init: true
# Giới hạn resources
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
# Logging configuration
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
7.4. Development workflow
# Script cho development workflow
#!/bin/bash
# scripts/dev.sh
echo "🚀 Starting development environment..."
# Pull latest images
docker-compose pull
# Build services
docker-compose build
# Start services
docker-compose up -d
# Show logs
docker-compose logs -f
🎊 Tổng kết
Docker Compose thực sự là một "phép màu" giúp việc quản lý multi-container application trở nên đơn giản và thú vị! 🎭
Những điều quan trọng cần nhớ:
- File docker-compose.yml là "kịch bản" điều phối tất cả
- Services là các container, networks kết nối chúng, volumes lưu trữ data
- Environment variables giúp config linh hoạt cho nhiều môi trường
- Health checks và depends_on đảm bảo services start đúng thứ tự
- Multiple compose files cho development/production environments
Hẹn gặp lại các bạn trong những phần tiếp theo! Chúc các bạn code vui vẻ và container luôn "up" thành công! 🐳✨
P/S: Có thắc mắc gì về Docker Compose thì đừng ngại liên hệ tôi nhé! Tôi sẽ cố gắng trả lời sớm nhất có thể! 😊