Giao diện
Module 6: Under the Hood (Bên dưới nắp Capo)
🎓 Instructor Profile
Giáo sư Tom (Chuyên gia HDH) & Kỹ sư Raizo. Đây là module Khó nhất và Hấp dẫn nhất. Chúng ta sẽ ngừng gõ lệnh Docker và đi sâu vào lõi Linux Kernel để xem bộ máy này thực sự hoạt động như thế nào.
Bạn nghĩ Docker là một công nghệ mới diệu kỳ? Không. Docker thực chất là một "nghệ thuật sắp đặt" (Orchestration Art) của các tính năng đã tồn tại trong Linux từ cả chục năm trước.
Hãy cùng mở nắp capo lên và nhìn vào động cơ V8 bên dưới.
🎭 Phần 1: The Great Illusion (Ảo ảnh vĩ đại)
Sự thật gây sốc
Trong Linux Kernel, KHÔNG CÓ cái gì gọi là "Container". Kernel chỉ biết đến Process (Tiến trình).
Container, về bản chất, chỉ là một Process bình thường (như Firefox, Chrome), nhưng bị Kernel "bịt mắt" (để không nhìn thấy process khác) và "trói tay" (để không dùng quá tài nguyên).
"Docker is just a fancy user interface for Linux Kernel features."
🧱 Phần 2: Namespaces (Những bức tường ngăn cách)
Làm sao để Process A không nhìn thấy Process B, dù chúng chạy trên cùng một máy? Câu trả lời là Namespaces (Không gian tên). Nó tạo ra sự cô lập (Isolation).
6 Loại Namespaces cốt lõi
| Namespace | Chức năng (Isolation) | Ví dụ minh họa |
|---|---|---|
| PID | Cô lập ID tiến trình. | Trong container, process thấy mình là PID 1 (Vua). Nhưng ngoài Host, nó là PID 12345 (Dân thường). |
| NET | Cô lập Mạng. | Container có eth0 riêng, IP riêng, bảng định tuyến riêng. |
| MNT | Cô lập File System. | Container nhìn thấy thư mục gốc / là một thế giới hoàn toàn khác với / của Host. |
| UTS | Cô lập Hostname. | Container có tên máy riêng (VD: a1b2c3d4e5f6) khác với tên máy thật. |
| IPC | Cô lập Giao tiếp. | Ngăn container dùng Shared Memory để đọc trộm dữ liệu process khác. |
| USER | Cô lập User ID. | Root trong container (UID 0) có thể bị map thành User thường (UID 1000) ngoài Host. |
| CGROUP | Cô lập Cgroup hierarchy. | Container có cgroup tree riêng, không thể thấy hoặc can thiệp resource limits của container khác. |
💡 Namespace trong thực tế
Bạn có thể xem namespaces của một process bất kỳ:
bash
# Xem namespaces của container process (thay PID thực tế)
ls -la /proc/<PID>/ns/
# Kết quả: cgroup, ipc, mnt, net, pid, user, uts
# Mỗi file là một symlink đến namespace IDHai process có cùng namespace ID → chúng chia sẻ namespace đó (cùng nhìn thấy nhau).
👮 Phần 3: Cgroups (Cảnh sát tài nguyên)
Namespaces chỉ giúp "che mắt" (Isolation), nhưng không ngăn được việc dùng chùa tài nguyên. Một process bị cô lập vẫn có thể ăn hết 100% RAM của máy.
Control Groups (Cgroups) sinh ra để giải quyết vấn đề Limitation (Giới hạn).
Chức năng của Cgroups
- Resource Limiting: Giới hạn RAM (VD: 512MB), CPU (0.5 core).
- Prioritization: Ưu tiên process quan trọng (VIP) được dùng nhiều CPU hơn.
- Accounting: Đo lường xem container đã dùng bao nhiêu tài nguyên (để tính tiền Cloud).
- Control: Đóng băng (Freeze) hoặc Kill cả nhóm process.
Cgroups v1 vs v2 (Cuộc chuyển giao)
| Đặc điểm | Cgroups v1 | Cgroups v2 |
|---|---|---|
| Kiến trúc | Nhiều hierarchy riêng biệt (cpu, memory, io riêng) | Unified hierarchy duy nhất |
| Quản lý | Phức tạp, có thể xung đột | Đơn giản, nhất quán |
| Resource control | Mỗi controller một tree | Tất cả controllers chung tree |
| Pressure Stall Info | ❌ Không hỗ trợ | ✅ PSI metrics (cpu.pressure, memory.pressure) |
| Thread-level control | Hạn chế | ✅ Hỗ trợ đầy đủ |
| Status | Legacy (vẫn dùng rộng rãi) | Default trên kernel ≥ 5.8, RHEL 9+, Ubuntu 22.04+ |
bash
# Kiểm tra hệ thống đang dùng cgroups v1 hay v2
stat -fc %T /sys/fs/cgroup/
# "cgroup2fs" → v2 | "tmpfs" → v1
# Xem resource limits của một container (cgroups v2)
cat /sys/fs/cgroup/system.slice/docker-<CONTAINER_ID>.scope/memory.max
cat /sys/fs/cgroup/system.slice/docker-<CONTAINER_ID>.scope/cpu.max
# Xem thực tế container đang dùng bao nhiêu memory
cat /sys/fs/cgroup/system.slice/docker-<CONTAINER_ID>.scope/memory.current⚠️ Cgroups v1 → v2 Migration
Nếu upgrade OS từ Ubuntu 20.04 → 22.04, cgroups tự động chuyển từ v1 sang v2. Một số container runtimes cũ và monitoring tools có thể không tương thích. Kiểm tra kỹ trước khi upgrade production.
👉 Ví dụ: Cgroups giống như cái cầu chì (Circuit Breaker). Nếu container dùng quá dòng điện (RAM) cho phép -> Cắt (OOM Kill).
🪄 Phần 4: The Storage Magic (Phép thuật lưu trữ)
Tư duy @[/performance]: Tại sao bạn khởi động 100 container Ubuntu (mỗi cái 70MB) mà ổ cứng không bị tốn 100 x 70MB = 7GB? Thực tế chỉ tốn... vài KB.
Union File System (UnionFS) & Overlay2
Docker sử dụng cơ chế Layered Architecture (Kiến trúc phân lớp). Image gồm nhiều lớp Read-only xếp chồng lên nhau.
Copy-on-Write (CoW)
- Khi đọc: Container đọc xuyên qua các lớp trong suốt.
- Khi ghi: Container KHÔNG sửa trực tiếp vào Image (vì Read-only).
- Nó tìm file cần sửa ở lớp dưới.
- Nó Copy file đó lên lớp trên cùng (Write Layer).
- Nó sửa (Write) vào bản copy đó.
👉 Kết quả: Nếu bạn không sửa file, bạn không tốn dung lượng đĩa mới. Rất thông minh!
Storage Drivers (Trình điều khiển lưu trữ)
Docker hỗ trợ nhiều storage drivers. Mỗi loại có ưu nhược điểm riêng:
| Driver | Ưu điểm | Nhược điểm | Khuyên dùng |
|---|---|---|---|
| overlay2 | Hiệu năng cao, stable, ít memory | Cần kernel ≥ 4.0 | ✅ Default & Best choice |
| btrfs | Snapshot nhanh, deduplication | Phức tạp, cần btrfs filesystem | Specialized storage |
| zfs | Data integrity, compression | RAM-hungry, cần zfs module | Enterprise storage |
| devicemapper | Ổn định trên RHEL cũ | Chậm hơn overlay2, deprecated | ❌ Legacy only |
| vfs | Không cần kernel feature đặc biệt | Không CoW → tốn disk gấp N lần | ❌ Testing only |
bash
# Kiểm tra storage driver đang dùng
docker info | grep "Storage Driver"
# Output: Storage Driver: overlay2
# Xem chi tiết layers của một image
docker inspect --format='{{json .GraphDriver.Data}}' nginx | jq
# Xem dung lượng thực tế Docker đang chiếm
docker system df
docker system df -v # Chi tiết từng image, container, volumeOverlayFS Deep Dive
Cơ chế hoạt động:
- Lower Dir: Chứa image layers (read-only). Nhiều container share cùng lower layers.
- Upper Dir: Mỗi container có riêng. Files mới hoặc sửa đổi nằm ở đây.
- Work Dir: Thư mục tạm cho atomic operations (rename, delete).
- Merged View: Kernel merge tất cả layers thành một unified view cho container.
⚙️ Phần 5: The Runtime Hierarchy (Hệ thống phân cấp)
Khi bạn gõ docker run, điều gì thực sự xảy ra? (Kiến trúc hiện đại).
- Docker CLI: Gửi lệnh REST API đến Daemon.
- Dockerd (Daemon): Tiếp nhận yêu cầu.
- Containerd: (High-level Runtime). Quản lý vòng đời container, kéo image từ Registry. Đây là "trái tim" mà Kubernetes cũng sử dụng (K8s đã bỏ Docker để dùng thẳng Containerd).
- Runc: (Low-level Runtime / OCI Runtime). Đây là "công nhân" thực sự. Nó gọi trực tiếp vào Kernel (Syscalls) để tạo Namespaces và Cgroups. Sau khi tạo xong container, Runc tự thoát (exit).
Tư duy @[/architect]: Sự tách biệt này cho phép dockerd có thể restart/update mà không làm chết các container đang chạy (Live Restore).
Docker Daemon Architecture (Kiến trúc chi tiết)
Vai trò của containerd-shim:
- Đóng vai trò "người giám hộ" cho container process
- Cho phép
containerdrestart mà container vẫn sống - Thu thập exit code khi container kết thúc
- Quản lý stdio (stdin/stdout/stderr) của container
OCI Specification (Tiêu chuẩn mở)
OCI (Open Container Initiative) định nghĩa 3 tiêu chuẩn:
| Spec | Mục đích | Ví dụ |
|---|---|---|
| Runtime Spec | Cách tạo và chạy container | runc, crun, youki, kata-containers |
| Image Spec | Format của container image | Docker images, Podman images |
| Distribution Spec | Cách push/pull images | Docker Hub, GitHub Container Registry, Harbor |
bash
# Xem OCI runtime config của container
docker inspect --format='{{json .HostConfig}}' my-container | jq
# Export container thành OCI bundle
mkdir my-bundle && cd my-bundle
docker export my-container | tar -xf -
# Xem OCI runtime spec mà runc sử dụng
runc spec # Tạo config.json mẫu💡 Tại sao OCI quan trọng?
Nhờ OCI, bạn có thể build image bằng Docker, push lên GitHub Container Registry, và chạy bằng Podman hoặc containerd (không cần Docker). Đó là sức mạnh của open standards.
🥊 The Final Challenge: "No Docker" Challenge
Bài tập tốt nghiệp dành cho True Engineer. Hãy tạo ra một container thô sơ trên Linux mà KHÔNG ĐƯỢC DÙNG lệnh docker.
Gợi ý:
- Tải một file hệ thống root (RootFS) của Alpine Linux.
- Dùng lệnh
unshaređể tạo Namespace mới (PID, MNT, UTS).bashsudo unshare --fork --pid --mount-proc /bin/bash - Dùng lệnh
chrootđể đổi thư mục gốc vào RootFS đã tải. - Gõ
ps auxxem PID có phải là 1 không? Gõhostnamexem đổi chưa?
Nếu bạn làm được, bạn đã hiểu bản chất của Containerization. Chúc mừng bạn đã tốt nghiệp Docker Masterclass! 🎉
⚠️ Production Pitfalls (Bẫy thực chiến)
⚠️ Cạm bẫy
Vấn đề: Upgrade OS (VD: Ubuntu 20.04 → 22.04) chuyển từ cgroups v1 sang v2 mà không kiểm tra compatibility.
Hậu quả thực tế: Container runtime (Docker cũ, rkt), monitoring tools (cAdvisor phiên bản cũ), và orchestrators có thể không nhận diện được resource metrics. Containers vẫn chạy nhưng resource limits không hoạt động → memory leak gây OOM kill.
Giải pháp:
- Kiểm tra trước:
stat -fc %T /sys/fs/cgroup/ - Update Docker Engine lên phiên bản hỗ trợ cgroups v2 (≥ 20.10)
- Test staging trước khi upgrade production
⚠️ Cạm bẫy
Vấn đề: Sử dụng devicemapper (loop mode) hoặc vfs trên production.
Hậu quả: devicemapper loop mode không stable cho production workloads — disk I/O cực chậm, data corruption risk cao. vfs không có Copy-on-Write → mỗi container chiếm full disk space của image.
Giải pháp: Sử dụng overlay2 (default trên kernel ≥ 4.0). Kiểm tra: docker info | grep "Storage Driver". Nếu thấy devicemapper hoặc vfs, migrate ngay.
⚠️ Cạm bẫy
Vấn đề: Dùng kill <PID> trên host để kill process trong container, nhưng kill nhầm vì PID namespace khác nhau.
Hậu quả: Container PID 1 (app) trên host có thể là PID 54321. Kill PID 1 trên host = kill init system = sập toàn bộ máy.
Giải pháp:
- Dùng
docker stop/killthay vì kill PID trực tiếp - Nếu cần PID host:
docker inspect --format='{{.State.Pid}}' <container> - Hiểu rõ PID mapping:
docker top <container>xem cả PID trong container và trên host
📝 Quiz: Kiểm tra kiến thức
🧠 Quiz
Câu 1: Trong Linux Kernel, "Container" thực chất là gì?
- [ ] A) Một Virtual Machine nhẹ với kernel riêng
- [ ] B) Một sandbox chạy trong hypervisor
- [x] C) Một process bị cô lập bởi namespaces và bị giới hạn bởi cgroups
- [ ] D) Một file system image được mount vào kernel
💡 Giải thích: Linux kernel không có khái niệm "container". Container chỉ là một process bình thường được "bịt mắt" bằng namespaces (cô lập PID, network, filesystem...) và "trói tay" bằng cgroups (giới hạn CPU, RAM). Docker đóng gói trải nghiệm này thành công cụ dễ dùng.
Câu 2: Khi runc tạo xong container, điều gì xảy ra với runc process?
- [ ] A) runc tiếp tục chạy và monitor container
- [ ] B) runc chuyển thành containerd-shim
- [x] C) runc tự thoát (exit), containerd-shim tiếp quản giám sát container
- [ ] D) runc bị dockerd kill để tiết kiệm tài nguyên
💡 Giải thích: runc là low-level runtime chỉ làm một việc: gọi syscalls (
clone,unshare) để tạo namespaces + cgroups, rồi tự thoát. Sau đó, containerd-shim tiếp quản vai trò giám hộ — thu thập exit code, quản lý stdio, cho phép containerd restart mà không ảnh hưởng container.
Câu 3: Copy-on-Write (CoW) trong OverlayFS giúp tiết kiệm tài nguyên bằng cách nào?
- [ ] A) Nén tất cả files trong image layers
- [ ] B) Xóa files không sử dụng tự động
- [x] C) Chỉ tạo bản copy của file khi container thực sự ghi/sửa file đó
- [ ] D) Chia sẻ RAM giữa các container cùng image
💡 Giải thích: CoW có nghĩa: 100 container dùng cùng image nginx → tất cả share chung các image layers (read-only). Chỉ khi container nào sửa file (VD: edit nginx.conf), file đó mới được copy lên writable layer riêng của container đó. Nếu không sửa → 0 bytes tốn thêm. Đó là lý do bạn chạy 100 container mà ổ cứng chỉ tốn vài KB extra.
✅ Production Readiness Checklist
✅ Checklist triển khai
Checklist Docker Internals cho Production:
- [ ] Hiểu 7 loại namespaces và mục đích cô lập của từng loại
- [ ] Xác nhận hệ thống đang dùng cgroups v2 (
stat -fc %T /sys/fs/cgroup/) - [ ] Storage driver là overlay2 (
docker info | grep "Storage Driver") - [ ] Docker Engine version hỗ trợ cgroups v2 (≥ 20.10)
- [ ] Hiểu runtime hierarchy: CLI → dockerd → containerd → shim → runc
- [ ] Live Restore đã bật để dockerd restart không kill containers
- [ ] Monitoring đã thu thập cgroup metrics (memory.current, cpu.stat)
- [ ] Team hiểu PID namespace mapping khi debug production issues
- [ ] OCI compatibility đã verify (images portable giữa Docker/Podman/containerd)
💡 Congratulations!
Bạn đã hoàn thành Docker Masterclass — từ nền tảng đến kernel internals. Bạn không chỉ biết dùng Docker, mà còn hiểu tại sao nó hoạt động. Đó là sự khác biệt giữa một Developer và một Engineer.