Appearance
Buổi 5: Docker Compose
🎯 Mục tiêu
- Hiểu Docker Compose là gì và tại sao cần dùng
- Viết file docker-compose.yml chuẩn
- Quản lý multi-container applications
- Sử dụng depends_on, profiles, healthcheck
- Thực hành: Full-stack app (Frontend + Backend + Database)
1. Tại sao cần Docker Compose?
Vấn đề khi dùng docker run
bash
# Phải chạy từng lệnh riêng lẻ, rất dài...
$ docker network create myapp-net
$ docker run -d --name db --network myapp-net \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16
$ docker run -d --name redis --network myapp-net \
redis:7-alpine
$ docker run -d --name api --network myapp-net \
-e DATABASE_URL=postgres://postgres:secret@db:5432/postgres \
-e REDIS_URL=redis://redis:6379 \
-p 3000:3000 \
my-api
$ docker run -d --name web --network myapp-net \
-e API_URL=http://api:3000 \
-p 8080:80 \
my-frontend😩 4 container = 4 lệnh dài + 1 lệnh network + phải nhớ thứ tự khởi động.
Giải pháp: Docker Compose
yaml
# docker-compose.yml – MỘT file, MỘT lệnh
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
api:
build: ./api
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://postgres:secret@db:5432/postgres
REDIS_URL: redis://redis:6379
depends_on:
- db
- redis
web:
build: ./frontend
ports:
- "8080:80"
depends_on:
- api
volumes:
pgdata:bash
# Một lệnh duy nhất!
$ docker compose up -d2. Cấu trúc docker-compose.yml
Cấu trúc cơ bản
yaml
# Phiên bản (optional với Compose v2+)
# version: "3.8" # không cần nữa
# Định nghĩa các services (containers)
services:
service-name:
image: image:tag # Hoặc build từ Dockerfile
build: ./path # Build từ Dockerfile
ports:
- "host:container"
environment:
KEY: value
volumes:
- volume:/path
depends_on:
- other-service
restart: unless-stopped
# Định nghĩa volumes
volumes:
volume-name:
# Định nghĩa networks
networks:
network-name:Các trường quan trọng
| Trường | Ý nghĩa | Ví dụ |
|---|---|---|
image | Image từ registry | postgres:16 |
build | Build từ Dockerfile | ./api hoặc {context: ., dockerfile: Dockerfile} |
ports | Port mapping | "3000:3000" |
environment | Biến môi trường | NODE_ENV: production |
env_file | File biến môi trường | .env |
volumes | Mount volumes | pgdata:/var/lib/postgresql/data |
depends_on | Thứ tự khởi động | [db, redis] |
restart | Chính sách restart | unless-stopped |
networks | Chọn networks | [frontend, backend] |
command | Override CMD | npm run dev |
healthcheck | Kiểm tra sức khỏe | test: curl -f http://localhost |
3. Docker Compose CLI
Các lệnh cơ bản
bash
# Khởi động tất cả services (foreground)
$ docker compose up
# Khởi động nền (detached)
$ docker compose up -d
# Build lại images trước khi khởi động
$ docker compose up -d --build
# Dừng tất cả
$ docker compose down
# Dừng và XÓA volumes (⚠️ mất dữ liệu)
$ docker compose down -v
# Xem logs
$ docker compose logs
$ docker compose logs -f api # follow logs của service "api"
# Xem trạng thái
$ docker compose ps
# Chạy lệnh trong service
$ docker compose exec api bash
$ docker compose exec db psql -U postgres
# Scale service
$ docker compose up -d --scale api=3
# Restart một service
$ docker compose restart api
# Pull images mới nhất
$ docker compose pullSo sánh docker compose up và down
| Lệnh | Tạo | Chạy | Dừng | Xóa Container | Xóa Network | Xóa Volume |
|---|---|---|---|---|---|---|
up -d | ✅ | ✅ | ||||
stop | ✅ | |||||
down | ✅ | ✅ | ✅ | |||
down -v | ✅ | ✅ | ✅ | ✅ |
4. depends_on nâng cao
depends_on cơ bản
yaml
services:
api:
depends_on:
- db
- redis⚠️ depends_on chỉ đảm bảo thứ tự khởi động, KHÔNG đảm bảo service kia đã sẵn sàng.
depends_on với healthcheck
yaml
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
api:
build: ./api
depends_on:
db:
condition: service_healthy
# API chỉ start KHI db đã healthy ✅5. Profiles
Tách môi trường dev và production
yaml
services:
api:
build: ./api
ports:
- "3000:3000"
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
# Chỉ chạy khi bật profile "dev"
adminer:
image: adminer
ports:
- "8080:8080"
profiles:
- dev
# Chỉ chạy khi bật profile "monitoring"
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
profiles:
- monitoringbash
# Chạy bình thường (không có adminer, prometheus)
$ docker compose up -d
# Chạy kèm adminer (dev profile)
$ docker compose --profile dev up -d
# Chạy kèm monitoring
$ docker compose --profile monitoring up -d
# Chạy tất cả profiles
$ docker compose --profile dev --profile monitoring up -d6. Environment variables trong Compose
Các cách truyền biến
yaml
services:
api:
image: my-api
# Cách 1: Inline
environment:
NODE_ENV: production
PORT: 3000
# Cách 2: Từ file .env
env_file:
- .env
- .env.local
# Cách 3: Từ biến host (${VAR})
environment:
DATABASE_URL: postgres://postgres:${DB_PASSWORD}@db:5432/${DB_NAME}File .env mặc định
Docker Compose tự động đọc file .env ở cùng thư mục:
env
# .env
DB_PASSWORD=secret
DB_NAME=myapp
API_PORT=3000yaml
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
api:
build: ./api
ports:
- "${API_PORT}:3000"7. Thực hành: Full-stack Application
Kiến trúc
Browser
│
:80 │ :8080
┌─────┴──────────────────────┐
│ │
▼ ▼
┌─────────┐ ┌────────────┐
│ Frontend│ │ Adminer │
│ (React) │ │ (DB Admin) │
│ nginx │ │ profile: │
│ │ │ dev │
└────┬────┘ └────────────┘
│ API calls
▼
┌─────────┐
│ API │
│ (Node) │
│ :3000 │
└────┬────┘
│
├──────────────┐
▼ ▼
┌─────────┐ ┌───────────┐
│ DB │ │ Redis │
│ Postgres│ │ Cache │
│ :5432 │ │ :6379 │
└─────────┘ └───────────┘docker-compose.yml
yaml
services:
# ── Database ──────────────────────────────
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d myapp"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
# ── Cache ─────────────────────────────────
redis:
image: redis:7-alpine
command: redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped
# ── API Server ────────────────────────────
api:
build:
context: ./api
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
NODE_ENV: production
DATABASE_URL: postgres://app:${DB_PASSWORD:-secret}@db:5432/myapp
REDIS_URL: redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
# ── Frontend ──────────────────────────────
web:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "80:80"
depends_on:
- api
restart: unless-stopped
# ── Dev tools (chỉ bật khi cần) ──────────
adminer:
image: adminer
ports:
- "8080:8080"
depends_on:
- db
profiles:
- dev
volumes:
pgdata:Các lệnh vận hành
bash
# Khởi động production
$ docker compose up -d
# Khởi động dev (kèm adminer)
$ docker compose --profile dev up -d
# Xem logs API
$ docker compose logs -f api
# Truy cập database
$ docker compose exec db psql -U app -d myapp
# Rebuild API sau khi sửa code
$ docker compose up -d --build api
# Dừng toàn bộ
$ docker compose down
# Dừng + xóa data (reset hoàn toàn)
$ docker compose down -v🏋️ Bài tập thực hành
Bài 1: Compose cơ bản
- Tạo file
docker-compose.ymlvới nginx + redis - Chạy
docker compose up -d - Kiểm tra
docker compose ps - Xem logs
docker compose logs - Dừng
docker compose down
Bài 2: WordPress stack bằng Compose
- Viết
docker-compose.ymlcho WordPress + MySQL (thay thế các lệnh docker run ở Buổi 4) - Thêm Adminer với profile
dev - Chạy và setup WordPress
Bài 3: Healthcheck
- Thêm healthcheck cho MySQL service
- Cấu hình depends_on với
condition: service_healthy - Kiểm tra trạng thái:
docker compose ps
Bài 4: Environment variables
- Tạo file
.envvới các biến cấu hình - Sử dụng
${VAR}trong docker-compose.yml - Thử thay đổi giá trị trong
.envvà restart