Docker cho Nhà Phát Triển: Containers, Compose và Quy Trình Làm Việc Thực Tế
Hướng dẫn thực hành về các khái niệm Docker, các phương pháp tốt nhất với Dockerfile, và cách sử dụng Docker Compose để chạy môi trường phát triển đa dịch vụ.
"Nó chạy được trên máy mình mà" — câu nói này đã gây ra vô số lần triển khai thất bại. Docker giải quyết vấn đề đó bằng cách đóng gói ứng dụng của bạn cùng toàn bộ các phụ thuộc vào một container — một môi trường độc lập, có thể tái tạo, chạy giống hệt nhau ở mọi nơi, từ laptop của bạn cho đến môi trường production.
Containers vs. máy ảo
| Máy ảo (Virtual Machine) | Container | |
|---|---|---|
| Cách ly | Toàn bộ OS | Cấp tiến trình |
| Thời gian khởi động | Vài phút | Vài giây |
| Kích thước | GBs | MBs |
| Tài nguyên tiêu thụ | Cao (hypervisor) | Gần như bằng không |
| Trường hợp dùng | Cách ly toàn bộ OS | Đóng gói ứng dụng |
Containers dùng chung kernel của máy chủ nhưng vẫn cách ly filesystem, tiến trình và mạng. Đó là lý do chúng rất nhẹ.
Các khái niệm cốt lõi trong Docker
Image — Bản thiết kế chỉ đọc để tạo container. Hãy nghĩ nó như một định nghĩa class.
Container — Một instance đang chạy của image. Hãy nghĩ nó như một object được tạo ra từ class đó.
Registry — Hệ thống lưu trữ và phân phối image. Docker Hub là registry công khai mặc định; GitHub Container Registry và AWS ECR là các lựa chọn phổ biến khác.
Volume — Bộ nhớ lưu trữ bền vững, tồn tại sau khi container khởi động lại. Dữ liệu ghi vào bên trong container mà không có volume sẽ bị mất khi container dừng lại.
Viết Dockerfile tốt
# Dùng phiên bản cụ thể — không bao giờ dùng "latest" trong production
FROM node:20-alpine
# Đặt thư mục làm việc
WORKDIR /app
# Copy file phụ thuộc trước (tận dụng layer caching)
COPY package.json package-lock.json ./
RUN npm ci --only=production
# Copy code ứng dụng
COPY . .
# Build ứng dụng
RUN npm run build
# Chạy với user không phải root để đảm bảo bảo mật
USER node
# Khai báo port (không publish nó)
EXPOSE 3000
# Dùng exec form để xử lý tín hiệu đúng cách
CMD ["node", "server.js"]
Layer caching: chìa khóa để build nhanh
Mỗi lệnh trong Dockerfile tạo ra một layer. Docker lưu cache các layer — nếu không có gì thay đổi ở một layer hay các layer trước đó, Docker sẽ tái sử dụng cache đó.
Copy file phụ thuộc trước khi copy code ứng dụng. Các phụ thuộc thay đổi ít hơn rất nhiều so với code của bạn. Nếu bạn copy tất cả cùng một lúc, chỉ một dòng thay đổi trong index.js cũng sẽ làm mất hiệu lực cache của bước cài đặt phụ thuộc và buộc phải chạy lại toàn bộ npm install.
Multi-stage builds
Dùng multi-stage builds để giữ cho image production gọn nhẹ:
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production (không có dev dependencies, không có source)
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/server.js"]
Image cuối cùng chỉ chứa output đã được build — không có source code, file test hay dev dependencies.
Docker Compose cho môi trường phát triển cục bộ
Docker Compose điều phối các ứng dụng đa container. Một file docker-compose.yml duy nhất định nghĩa toàn bộ stack cục bộ của bạn:
version: "3.9"
services:
app:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
depends_on:
db:
condition: service_healthy
volumes:
- .:/app
- /app/node_modules
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
postgres_data:
Tạo cấu trúc khởi đầu cho stack của bạn bằng Docker Compose Generator của chúng tôi — chọn các dịch vụ và nhận ngay file docker-compose.yml sẵn sàng cho production trong vài giây.
Các lệnh Compose cần thiết
# Khởi động tất cả dịch vụ ở chế độ nền
docker compose up -d
# Xem logs (chế độ theo dõi liên tục)
docker compose logs -f app
# Chạy lệnh bên trong container đang hoạt động
docker compose exec app sh
# Dừng và xóa containers (giữ lại volumes)
docker compose down
# Dừng, xóa containers VÀ volumes
docker compose down -v
# Rebuild images sau khi thay đổi Dockerfile
docker compose up -d --build
Biến môi trường và secrets
Không bao giờ hardcode thông tin xác thực trong Dockerfile hay file Compose. Hãy dùng file .env:
# .env (thêm vào .gitignore!)
DATABASE_URL=postgresql://user:pass@db:5432/myapp
REDIS_URL=redis://redis:6379
JWT_SECRET=your-super-secret-key
Compose tự động tải .env từ thư mục dự án. Tham chiếu các biến bằng cú pháp ${VARIABLE_NAME} trong file YAML của bạn.
Dùng Env Generator của chúng tôi để tạo file .env với các giá trị mặc định hợp lý và template .env.example cho cả nhóm.
Networking trong Docker
Compose tạo một mạng mặc định cho stack của bạn. Các dịch vụ có thể kết nối với nhau bằng cách dùng tên dịch vụ làm hostname — đó là lý do tại sao app dùng db làm host của cơ sở dữ liệu, không phải localhost.
app → kết nối đến "db:5432"
app → kết nối đến "redis:6379"
Chỉ những port được ánh xạ rõ ràng qua ports: mới có thể truy cập từ máy chủ.
Health checks và thứ tự khởi động
depends_on chỉ chờ container khởi động, chứ không chờ container sẵn sàng. Một container database khởi động trong vài giây, nhưng Postgres có thể cần thêm vài giây nữa để chấp nhận kết nối. Dùng healthcheck kết hợp với condition: service_healthy để chờ cho đến khi dịch vụ thực sự sẵn sàng.
Lưu ý khi triển khai production
- Dùng tag image cụ thể —
node:20.11.1-alpine, không phảinode:latest. - Quét image để phát hiện lỗ hổng bảo mật —
docker scout cves myapp:latest. - Đặt giới hạn tài nguyên — ngăn một container chiếm tài nguyên của các container khác.
- Dùng filesystem chỉ đọc khi có thể —
read_only: truetrong Compose. - Không bao giờ chạy với quyền root — thêm
USER nodehoặc tương đương. - Cấu hình Nginx làm reverse proxy trước ứng dụng của bạn — dùng Nginx Config Generator của chúng tôi để có cấu hình đã được kiểm chứng thực tế.
Tài liệu tham khảo nhanh
# Images
docker images # liệt kê các image cục bộ
docker pull nginx:alpine # tải image
docker rmi myimage # xóa image
# Containers
docker ps # các container đang chạy
docker ps -a # tất cả các container
docker stop <id> # dừng container một cách an toàn
docker rm <id> # xóa container đã dừng
docker logs <id> -f # xem logs liên tục
# Dọn dẹp
docker system prune # xóa tất cả những gì không dùng đến
docker volume prune # xóa các volume không dùng đến
Docker loại bỏ hoàn toàn một lớp lỗi liên quan đến môi trường và giúp việc onboarding cho nhà phát triển mới trở nên cực kỳ đơn giản. Nắm vững những kiến thức cơ bản ở đây và bạn sẽ có nền tảng vững chắc cho Kubernetes, CI/CD pipelines và triển khai lên cloud.