Docker là gì? Image, container, volume và Docker Compose
Hướng dẫn Docker từ cơ bản: container hoạt động thế nào, image và container khác gì, Dockerfile, volume, network và Docker Compose.
Mục lục bài viết
VPS & Hosting cho dự án của bạn
Khám phá các gói máy chủ và lưu trữ phù hợp từ website cá nhân đến ứng dụng production.
Docker là một trong những công cụ phổ biến nhất khi developer bắt đầu học deploy và DevOps. Tuy nhiên, nhiều người mới chỉ nhớ một vài lệnh như docker build và docker run mà chưa phân biệt rõ image, container, volume và network.
Bài viết này giải thích Docker từ mô hình nền tảng, sau đó đi qua một ví dụ đóng gói ứng dụng Node.js để bạn hiểu toàn bộ luồng từ source code đến container đang chạy.
Docker là gì?
Docker là một nền tảng và bộ công cụ dùng để đóng gói, phân phối và chạy ứng dụng trong container.
Theo tài liệu chính thức, Docker cho phép đóng gói và chạy ứng dụng trong môi trường được cô lập tương đối gọi là container. Container nhẹ và chứa những thành phần cần thiết để chạy ứng dụng, giúp giảm phụ thuộc vào phần mềm đã cài sẵn trên máy host.
Luồng cơ bản:
Source code
↓
Dockerfile
↓ docker build
Docker image
↓ docker run
Container đang chạy
Docker không phải chỉ là một câu lệnh. Hệ sinh thái Docker thường gồm:
- Docker Engine.
- Docker CLI.
- Dockerfile.
- Docker image.
- Docker container.
- Docker registry.
- Docker network.
- Docker volume.
- Docker Compose.
- Docker Desktop.
Vì sao Docker ra đời?
Một ứng dụng không chỉ có source code. Nó còn phụ thuộc vào:
- Phiên bản runtime.
- Thư viện hệ thống.
- Package.
- Biến môi trường.
- File cấu hình.
- Port.
- Quyền thư mục.
- Dịch vụ phụ trợ.
Ví dụ một dự án NestJS có thể cần:
Node.js 22
npm dependencies
PostgreSQL 18
Redis
Biến DATABASE_URL
Port 3000
Nếu developer A dùng Node.js 20, developer B dùng Node.js 22 và production dùng Node.js 18, kết quả có thể khác nhau.
Docker giúp mô tả môi trường chạy trong file có thể lưu cùng source code.
Thay vì hướng dẫn thủ công:
Cài Node.js
Cài đúng phiên bản npm
Cài package hệ thống
Tạo user
Copy source
Chạy lệnh
Bạn có thể xây một image rồi chạy image đó ở nhiều môi trường có Docker tương thích.
Docker giúp tăng tính nhất quán, nhưng không bảo đảm mọi môi trường giống nhau tuyệt đối. CPU architecture, kernel, volume, secret, network và tài nguyên vẫn có thể khác.
Container là gì?
Container là một process được chạy trong môi trường tách biệt tương đối với các process khác.
Container thường có:
- Filesystem riêng dựa trên image.
- Process riêng.
- Network namespace.
- Biến môi trường.
- Giới hạn tài nguyên nếu được cấu hình.
- Volume được mount từ bên ngoài.
Ví dụ:
docker run --name web -p 8080:80 nginx
Lệnh này:
- Tìm image
nginx. - Pull image nếu máy chưa có.
- Tạo container tên
web. - Chạy process Nginx trong container.
- Map port
8080của host tới port80trong container.
Truy cập:
http://localhost:8080
Image là gì?
Docker image là gói chuẩn hóa chứa các file, binary, thư viện và cấu hình cần thiết để tạo container.
Image có tính chất gần như bất biến. Khi cần thay đổi ứng dụng, thông thường bạn build image mới thay vì đăng nhập vào container rồi sửa thủ công.
Ví dụ:
node:22-alpine
postgres:18
nginx:alpine
redis:8
Một image có thể tạo ra nhiều container:
Image: my-api:1.0
├── Container api-1
├── Container api-2
└── Container api-3
Image và container khác nhau thế nào?
Có thể hình dung:
Image = bản thiết kế/khuôn đóng gói
Container = một phiên bản đang được tạo và chạy từ image
| Tiêu chí | Image | Container |
|---|---|---|
| Trạng thái | Gói chỉ đọc theo layer | Instance có lớp ghi |
| Tạo bằng | docker build hoặc docker pull | docker run hoặc docker create |
| Có chạy process | Không | Có khi ở trạng thái running |
| Có thể tạo nhiều bản | Có | Mỗi container là một instance |
| Lưu ở registry | Có | Không đẩy trực tiếp như container |
Docker Engine hoạt động thế nào?
Docker Engine theo mô hình client-server.
Các thành phần chính:
Docker client
Đây là lệnh docker bạn nhập:
docker build
docker run
docker ps
Client gửi yêu cầu qua Docker API.
Docker daemon
Daemon thường là dockerd. Nó chịu trách nhiệm:
- Build image.
- Tạo container.
- Quản lý network.
- Quản lý volume.
- Pull và push image.
Registry
Registry lưu Docker image.
Ví dụ:
- Docker Hub.
- GitHub Container Registry.
- GitLab Container Registry.
- Registry riêng.
Luồng:
docker pull
Registry ─────────→ Máy local
docker push
Máy local ────────→ Registry
Docker có phải máy ảo không?
Không. Container và virtual machine giải quyết một số bài toán tương tự về cô lập môi trường, nhưng hoạt động khác nhau.
Máy ảo
Mỗi VM thường có:
- Hệ điều hành guest.
- Kernel riêng.
- Virtual hardware.
- RAM được phân bổ.
Container
Container dùng chung kernel của host nhưng có không gian process, filesystem và network được cô lập.
Mô hình:
Máy ảo:
Phần cứng
└── Hypervisor
├── Guest OS
│ └── App
└── Guest OS
└── App
Container:
Phần cứng
└── Host OS + Kernel
└── Container runtime
├── Container App A
└── Container App B
Vì không cần mang theo toàn bộ guest OS, container thường khởi động nhanh và nhẹ hơn VM.
Tuy nhiên:
- Container không thay thế VM trong mọi trường hợp.
- Mức cô lập khác VM.
- Docker Desktop trên Windows và macOS có thể dùng lớp máy ảo bên dưới để chạy Linux container.
Dockerfile là gì?
Dockerfile là file văn bản chứa các chỉ dẫn để build image.
Ví dụ ứng dụng Node.js:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "run", "start"]
Ý nghĩa:
FROM
FROM node:22-alpine
Chọn base image.
WORKDIR
WORKDIR /app
Đặt thư mục làm việc bên trong image.
COPY
COPY package*.json ./
Copy file từ build context vào image.
RUN
RUN npm ci
Chạy lệnh trong lúc build image và tạo layer mới.
EXPOSE
EXPOSE 3000
Mô tả port ứng dụng dự kiến lắng nghe. EXPOSE không tự public port ra host.
CMD
CMD ["npm", "run", "start"]
Lệnh mặc định khi container khởi động.
Build image
Tại thư mục có Dockerfile:
docker build -t deployeasy-api:1.0 .
Trong đó:
docker build build image
-t đặt tên/tag
deployeasy-api:1.0 repository và tag
. build context hiện tại
Kiểm tra image:
docker image ls
Chạy container
docker run \
--name deployeasy-api \
-p 3000:3000 \
deployeasy-api:1.0
Port mapping:
Host : Container
3000 : 3000
Chạy nền:
docker run -d \
--name deployeasy-api \
-p 3000:3000 \
deployeasy-api:1.0
Xem container:
docker ps
Xem cả container đã dừng:
docker ps -a
Xem log:
docker logs -f deployeasy-api
Dừng:
docker stop deployeasy-api
Xóa:
docker rm deployeasy-api
Layer và cache khi build
Docker image được tạo từ nhiều layer. Cách sắp xếp Dockerfile ảnh hưởng tốc độ build.
Không tối ưu:
COPY . .
RUN npm install
Mỗi lần source thay đổi, bước cài package có thể phải chạy lại.
Tốt hơn:
COPY package*.json ./
RUN npm ci
COPY . .
Nếu package-lock.json không đổi, Docker có thể tái sử dụng cache của layer cài dependency.
.dockerignore
Tạo .dockerignore để không gửi file không cần thiết vào build context:
node_modules
dist
.next
.git
.env
*.log
Dockerfile*
README.md
Lợi ích:
- Build context nhỏ hơn.
- Build nhanh hơn.
- Giảm nguy cơ đưa file nhạy cảm vào image.
- Tránh copy
node_modulestừ host.
Đừng chỉ dựa vào .dockerignore để quản lý secret. Secret production cần được quản lý bằng cơ chế phù hợp và không commit vào Git.
Multi-stage build
Multi-stage build dùng nhiều giai đoạn để giảm kích thước image cuối.
Ví dụ Node.js:
FROM node:22-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/main.js"]
Ba stage:
deps → cài dependency
builder → build source
runner → image production tối thiểu
Lợi ích:
- Không mang source và build tool thừa vào image cuối.
- Giảm kích thước.
- Giảm bề mặt tấn công.
- Tách rõ build và runtime.
Cần kiểm tra cấu trúc output thật của framework. Next.js, NestJS và monorepo có cách copy file khác nhau.
Volume là gì?
Dữ liệu ghi trong lớp writable của container có thể mất khi container bị xóa.
Volume dùng để lưu dữ liệu ngoài vòng đời container.
Ví dụ PostgreSQL:
docker volume create postgres-data
docker run -d \
--name postgres \
-e POSTGRES_PASSWORD=change-me \
-v postgres-data:/var/lib/postgresql/data \
postgres:18
Khi xóa container, volume vẫn có thể còn.
Kiểm tra:
docker volume ls
Named volume
postgres-data:/var/lib/postgresql/data
Docker quản lý vị trí thật trên host.
Bind mount
./uploads:/app/uploads
Mount trực tiếp thư mục host vào container.
Bind mount tiện cho development và file cần truy cập trực tiếp, nhưng phụ thuộc cấu trúc filesystem và permission của host.
Volume không phải backup
Volume giúp dữ liệu tồn tại khi container bị thay thế. Nó không bảo vệ trước:
- Xóa nhầm volume.
- Hỏng ổ đĩa.
- Hỏng database logic.
- Ransomware.
- VPS bị mất.
- Backup nằm cùng server bị hỏng.
Bạn vẫn cần backup ra nơi khác và kiểm tra restore.
Docker network là gì?
Container trong cùng network có thể liên lạc bằng tên service hoặc tên container.
Tạo network:
docker network create app-network
Chạy PostgreSQL:
docker run -d \
--name database \
--network app-network \
-e POSTGRES_PASSWORD=change-me \
postgres:18
Chạy API:
docker run -d \
--name api \
--network app-network \
-e DATABASE_HOST=database \
deployeasy-api:1.0
Bên trong network, API dùng:
database:5432
Không dùng:
localhost:5432
Vì localhost bên trong container API chỉ trỏ về chính container API.
Docker Compose là gì?
Docker Compose dùng file YAML để định nghĩa và chạy nhiều service.
Ví dụ compose.yaml:
services:
database:
image: postgres:18
restart: unless-stopped
environment:
POSTGRES_DB: deployeasy
POSTGRES_USER: deployeasy
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- app-network
api:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
depends_on:
- database
environment:
NODE_ENV: production
DATABASE_HOST: database
DATABASE_PORT: 5432
DATABASE_NAME: deployeasy
DATABASE_USER: deployeasy
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
ports:
- "127.0.0.1:3000:3000"
networks:
- app-network
volumes:
postgres-data:
networks:
app-network:
Chạy:
docker compose up -d --build
Xem trạng thái:
docker compose ps
Xem log:
docker compose logs -f
Dừng và xóa container/network do Compose tạo:
docker compose down
Không thêm -v nếu chưa muốn xóa volume:
docker compose down -v
Lệnh trên có thể xóa named volume của project và làm mất dữ liệu nếu không có backup.
depends_on có chờ database sẵn sàng không?
depends_on giúp xác định thứ tự tạo hoặc khởi động service, nhưng không nên mặc định hiểu rằng database đã hoàn toàn sẵn sàng nhận kết nối.
Có thể thêm healthcheck:
services:
database:
image: postgres:18
healthcheck:
test: ["CMD-SHELL", "pg_isready -U deployeasy -d deployeasy"]
interval: 10s
timeout: 5s
retries: 5
api:
depends_on:
database:
condition: service_healthy
Ứng dụng vẫn nên có cơ chế retry kết nối hợp lý.
Environment variable và secret
Không nên hard-code password thật trong compose.yaml.
Dùng .env cục bộ:
DATABASE_PASSWORD=replace-with-a-strong-password
Trong Compose:
environment:
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
Thêm .env vào .gitignore:
.env
.env.*
!.env.example
Tạo .env.example:
DATABASE_PASSWORD=CHANGE_ME
Biến môi trường vẫn có thể bị nhìn thấy bởi process hoặc người có quyền quản trị host. Với hệ thống lớn, cân nhắc secret manager phù hợp.
Docker trong development và production khác nhau thế nào?
Development
Thường ưu tiên:
- Bind mount source.
- Hot reload.
- Debug port.
- Dependency dev.
- Log chi tiết.
Ví dụ:
services:
api:
build: .
command: npm run start:dev
volumes:
- .:/app
- /app/node_modules
Production
Ưu tiên:
- Image bất biến.
- Không mount source.
- Chạy non-root.
- Chỉ cài production dependency.
- Healthcheck.
- Restart policy.
- Log và monitoring.
- Backup.
- Không public database port.
- Image có tag rõ ràng.
Không nên dùng cấu hình development nguyên trạng cho production.
Docker có tự động làm hệ thống an toàn không?
Không.
Một số nguyên tắc cơ bản:
- Dùng image từ nguồn đáng tin cậy.
- Pin phiên bản phù hợp, không phụ thuộc mù quáng vào
latest. - Cập nhật base image.
- Chạy bằng user không phải root khi có thể.
- Không mount Docker socket vào container nếu không thật sự cần.
- Không đưa secret vào image.
- Chỉ public port cần thiết.
- Dùng firewall trên host.
- Quét image và dependency.
- Giới hạn quyền và capability.
- Backup volume.
- Theo dõi log.
Container bị cô lập không có nghĩa là ứng dụng bên trong không thể có lỗ hổng.
Docker có làm ứng dụng nhanh hơn không?
Không nhất thiết.
Docker chủ yếu giải quyết tính đóng gói, nhất quán và quản lý môi trường. Hiệu năng phụ thuộc vào:
- Ứng dụng.
- CPU.
- RAM.
- I/O.
- Network.
- Volume.
- Cấu hình runtime.
- Hệ điều hành host.
Một ứng dụng chưa tối ưu không tự nhanh lên chỉ vì chạy trong container.
Docker có phù hợp VPS 1 GB RAM không?
Có thể chạy container nhẹ, nhưng cần thận trọng.
Ví dụ cùng lúc chạy:
Docker daemon
Nginx
Next.js
NestJS
PostgreSQL
có thể vượt quá 1 GB, đặc biệt khi build.
Cách giảm áp lực:
- Build image ở CI rồi pull về VPS.
- Không build Next.js trên server RAM thấp.
- Tách database sang dịch vụ khác.
- Chỉ chạy service cần thiết.
- Thiết lập swap như lớp hỗ trợ, không xem là RAM thay thế.
- Theo dõi
docker stats.
Kiểm tra:
docker stats
free -h
df -h
Docker và Kubernetes khác nhau thế nào?
Docker giúp build và chạy container.
Kubernetes là nền tảng orchestration để quản lý container trên một hoặc nhiều node, với các khả năng như:
- Scheduling.
- Self-healing.
- Service discovery.
- Rolling update.
- Scaling.
- Secret và config.
- Quản lý workload phân tán.
Dự án nhỏ trên một VPS thường có thể bắt đầu bằng Docker Compose. Không cần dùng Kubernetes chỉ để làm hệ thống trông “chuyên nghiệp”.
Các lệnh Docker cơ bản
Image
docker image ls
docker pull nginx:alpine
docker build -t my-app:1.0 .
docker image rm my-app:1.0
Container
docker ps
docker ps -a
docker run -d --name web -p 8080:80 nginx:alpine
docker logs -f web
docker exec -it web sh
docker stop web
docker start web
docker rm web
Volume
docker volume ls
docker volume inspect postgres-data
docker volume rm postgres-data
Network
docker network ls
docker network inspect app-network
Compose
docker compose config
docker compose build
docker compose up -d
docker compose ps
docker compose logs -f
docker compose down
Trước khi chạy:
docker system prune
hãy hiểu rõ những tài nguyên sẽ bị xóa. Không dùng lệnh dọn dẹp một cách máy móc trên production.
Quy trình deploy Docker cơ bản
1. Viết Dockerfile
2. Build image
3. Test image ở local
4. Push image lên registry
5. VPS pull image
6. Chạy container bằng Compose
7. Nginx reverse proxy
8. Kiểm tra health
9. Theo dõi log
10. Rollback bằng image tag cũ
Ví dụ tag:
deployeasy-api:1.0.0
deployeasy-api:1.0.1
deployeasy-api:git-a1b2c3d
Không nên chỉ dùng latest, vì khó xác định chính xác phiên bản đang chạy và rollback.
Những lỗi người mới thường gặp
Dùng localhost để kết nối container khác
Sai:
DATABASE_HOST=localhost
Đúng trong Compose:
DATABASE_HOST=database
Public port database ra Internet
Không cần viết:
ports:
- "5432:5432"
nếu chỉ API trong cùng network cần truy cập database.
Mất dữ liệu khi xóa container
Database phải dùng volume và có backup.
Copy .env vào image
Kiểm tra .dockerignore và không hard-code secret.
Image quá lớn
Dùng:
- Base image phù hợp.
- Multi-stage build.
.dockerignore.- Chỉ cài production dependencies.
Container chạy nhưng website không truy cập được
Kiểm tra:
docker ps
docker logs CONTAINER
docker port CONTAINER
ss -tulpn
curl -I http://127.0.0.1:PORT
Ứng dụng bên trong container có thể đang chỉ bind 127.0.0.1. Trong nhiều trường hợp, nó cần lắng nghe trên:
0.0.0.0
Build bị exit code 137
Thường liên quan tới process bị kill, trong đó thiếu bộ nhớ là nguyên nhân phổ biến. Kiểm tra:
free -h
dmesg --ctime | tail
docker stats
Khi nào nên dùng Docker?
Nên cân nhắc Docker khi:
- Có nhiều service.
- Cần môi trường nhất quán.
- Muốn CI/CD bằng image.
- Cần chạy cùng stack trên local, staging và production.
- Muốn rollback bằng version image.
- Cần cô lập dependency.
Không nhất thiết dùng Docker khi:
- Website tĩnh chỉ cần deploy lên Vercel.
- Dự án rất nhỏ và deploy truyền thống đã đơn giản.
- Đội ngũ chưa có khả năng vận hành container.
- Docker làm tăng độ phức tạp nhưng chưa mang lại lợi ích rõ ràng.
Kết luận
Docker giúp đóng gói ứng dụng cùng môi trường chạy vào image và tạo container từ image đó.
Những khái niệm quan trọng nhất:
Dockerfile → hướng dẫn build
Image → gói ứng dụng
Container → instance đang chạy
Volume → dữ liệu bền vững
Network → kết nối giữa container
Registry → nơi lưu image
Compose → quản lý nhiều service
Để dùng Docker tốt trên production, không chỉ cần container chạy được. Bạn còn cần:
- Image nhỏ và có version.
- Secret không nằm trong source hoặc image.
- Database có volume và backup.
- Port được giới hạn.
- Healthcheck.
- Log và monitoring.
- Kế hoạch rollback.
- Đủ CPU, RAM và dung lượng.
Câu hỏi thường gặp
Docker có miễn phí không?
Docker Engine là công nghệ container mã nguồn mở. Một số sản phẩm và gói dịch vụ của Docker có điều khoản thương mại riêng. Cần kiểm tra chính sách hiện tại cho tổ chức và trường hợp sử dụng của bạn.
Xóa container có mất image không?
Không. Container và image là hai tài nguyên khác nhau. Tuy nhiên, dữ liệu ghi trong lớp container có thể mất nếu không dùng volume.
Docker Compose có dùng được trên production không?
Có thể dùng cho hệ thống nhỏ hoặc một server, nếu được cấu hình và vận hành cẩn thận. Nó không tự cung cấp high availability đa node.
Có cần Docker để deploy Next.js không?
Không. Next.js có thể deploy bằng Vercel, PM2, systemd hoặc các nền tảng khác. Docker là một lựa chọn đóng gói và vận hành.
Docker có thay thế Nginx không?
Không mặc định. Nginx có thể chạy trong hoặc ngoài Docker để reverse proxy, kết thúc HTTPS và phục vụ file tĩnh.
Container có hệ điều hành riêng không?
Image có userspace và filesystem cần thiết, nhưng Linux container thường dùng chung kernel của host.
Nguồn tham khảo
Đọc tiếp
Bài viết liên quan
Dockerfile production: 15 cách giảm image, tăng bảo mật
Hướng dẫn viết Dockerfile production với multi-stage build, cache, .dockerignore, non-root user, pin version, healthcheck và secret an toàn.
Đọc bài →Docker Compose production: App, PostgreSQL, healthcheck và backup
Hướng dẫn thiết kế Docker Compose production với app, PostgreSQL, network, volume, healthcheck, secret, restart và deploy an toàn.
Đọc bài →Build Docker trên local rồi deploy lên VPS
Quy trình từng bước tạo Dockerfile, chạy thử monorepo REMAP trên local và dùng cùng cấu hình Docker Compose để triển khai lên VPS.
Đọc bài →