DeployEasy
Menu
DockerCơ bản

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.

· 16 phút đọc · 3.409 từ
Mục lục bài viết

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 builddocker 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:

  1. Tìm image nginx.
  2. Pull image nếu máy chưa có.
  3. Tạo container tên web.
  4. Chạy process Nginx trong container.
  5. Map port 8080 của host tới port 80 trong 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íImageContainer
Trạng tháiGói chỉ đọc theo layerInstance có lớp ghi
Tạo bằngdocker build hoặc docker pulldocker run hoặc docker create
Có chạy processKhôngCó khi ở trạng thái running
Có thể tạo nhiều bảnMỗi container là một instance
Lưu ở registryKhô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_modules từ 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

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:

  1. Image nhỏ và có version.
  2. Secret không nằm trong source hoặc image.
  3. Database có volume và backup.
  4. Port được giới hạn.
  5. Healthcheck.
  6. Log và monitoring.
  7. Kế hoạch rollback.
  8. Đủ 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