Back to blog

Docker Images và Containers: Giải Thích Từ Đầu

dockercontainersdevopsbackendtutorial
Docker Images và Containers: Giải Thích Từ Đầu

Bạn đã nghe câu này cả trăm lần: "Dùng Docker đi." Nhưng nếu chưa từng đụng vào, các concept cứ mờ mờ ảo ảo. Docker image là gì? Container là gì? Và tại sao mọi người cứ ca bài "it works on my machine"?

Bài này sẽ giải thích hết — kèm ví dụ thực hành chạy được ngay bây giờ.

Docker là gì?

Docker là platform cho phép bạn đóng gói app cùng tất cả những thứ nó cần (runtime, libraries, config) vào một unit duy nhất, portable — và chạy nó nhất quán ở bất kỳ đâu.

Problem kinh điển mà nó giải quyết:

Developer: "Trên máy mình chạy ngon!"
Server: "Trên server thì không."

Tại sao? Vì máy developer dùng Node 20, server dùng Node 16. Hoặc developer cài một library global mà server không có. Hoặc environment variables khác nhau.

Docker fix điều này bằng cách đóng gói tất cả mọi thứ app cần vào một bundle duy nhất, chạy giống nhau ở mọi nơi:

  • Laptop của bạn
  • Máy của teammate
  • CI/CD pipeline
  • Staging server
  • Production

Không còn "works on my machine" nữa.

Lợi ích khác

Onboard teammate mới nhanh hơn:
Thay vì hướng dẫn setup 2 tiếng, họ chỉ cần chạy docker compose up là bắt đầu code được. Không cần cài MySQL, Redis, Node, Java — tất cả đã có sẵn trong project.

Giữ máy sạch:
Không muốn cài PostgreSQL, Redis hay Java thẳng lên laptop? Chạy chúng trong Docker container. Khi xong project, docker compose down -v --rmi all là dọn sạch hết.


Cài đặt Docker

macOS

Download Docker Desktop từ trang Docker.

Windows

Download Docker Desktop từ trang Docker.

Ubuntu / Linux

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
 
# Kiểm tra hoạt động
docker info

Sau khi cài xong, chạy smoke test kinh điển:

docker run hello-world

Nếu thấy "Hello from Docker!" — OK rồi đó.


Docker Image là gì?

Image là một package read-only chứa tất cả mọi thứ cần để chạy app:

  • Layer OS (ví dụ Ubuntu, Alpine Linux)
  • Runtime (Node.js, Python, Java,...)
  • Libraries và dependencies
  • Code của app
  • Configuration

Hình dung nó như một bản thiết kế — bạn không chạy trực tiếp image; bạn tạo ra container từ image đó.

Image được tạo từ các Layer

Mỗi image được build lên từ nhiều layer chồng nhau:

Tại sao layer lại quan trọng:

  • Caching — layer không thay đổi sẽ được reuse. Nếu chỉ sửa code app, Docker không cần re-download Node.js hay re-install dependencies.
  • Sharing — nếu hai image dùng cùng base layer (ví dụ ubuntu:22.04), Docker chỉ lưu một bản trên disk.
  • Immutable — không thể sửa image đang tồn tại. Bạn chỉ có thể build image mới hoặc thêm layer mới lên trên.

Docker Hub: Registry chứa Image

Docker Hub là registry công khai nơi hàng nghìn image sẵn có. Cần PostgreSQL? Chạy thôi:

docker pull postgres:15

Docker tải image PostgreSQL 15 chính thức từ Docker Hub. Bạn cũng tìm được image của Nginx, Redis, Node.js, Python, MySQL và hầu hết mọi tool cần thiết.

Liệt kê và xóa Image

# Xem tất cả image trên máy
docker images
 
# Xóa một image
docker rmi nginx:1.25
 
# Xóa tất cả image không dùng
docker image prune

Docker Container là gì?

Container là một instance đang chạy của image. Mối quan hệ là:

Image (bản thiết kế)  →  Container (app đang chạy)

Bạn có thể tạo nhiều container từ cùng một image:

image nginx:1.25  →  container: web-1 (đang chạy)
                  →  container: web-2 (đang chạy)
                  →  container: web-test (đã dừng)

Container độc lập với nhau và với host. Chúng có filesystem, process space và network interface riêng. Nhưng nhẹ hơn VM rất nhiều — khởi động trong vài giây, không phải vài phút, và chỉ dùng vài MB RAM thay vì vài GB.

Container vs Virtual Machine

FeatureContainerVirtual Machine
Kích thướcMBGB (full OS)
Khởi độngVài giâyVài phút
IsolationProcess-levelHardware-level
PerformanceGần native~5–10% overhead
Mật độHàng trăm trên một hostVài chục trên một host

Container chia sẻ kernel của host OS, đó là lý do chúng nhẹ đến vậy. VM phải chạy toàn bộ guest OS trên hypervisor.


Chạy Container Đầu Tiên

Hello World

docker run hello-world

Câu này pull image hello-world (nếu chưa có) và chạy container từ nó.

Nginx Web Server

# Chạy Nginx, map port 8080 trên host vào port 80 trong container
docker run -p 8080:80 nginx

Mở http://localhost:8080 trên trình duyệt — bạn thấy trang welcome của Nginx.

Flag -p 8080:80 nghĩa là: "forward traffic từ port 8080 của host vào port 80 của container."

Chạy ngầm (Detached Mode)

docker run -d --name my-nginx -p 8080:80 nginx

-d chạy container ngầm. --name my-nginx đặt tên dễ nhớ.

Xem các Container đang chạy

docker ps
CONTAINER ID   IMAGE   COMMAND   CREATED        STATUS         PORTS                  NAMES
a3f1b2c4d5e6   nginx   ...       2 phút trước   Up 2 phút      0.0.0.0:8080->80/tcp   my-nginx

Dừng và Xóa

docker stop my-nginx
docker rm my-nginx

Hoặc force-remove container đang chạy:

docker rm -f my-nginx

Tự Build Image (Dockerfile)

Đến giờ ta chỉ dùng image từ Docker Hub. Nhưng làm sao để đóng gói app của chính mình?

Bạn viết một Dockerfile — script nói cho Docker biết cách build image.

Đây là ví dụ Node.js đơn giản:

index.js:

const http = require('http');
 
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello from Docker!\n');
});
 
server.listen(3000, () => {
  console.log('Server đang chạy trên port 3000');
});

Dockerfile:

FROM node:24
WORKDIR /app
 
COPY index.js .
 
EXPOSE 3000
 
CMD ["node", "index.js"]

Build và chạy:

# Build image (đặt tên là "my-app")
docker build -t my-app .
 
# Chạy container từ image đó
docker run -p 3000:3000 my-app

Vào http://localhost:3000 — bạn thấy "Hello from Docker!"

Mỗi Instruction Trong Dockerfile Làm Gì

InstructionMục đích
FROM node:24Bắt đầu từ image Node.js 24 chính thức
WORKDIR /appĐặt thư mục làm việc bên trong container
COPY index.js .Copy file của bạn vào container
EXPOSE 3000Khai báo app lắng nghe trên port 3000
CMD [...]Lệnh chạy khi container khởi động

Cheat Sheet Các Lệnh Cần Biết

Image

docker pull nginx:1.25          # Tải image
docker images                    # Liệt kê image local
docker rmi nginx:1.25            # Xóa image
docker build -t my-app .         # Build từ Dockerfile
docker image prune               # Xóa image không dùng

Container

docker run -d --name web -p 8080:80 nginx   # Chạy container
docker ps                                    # Liệt kê container đang chạy
docker ps -a                                 # Liệt kê tất cả container
docker stop web                              # Dừng container
docker start web                             # Start container đã dừng
docker rm web                                # Xóa container
docker rm -f web                             # Force-remove container đang chạy
docker logs web                              # Xem log
docker logs -f web                           # Follow log
docker exec -it web bash                     # Mở shell bên trong container
docker container prune                       # Xóa tất cả container đã dừng

Dọn dẹp hệ thống

docker system df               # Kiểm tra dung lượng
docker system prune            # Xóa resource không dùng
docker system prune --volumes  # Bao gồm cả volume

Lỗi Hay Gặp

Dùng :latest trong Production

# Rủi ro — "latest" thay đổi mà không báo trước
docker run nginx:latest
 
# An toàn — pin vào version cụ thể
docker run nginx:1.25.3

Lưu Data Bên Trong Container

Container là ephemeral — khi docker rm một container, toàn bộ data bên trong mất hết. Với database, luôn dùng volume:

# Nguy hiểm — data mất khi container bị xóa
docker run postgres:15
 
# Đúng cách — data tồn tại trong named volume
docker run -v pgdata:/var/lib/postgresql/data postgres:15

Không Dọn Dẹp

Container, image và volume tích lũy nhanh lắm. Chạy lệnh này định kỳ:

docker system prune --volumes

Tóm Lại

✅ Docker đóng gói app và dependencies vào container portable
✅ Image là bản thiết kế read-only; container là instance đang chạy
✅ Image được build từ nhiều layer — layer không đổi được cache để build nhanh
✅ Container nhẹ hơn VM rất nhiều (MB vs GB, vài giây vs vài phút)
✅ Dùng docker run để start container, -p để map port, -d để chạy ngầm
✅ Viết Dockerfile để đóng gói app của mình thành image
✅ Dùng volume cho data persistent; đừng bao giờ trông cậy vào filesystem của container


Series: Docker & Kubernetes Learning Roadmap
Next: Docker Fundamentals: Hướng Dẫn Toàn Diện Cho Người Mới


Muốn đào sâu hơn? Bài tiếp theo cover Docker đầy đủ — networking, volumes, Docker Compose và best practices cho production.

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.