Docker cơ bản — Container hóa ứng dụng
Docker giải quyết vấn đề gì?
Câu chuyện quen thuộc trong mọi dự án:
"Trên máy tôi chạy được mà!"
Docker đóng gói ứng dụng cùng toàn bộ dependencies vào một container — chạy giống nhau trên mọi máy.
Các khái niệm cốt lõi
| Khái niệm | Tương tự | Mô tả |
|---|---|---|
| Dockerfile | Công thức nấu ăn | Hướng dẫn từng bước tạo Image |
| Image | Class trong OOP | Bản thiết kế, bất biến, dùng lại được |
| Container | Object (instance) | Image đang chạy, có thể tạo nhiều cái |
| Registry | npm / App Store | Kho lưu và chia sẻ Image |
| Volume | Ổ đĩa gắn ngoài | Lưu dữ liệu bền vững ngoài container |
Cài đặt và lệnh cơ bản
Kiểm tra cài đặt
docker --version
docker compose version
# Chạy container test
docker run hello-world
Làm việc với Image
# Tải image từ Docker Hub
docker pull node:18-alpine
docker pull nginx:latest
# Xem image đã có trên máy
docker images
# Xóa image
docker rmi node:18-alpine
# Tìm kiếm image
docker search nginx
Làm việc với Container
# Chạy container (tải image nếu chưa có)
docker run nginx
# Chạy ngầm (detached mode)
docker run -d nginx
# Chạy với đặt tên
docker run -d --name my-nginx nginx
# Chạy với map port: máy_host:container
docker run -d -p 8080:80 --name my-nginx nginx
# → Truy cập http://localhost:8080
# Xem container đang chạy
docker ps
# Xem tất cả container (kể cả đã dừng)
docker ps -a
# Dừng container
docker stop my-nginx
# Xóa container
docker rm my-nginx
# Xem log
docker logs my-nginx
docker logs -f my-nginx # follow log real-time
# Vào bên trong container
docker exec -it my-nginx bash
docker exec -it my-nginx sh # Nếu không có bash
Viết Dockerfile
Cấu trúc cơ bản
# 1. Base image
FROM node:18-alpine
# 2. Thư mục làm việc trong container
WORKDIR /app
# 3. Copy file package trước (tận dụng cache layer)
COPY package*.json ./
# 4. Cài dependencies
RUN npm install --production
# 5. Copy toàn bộ source code
COPY . .
# 6. Build (nếu cần)
RUN npm run build
# 7. Khai báo port container sẽ lắng nghe
EXPOSE 3000
# 8. Lệnh chạy khi container khởi động
CMD ["node", "src/index.js"]
Bài toán thực tế: Dockerize ứng dụng Node.js + React
Giả sử bạn có dự án:
my-app/
backend/ ← Node.js API
frontend/ ← React app
docker-compose.yml
Dockerfile cho Backend:
FROM node:18-alpine
WORKDIR /app
# Tách riêng bước install để tận dụng Docker cache layer
# → Chỉ re-install khi package.json thay đổi
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 4000
CMD ["node", "server.js"]
Dockerfile cho Frontend:
# Stage 1: Build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Stage 2: Serve (image nhỏ hơn nhiều)
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Multi-stage Build
Kỹ thuật dùng nhiều FROM — build ở stage 1 (image lớn, nhiều tool), copy artifact sang stage 2 (image nhỏ, chỉ có runtime). Image cuối có thể nhỏ hơn 10 lần.
.dockerignore
Tương tự .gitignore — tránh copy file thừa vào image:
node_modules
.git
.env
*.log
dist
coverage
Volume — Lưu dữ liệu bền vững
Container bị xóa thì dữ liệu bên trong mất theo. Volume giải quyết vấn đề này.
# Tạo volume
docker volume create mydb-data
# Chạy container với volume
docker run -d \
--name mongodb \
-v mydb-data:/data/db \
-p 27017:27017 \
mongo:6
# Xem danh sách volume
docker volume ls
# Xóa volume
docker volume rm mydb-data
# Mount thư mục máy host (bind mount) — dùng khi dev
docker run -d \
-v $(pwd)/src:/app/src \ # thay đổi code → tự reload
-p 3000:3000 \
my-app
Docker Compose — Quản lý nhiều container
Thay vì gõ lệnh docker run dài dòng cho từng service, Docker Compose dùng file YAML.
docker-compose.yml:
version: '3.8'
services:
# MongoDB
mongodb:
image: mongo:6
container_name: app-mongodb
restart: unless-stopped
volumes:
- mongodb-data:/data/db
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: secret
ports:
- "27017:27017"
# Backend API
backend:
build: ./backend # Build từ Dockerfile trong ./backend
container_name: app-backend
restart: unless-stopped
depends_on:
- mongodb # Đợi mongodb khởi động trước
environment:
NODE_ENV: production
MONGO_URI: mongodb://admin:secret@mongodb:27017/myapp
ports:
- "4000:4000"
# Frontend
frontend:
build: ./frontend
container_name: app-frontend
restart: unless-stopped
depends_on:
- backend
ports:
- "80:80"
volumes:
mongodb-data: # Khai báo volume dùng chung
Các lệnh Docker Compose hay dùng
# Khởi động tất cả service (build nếu chưa có image)
docker compose up -d
# Build lại image trước khi chạy
docker compose up -d --build
# Xem log tất cả service
docker compose logs -f
# Xem log 1 service cụ thể
docker compose logs -f backend
# Dừng tất cả
docker compose down
# Dừng và xóa luôn volume (⚠️ mất data)
docker compose down -v
# Restart 1 service
docker compose restart backend
# Vào shell trong service
docker compose exec backend sh
# Xem trạng thái các service
docker compose ps
Workflow phát triển với Docker
Cheat sheet nhanh
| Việc cần làm | Lệnh |
|---|---|
| Chạy container | docker run -d -p 8080:80 nginx |
| Xem container đang chạy | docker ps |
| Xem log | docker logs -f <tên> |
| Vào trong container | docker exec -it <tên> sh |
| Dừng container | docker stop <tên> |
| Xóa container | docker rm <tên> |
| Build image | docker build -t my-app . |
| Xem image | docker images |
| Khởi động Compose | docker compose up -d |
| Dừng Compose | docker compose down |
| Dọn rác (image cũ) | docker system prune |