Docker Compose là cách nhanh nhất để dev và team có cùng môi trường (Postgres, Redis, app) chỉ với 1 lệnh docker compose up. Bài này đi qua compose.yml production-grade từ A-Z.
Vì sao cần Compose?
Setup local thuần: cài Postgres, Redis, Node.js, config từng cái — mỗi dev mới mất 1 ngày, version khác nhau, "works on my machine". Compose: 1 file, 1 lệnh, ai pull về cũng chạy.
compose.yml cơ bản
services:
app:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://postgres:secret@db:5432/alodev
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: alodev
volumes:
- pg-data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pg-data:
Chạy:
docker compose up -d # background
docker compose logs -f app # tail log
docker compose down # stop + remove
docker compose down -v # cũng xoá volume (cẩn thận!)
Dev mode: hot reload qua bind mount
services:
app:
build:
context: .
target: dev # multi-stage Dockerfile có stage 'dev'
volumes:
- .:/app # bind mount source code
- /app/node_modules # anonymous volume — không overlap với host node_modules
command: npm run dev
environment:
NODE_ENV: development
Sửa code trên host → file system event → nodemon reload. Anonymous volume trick: /app/node_modules không bị bind mount cover — container dùng deps đã npm ci trong build.
env_file để tách secret
services:
app:
env_file:
- .env.local
environment:
NODE_ENV: development # override env_file
# .env.local (gitignored)
DATABASE_URL=postgres://postgres:secret@db:5432/alodev
JWT_SECRET=dev-secret-thay-cho-production
SENDGRID_API_KEY=SG.test-key
Tách dev và prod: compose.override.yml
# compose.yml — base
services:
app:
image: alodev/api:latest
environment:
NODE_ENV: production
# compose.override.yml — auto-loaded khi docker compose up
services:
app:
build: . # build local thay vì pull image
volumes:
- .:/app
command: npm run dev
Production không có override file → dùng image từ registry. Dev có override → build local, mount code.
Profile cho stack lớn
services:
app:
# ... luôn chạy
db:
# ... luôn chạy
monitoring:
image: prom/prometheus
profiles: ["monitoring"]
grafana:
image: grafana/grafana
profiles: ["monitoring"]
mailhog:
image: mailhog/mailhog
profiles: ["test"]
docker compose up -d # chỉ app + db
docker compose --profile monitoring up -d # + prometheus + grafana
docker compose --profile test up -d # + mailhog
Network và service discovery
Mặc định Compose tạo 1 network, mọi service join. Service resolve nhau bằng tên: db:5432, cache:6379. Không cần IP.
Nếu cần tách network (ví dụ frontend không truy cập db trực tiếp):
services:
api: { networks: [frontend, backend] }
web: { networks: [frontend] }
db: { networks: [backend] }
networks:
frontend: {}
backend: {}
Healthcheck đúng cho từng service
services:
db:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d alodev"]
interval: 5s
timeout: 3s
retries: 5
cache:
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 2s
app:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/healthz"]
interval: 10s
timeout: 5s
start_period: 30s
start_period = grace period — không tính fail trong 30s đầu (app chưa start xong).
Compose trong CI
# .github/workflows/test.yml
- name: Start services
run: docker compose up -d db cache
- name: Wait for db
run: until docker compose exec -T db pg_isready; do sleep 1; done
- name: Run tests
run: docker compose run --rm app npm test
- name: Cleanup
if: always()
run: docker compose down -v
Test integration với DB thực — không phải mock. Đọc thêm Integration testing.
Kết luận
Docker Compose là tool dev environment tốt nhất cho team Node.js/Python/Go. 1 lệnh docker compose up = full stack chạy. Khi production scale lớn hơn 1 server, anh sẽ migrate sang Kubernetes — nhưng giai đoạn đầu Compose là vừa đủ.