Thiết kế Docker compose chuẩn Production
Hướng dẫn thực chiến xây dựng docker-compose.yml hoàn chỉnh cho Production: healthchecks, restart policies, resource limits, quản lí .env và Persistent Volumes

Tự học Docker từ A-Z
Giới thiệu: Chuyển dịch từ Development sang Production với Docker Compose
Docker Compose là công cụ tuyệt vời giúp khởi chạy cụm ứng dụng đa container (Multi-container Architecture) chỉ bằng một lệnh duy nhất: docker compose up -d.
Tuy nhiên, một file docker-compose.yml viết cho môi trường phát triển (Local Development) hoàn toàn khác biệt so với một file được thiết kế để vận hành thực tế trên máy chủ Production. Trong môi trường Production, bạn phải đối mặt với các bài toán:
- Làm sao để container tự phục hồi khi bị crash?
- Làm sao ngăn một container bị rò rỉ bộ nhớ (Memory Leak) làm sập toàn bộ máy chủ VPS?
- Làm sao đảm bảo cơ sở dữ liệu (Database) đã sẵn sàng trước khi ứng dụng backend cố gắng kết nối?
- Làm sao để dữ liệu không bao giờ bị mất mát khi container bị xóa bỏ hoặc cập nhật bản mới?
Bài viết này sẽ hướng dẫn bạn thiết kế một cấu trúc Docker Compose hoàn chỉnh chuẩn Production cho một hệ thống Web điển hình gồm: Node.js API + PostgreSQL + Redis Cache.
1. Cấu trúc thư mục dự án chuẩn hóa
Một cấu trúc thư mục rõ ràng giúp việc quản trị và sao lưu trở nên thuận tiện hơn bao giờ hết:
/opt/my-app/
├── .env.example # Mẫu biến môi trường (cam kết vào Git)
├── .env.production # Biến môi trường bảo mật thực tế (KHÔNG cam kết vào Git)
├── docker-compose.yml # File compose chính
├── nginx/
│ └── default.conf # File cấu hình proxy
└── backups/ # Thư mục lưu trữ backup định kỳ
2. File Docker Compose hoàn chỉnh chuẩn Production
Dưới đây là file docker-compose.yml mẫu đã được áp dụng toàn bộ các tiêu chuẩn vận hành tốt nhất:
version: '3.8'
services:
# ==========================================
# 1. Backend Application Service (Node.js)
# ==========================================
api:
image: my-company/backend:v1.4.2
container_name: production_api
restart: unless-stopped
env_file:
- .env.production
environment:
- NODE_ENV=production
- PORT=3000
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app_network
# Chỉ bind cổng vào localhost nội bộ (Nginx Reverse Proxy sẽ kết nối tới đây)
ports:
- "127.0.0.1:3000:3000"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
cpus: '1.5'
memory: 1024M
reservations:
cpus: '0.25'
memory: 256M
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# ==========================================
# 2. Database Service (PostgreSQL 16)
# ==========================================
db:
image: postgres:16-alpine
container_name: production_postgres
restart: unless-stopped
env_file:
- .env.production
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- app_network
# Không mở port ra ngoài Internet, chỉ giao tiếp nội bộ trong mạng app_network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
deploy:
resources:
limits:
cpus: '2.0'
memory: 2048M
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# ==========================================
# 3. In-memory Cache Service (Redis 7)
# ==========================================
redis:
image: redis:7-alpine
container_name: production_redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
volumes:
- redisdata:/data
networks:
- app_network
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
logging:
driver: "json-file"
options:
max-size: "5m"
max-file: "2"
# ==========================================
# Persistent Volumes (Dữ liệu bền vững)
# ==========================================
volumes:
pgdata:
name: production_pgdata
redisdata:
name: production_redisdata
# ==========================================
# Isolated Network (Mạng nội bộ cách ly)
# ==========================================
networks:
app_network:
name: production_network
driver: bridge
3. Phân tích các nguyên tắc then chốt trong cấu hình Production
A. Cơ chế Khởi động lại (Restart Policy)
restart: unless-stopped: Đảm bảo container sẽ luôn tự động khởi động lại khi bị crash hoặc khi máy chủ VPS khởi động lại sau bảo trì, trừ khi bạn cố ý gõ lệnh dừng thủ công (docker compose stop).
B. Healthcheck & Khởi động tuần tự có điều kiện (service_healthy)
Trong quá khứ, lệnh depends_on: ['db'] chỉ chờ container database được bật lên chứ không biết database đã thực sự nạp xong dữ liệu hay chưa.
Bằng cách định nghĩa healthcheck trên service db và sử dụng:
depends_on:
db:
condition: service_healthy
Container api sẽ chỉ được khởi động khi lệnh pg_isready trả về mã thành công, loại bỏ hoàn toàn lỗi "Database connection refused" khi vừa reboot.
C. Giới hạn tài nguyên phần cứng (Resource Limits)
Ngăn chặn một container bị lỗi rò rỉ bộ nhớ (Memory Leak) tiêu thụ 100% RAM của VPS dẫn đến cơ chế OOM Killer của Linux tiêu diệt các tiến trình quan trọng khác:
limits.memory: 1024M: Container chỉ được phép dùng tối đa 1GB RAM.limits.cpus: '1.5': Container chỉ được phép dùng tối đa 1.5 nhân CPU.
D. Giới hạn dung lượng Log (Logging Driver)
Mặc định Docker ghi lại mọi output stdout/stderr không giới hạn, rất dễ làm đầy 100% ổ cứng (Disk full).
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Cấu hình này đảm bảo mỗi container không bao giờ chiếm quá 30MB dung lượng lưu trữ log.
4. Quản lý biến môi trường bảo mật (.env.production)
Tạo file .env.production và cấp quyền bảo mật chỉ cho user hiện tại được đọc:
touch .env.production
chmod 600 .env.production
nano .env.production
Nội dung file:
DB_NAME=core_production
DB_USER=pgadmin
DB_PASSWORD=SuperSecretPassword2024!
REDIS_PASSWORD=AnotherVeryStrongPassword2024!
JWT_SECRET=your-random-jwt-token-string-here
!CAUTION Luôn thêm file
.env.productionvào danh sách.gitignoređể không bao giờ vô tình đẩy mật khẩu lên GitHub hoặc GitLab.
5. Quy trình Vận hành và Triển khai (Deployment Workflow)
Khi triển khai phiên bản mới hoặc cập nhật cấu hình:
# 1. Kéo image mới nhất về máy
docker compose --env-file .env.production pull
# 2. Khởi chạy lại container ở chế độ nền (Zero/Minimal Downtime)
docker compose --env-file .env.production up -d --remove-orphans
# 3. Kiểm tra trạng thái sức khỏe (Health status)
docker compose ps
# 4. Xem log theo dõi thời gian thực
docker compose logs -f --tail=100 api
Tổng kết
Một file Docker Compose được thiết kế chuẩn Production không chỉ giúp hệ thống chạy ổn định mà còn giúp các kĩ sư vận hành dễ dàng chẩn đoán lỗi, nâng cấp phiên bản và mở rộng qui mô một cách an toàn và tự tin.



Đăng nhập bằng tài khoản GitHub để đặt câu hỏi hoặc đóng góp ý kiến cho bài viết.