Bài 01
Docker cơ bản: container vs VM, kiến trúc, lifecycle
Bài 01 — Docker cơ bản: container vs VM, kiến trúc, lifecycle
Đây là bài đầu tiên của chặng Container. Mục tiêu: sau bài này bạn hiểu container thực sự là gì (không phải VM thu nhỏ), docker run được app bất kỳ, vào shell container debug được, và monitor được container đang chạy.
Đừng vội đi đến Dockerfile, Compose, K8s. Container cơ bản phải vững — không thì sau này nhìn vào K8s sẽ thấy magic, không hiểu vì sao.
Vấn đề trước khi có Docker
Hồi 2012, mình deploy 1 app PHP cho khách. Code chạy ngon trên laptop. SSH vào server prod, copy code lên, chạy:
php index.php
# PHP Parse error: syntax error, unexpected '?'Lỗi syntax? Code mình test 100 lần rồi! Check version:
php -v
# PHP 5.4.16 on server
# PHP 7.0.8 on laptopLaptop PHP 7, server PHP 5.4. Toán tử ?? (null coalescing) chỉ có từ PHP 7. Code valid trên laptop, invalid trên server.
Giải pháp lúc đó: upgrade PHP server → break 3 app cũ → rollback → tìm cách downgrade laptop → mất 2 ngày.
Đây là chuyện kinh điển: "trên máy mình chạy được". Container ra đời để kết thúc vấn đề này — đóng gói app + runtime + dependency thành 1 đơn vị, chạy ở đâu cũng như nhau.
Container vs VM — khác biệt cốt lõi
Phải hiểu rõ phần này trước khi đi tiếp. Nhiều người tưởng container là "VM nhẹ" — sai và dẫn tới hiểu lầm cả đời.
Virtual Machine
VM là một máy ảo được chạy trên OS chính. Có đầy đủ các thành phần như CPU, RAM, disk, kernel — tức là 1 hệ điều hành hoàn chỉnh chạy bên trong 1 hệ điều hành khác.
┌─────────────────────────────────────────┐
│ Host OS (CentOS 9) │
│ ┌───────────────────────────────────┐ │
│ │ Hypervisor (VMware/KVM/VBox) │ │
│ └───────────────────────────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ VM 1 │ │ VM 2 │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │ App A │ │ │ │ App B │ │ │
│ │ ├─────────┤ │ │ ├─────────┤ │ │
│ │ │ Libs │ │ │ │ Libs │ │ │
│ │ ├─────────┤ │ │ ├─────────┤ │ │
│ │ │ Guest OS│ │ │ │ Guest OS│ │ │
│ │ │ (Ubuntu)│ │ │ │ (Debian)│ │ │
│ │ ├─────────┤ │ │ ├─────────┤ │ │
│ │ │ Kernel │ │ │ │ Kernel │ │ │
│ │ │ riêng │ │ │ │ riêng │ │ │
│ │ └─────────┘ │ │ └─────────┘ │ │
│ └─────────────┘ └─────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ Hardware (CPU, RAM, Disk) │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘Mỗi VM có kernel riêng — tức là khi mở 1 VM, bạn bật thêm 1 OS chạy song song với host. Tốn RAM, tốn CPU, khởi động chậm (vài chục giây đến vài phút).
Container
Container không phải là máy ảo. Chỉ là một tiến trình (process) chạy trên hệ điều hành. Bản thân container không có kernel hệ điều hành — nó chia sẻ kernel với host.
┌─────────────────────────────────────────┐
│ Host OS (CentOS 9) │
│ ┌───────────────────────────────────┐ │
│ │ Docker Engine │ │
│ └───────────────────────────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌─────────┐ │
│ │ Container│ │ Container│ │Container│ │
│ │ ┌──────┐ │ │ ┌──────┐ │ │┌──────┐ │ │
│ │ │App A │ │ │ │App B │ │ ││App C │ │ │
│ │ ├──────┤ │ │ ├──────┤ │ │├──────┤ │ │
│ │ │Libs │ │ │ │Libs │ │ ││Libs │ │ │
│ │ └──────┘ │ │ └──────┘ │ │└──────┘ │ │
│ └──────────┘ └──────────┘ └─────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ Kernel (CHIA SẺ chung) │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ Hardware (CPU, RAM, Disk) │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘Vài đặc điểm:
- Container không có kernel riêng — dùng chung kernel với host.
- Process này được giới hạn tài nguyên sử dụng trên OS — qua cgroups.
- Container dừng và thoát như đóng process — không có cơ chế "shutdown OS".
So sánh chi tiết
| Tiêu chí | VM | Container |
|---|---|---|
| Kernel | Mỗi VM có kernel riêng | Chia sẻ kernel với host |
| Khởi động | 30s - vài phút | < 1 giây |
| Kích thước | GB (cả OS) | MB (chỉ app + libs) |
| RAM tốn | Vài trăm MB - GB idle | Vài MB idle |
| Cô lập | Mạnh (kernel-level) | Yếu hơn (process-level, namespace) |
| Run OS khác? | Có (Windows trên Linux, ngược lại) | Không (Linux container chỉ chạy trên Linux kernel) |
| Số lượng/host | ~10-50 | ~hàng trăm đến nghìn |
| Use case | Cần OS khác, cô lập strict | Đóng gói app, microservices |
⚠️ Container không thay thế hoàn toàn VM. Vẫn cần VM khi:
- Chạy OS khác (Windows app trên Linux host).
- Yêu cầu cô lập security mạnh (multi-tenant).
- Run kernel modules custom.
Production hiện đại thường lai: VM bên ngoài (cô lập tenant), container bên trong VM (deploy app).
Docker là gì
Docker là một nền tảng mã nguồn mở giúp đóng gói, phân phối và chạy ứng dụng trong các container. Container giúp đảm bảo rằng ứng dụng có thể chạy đồng nhất trên mọi môi trường (máy cá nhân, server, cloud...).
Docker không phải là container — Docker là tool quản lý container. Bản thân container technology là feature của Linux kernel (namespaces + cgroups) có từ trước Docker. Docker là layer phía trên giúp developer dùng dễ.
Kiến trúc Docker
Docker sử dụng kiến trúc client-server. Docker Client liên lạc với Docker Daemon, Docker Daemon thực hiện các tác vụ build, run, distribute container.
┌──────────────────────────────────────────────────────────────┐
│ DOCKER HOST │
│ │
│ ┌──────────────┐ ┌─────────────────────────────┐ │
│ │ Docker CLI │ ──REST──▶│ Docker Daemon (dockerd) │ │
│ │ (docker cmd) │ API │ - Quản lý container │ │
│ └──────────────┘ │ - Build image │ │
│ ▲ │ - Quản lý volume, network │ │
│ │ └──────┬──────────────────────┘ │
│ │ │ │
│ │ ▼ │
│ user types: ┌──────────┐ ┌──────────┐ │
│ docker run nginx │container1│ │container2│ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────┬─────────────────────────┘
│
│ pull image
▼
┌──────────────────────────┐
│ Docker Registry │
│ (Docker Hub, AWS ECR) │
│ │
│ 📦 nginx:latest │
│ 📦 mariadb:11 │
│ 📦 your-app:v1.0 │
└──────────────────────────┘Docker Engine
Docker Engine là phần mềm chính của Docker, bao gồm:
- Docker Daemon (
dockerd): chạy background, quản lý container, image, volume, network. - Docker CLI: command-line interface — tool
dockermà bạn gõ trong terminal. - REST API: cho phép Docker Client (CLI hoặc bất kỳ tool nào) giao tiếp với Daemon.
Quan trọng: Daemon mới làm thật, CLI chỉ gửi lệnh qua API. Hiểu cái này để biết: lúc systemctl stop docker → daemon chết → CLI không làm được gì.
Docker Registry
Kho lưu trữ Docker Image. Ví dụ:
- Docker Hub — public registry, mặc định khi pull (
docker pull nginx). - AWS ECR — private registry trên AWS.
- GitHub Container Registry — private registry trên GitHub.
- Harbor, GitLab Registry — self-hosted.
Khi chạy container, nếu image chưa có trên máy, Docker sẽ tự động pull từ registry.
Docker Objects
4 đối tượng chính Docker quản lý:
Docker Image: tệp chứa mã nguồn, thư viện, môi trường và mọi cài đặt cần thiết để chạy app. Image là template tĩnh (như class trong OOP).
Docker Container: instance của image đang chạy. Hoạt động độc lập, nhẹ, chia sẻ kernel với host. Dễ di chuyển giữa môi trường. Container là instance (như object trong OOP).
Docker Volume: lưu trữ dữ liệu bên ngoài container, tránh mất dữ liệu khi container bị xóa.
Docker Network: quản lý cách các container giao tiếp với nhau hoặc với bên ngoài.
Quan hệ image-container:
Image (template)
nginx:latest (1 image)
│
│ docker run nginx (chạy nhiều lần)
▼
┌───────┬───────┬───────┐
▼ ▼ ▼ ▼
container container container container
web1 web2 web3 web41 image có thể tạo ra nhiều container, mỗi container hoạt động độc lập.
Cơ chế hoạt động — Container chạy như thế nào
Khi bạn chạy docker run nginx:
1. Docker CLI gửi lệnh tới Docker Daemon (qua REST API)
│
▼
2. Daemon kiểm tra image 'nginx' có trên máy không
│
├── Có → bước 3
└── Không → pull từ Docker Registry (Docker Hub)
│
▼
3. Daemon tạo container từ image, thiết lập:
- Namespace (cô lập PID, Network, Filesystem, IPC...)
- Cgroups (giới hạn CPU, RAM, IO)
│
▼
4. Container chạy với environment riêng,
nhưng vẫn dùng chung kernel với hostNamespace — cơ chế cô lập
Namespace là feature kernel Linux, cho phép cô lập tài nguyên giữa các process. Mỗi container có namespace riêng cho:
- PID namespace — container chỉ thấy process của chính nó, không thấy process khác của host. PID 1 trong container không phải systemd, mà là app chính.
- Network namespace — container có network stack riêng (IP, interface, routing table, port).
- Mount namespace — container có filesystem riêng (qua image layers).
- UTS namespace — hostname riêng.
- IPC namespace — inter-process communication riêng.
- User namespace — user/group ID riêng.
Visual: mỗi container như được "che" trong bong bóng riêng:
┌─────────────────── HOST OS (Linux Kernel) ──────────────────┐
│ │
│ PID Namespaces (mỗi container 1 view khác nhau): │
│ │
│ Container A view: Container B view: Host view: │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐│
│ │ PID 1: nginx │ │ PID 1: mysql │ │PID 1: systd││
│ │ PID 8: workr │ │ PID 5: mysqld│ │PID 234: ngx││
│ │ PID 9: workr │ │ │ │PID 456: mys││
│ └──────────────┘ └──────────────┘ │PID 999: ssh││
│ └────────────┘│
│ │
│ Network Namespaces: │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐│
│ │ eth0: 172.0.2│ │ eth0: 172.0.3│ │eth0: 10.0.5││
│ │ lo: 127.0.0.1│ │ lo: 127.0.0.1│ │docker0:172 ││
│ └──────────────┘ └──────────────┘ └────────────┘│
│ │
│ Mount Namespaces: │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐│
│ │/: nginx FS │ │/: mysql FS │ │/: host FS ││
│ │(read-only) │ │(read-only) │ │ ││
│ └──────────────┘ └──────────────┘ └────────────┘│
└──────────────────────────────────────────────────────────────┘
CÙNG kernel, KHÁC view → mỗi container như 1 mini-server riêng# Trong container nginx:
docker exec nginx-1 ps aux
# UID PID PPID ... COMMAND
# root 1 0 ... nginx: master process
# nginx 8 1 ... nginx: worker process
# nginx 9 1 ... nginx: worker process
# Container chỉ thấy nginx — không thấy systemd, sshd, etc của hostCgroups — giới hạn tài nguyên
Cgroups (Control Groups) là feature kernel Linux để giới hạn CPU, RAM, IO của process.
Khi tạo container, Docker đặt nó vào 1 cgroup → giới hạn tài nguyên:
docker run --memory=512m --cpus=2 nginx
# Container này chỉ được dùng tối đa 512MB RAM, 2 CPU coresKhông có cgroups → 1 container ngốn hết RAM host → mọi container khác chết. Đây là lý do luôn đặt resource limit trong production.
Visual cgroups:
Total Host Resources
┌─────────────────────────────┐
│ 16 CPU cores │
│ 64 GB RAM │
└─────────────────────────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│Cgroup A│ │Cgroup B│ │Cgroup C│
│ │ │ │ │ │
│CPU: 2 │ │CPU: 4 │ │CPU: 1 │
│RAM: 4GB│ │RAM: 8GB│ │RAM: 2GB│
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
▼ ▼ ▼
Container A Container B Container C
(nginx) (mysql) (redis)
Tổng cấp: 7 CPU + 14GB RAM (còn 9 CPU + 50GB free)
Nếu A ngốn 100% CPU → chỉ tốn 2 cores của nó.
B, C không bị ảnh hưởng.Kernel sharing — không phải VM
Vì container chia sẻ kernel host:
- ✅ Khởi động nhanh (không boot OS).
- ✅ Nhẹ (vài MB thay vì GB).
- ❌ Không chạy Windows container trên Linux host (kernel khác hoàn toàn).
- ❌ Container Linux chạy trên macOS/Windows thực ra dùng VM ẩn (Docker Desktop tạo VM Linux nhỏ bên dưới).
Tại sao nên dùng Docker
5 lý do chính:
- Chạy đồng nhất trên mọi môi trường — laptop, server, cloud đều y hệt.
- Nhẹ hơn VM — khởi động giây, RAM idle vài MB.
- Dễ scale & triển khai — chạy thêm container = vài giây.
- Quản lý & rollback dễ — image có version, deploy sai rollback bằng pull image cũ.
- Dễ chia sẻ & CI/CD — push image lên registry, mọi nơi pull về dùng.
Khi nào nên dùng Docker
- ✅ Cần môi trường đồng nhất giữa DEV, TEST, PROD.
- ✅ Cần triển khai nhanh và tự động.
- ✅ Làm việc với microservices (mỗi service 1 container).
- ✅ Muốn tiết kiệm tài nguyên hơn so với VM.
- ✅ Cần CI/CD tự động (build image trong pipeline).
Khi nào không nên dùng Docker:
- App single user trên desktop (Word, Photoshop) — không cần.
- Workload cần truy cập hardware trực tiếp (driver kernel).
- Compliance yêu cầu cô lập strict (multi-tenant security) — dùng VM thay.
Cài đặt Docker trên CentOS 9
sudo dnf update -y
# Add Docker official repo
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# Cài Docker Engine
sudo dnf install -y docker-ce docker-ce-cli containerd.io
# Start & enable
sudo systemctl start docker
sudo systemctl enable docker
# Verify
sudo docker version
sudo docker run hello-worldOutput hello-world cuối:
Hello from Docker!
This message shows that your installation appears to be working correctly.→ Docker đã chạy. Đây cũng là test container đầu tiên của bạn.
Cho user thường (không cần sudo)
Mặc định docker cần sudo. Thêm user vào group docker:
sudo usermod -aG docker $USER
newgrp docker
docker version # Không cần sudo nữa⚠️ Security warning: user trong group docker về cơ bản có quyền root tương đương (vì Docker daemon chạy root, ai gọi được daemon = root). Production phải cẩn thận group này.
Câu lệnh check trạng thái
docker info # Trạng thái chung của Docker Engine
docker --help # Trợ giúp lệnhCấu trúc cú pháp lệnh Docker
docker <management_command> <command> [options]3 phần:
- Management command — đối tượng chính cần quản trị:
container,image,network,volume. - Command — chức năng phụ gắn vào đối tượng chính:
run,ls,stop,rm. - Options — tham số phụ:
-d,-p,--name.
Ví dụ:
docker container run --publish 8080:80 --detach --name web nginx
↑ ↑ ↑ ↑ ↑ ↑
management command option option option image⚠️ Legacy syntax vẫn dùng được nhưng không khuyến nghị:
docker run -d -p 8080:80 nginx # cũ, không có "container"
docker container run -d -p 8080:80 nginx # mới, đầy đủ2 cái cho ra kết quả như nhau. Modern Docker khuyến khích explicit container/image/network/volume để dễ đọc.
Container đầu tiên
docker container run --publish 8080:80 --detach nginxPhân tích:
docker container run— tạo container mới và chạy ngay.--publish 8080:80(hoặc-p) — phơi (publish) port. Map port8080của host → port80của container. User truy cậphttp://<host>:8080sẽ vào nginx trong container.--detach(hoặc-d) — chạy ẩn (background), không chiếm terminal.nginx— tên image. Docker tự pull từ Docker Hub nếu chưa có.
Đặt tên container:
docker container run --publish 8080:80 --name web-1 --detach nginx--name web-1 — đặt tên web-1 thay vì để Docker tự sinh tên random.
Test: vào browser http://localhost:8080 → thấy trang welcome của nginx.
Lệnh kiểm tra container
docker container ls # Liệt kê container đang chạy
docker container ls -a # Liệt kê tất cả (kể cả đã dừng)Output:
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
abc123def456 nginx "/docker-entrypoint.…" Up 2 minutes 0.0.0.0:8080->80/tcp web-1Dừng/start/xoá container
docker container stop web-1 # Dừng (gracefully)
docker container start web-1 # Start lại
docker container restart web-1 # Restart
docker container rm web-1 # Xoá (phải stop trước)
docker container rm -f web-1 # Force xoá (kể cả đang chạy)Có thể dùng container ID hoặc tên. ID chỉ cần vài ký tự đầu (unique là đủ):
docker container stop abc1 # OK nếu chỉ có 1 container ID bắt đầu bằng abc1Container Lifecycle — vòng đời
┌───────────────────────────────────────┐
│ │
▼ │
┌──────────────┐ docker create ┌─────────────┐
│ (no exist) │ ───────────────────► │ Created │
└──────────────┘ └──────┬──────┘
▲ │
│ │ docker start
│ docker rm ▼
│ ┌─────────────┐
│ ┌──pause───►│ Running │◄──┐
│ │ └──────┬──────┘ │
│ ┌──────┴──────┐ │ │
│ │ Paused │ │ docker │
│ └──────┬──────┘ │ stop │ docker
│ │ unpause │ │ start
│ └──────────────────┤ │
│ ▼ │
│ ┌─────────────┐ │
└──────────────────────────────│ Stopped │────┘
docker rm └─────────────┘
▲
│ (auto khi
│ app exit
│ hoặc OOM)
┌──────────────────────────┐
docker run ──────────►│ Created → Running │
(shortcut combo) └──────────────────────────┘5 trạng thái:
- Created — đã
docker create, chưa start. - Running — đang chạy (
docker run= create + start). - Paused — tạm dừng, RAM giữ nguyên (
docker pause). - Stopped — đã dừng, RAM mất. Có thể
startlại. - Removed — đã
rm, biến mất hoàn toàn.
docker run = shortcut của docker create + docker start. Hiếm khi dùng create riêng (trừ khi cần prepare container nhưng chưa muốn chạy).
Image vs Container — đừng nhầm
| Image | Container |
|---|---|
| Ứng dụng (file đóng gói) | Process khi chạy ứng dụng đó |
| Tĩnh, không thay đổi | Có state, đang chạy |
| Lưu trên registry | Chạy trên host |
| 1 image | Tạo được nhiều container từ 1 image |
# 1 image, 3 container chạy song song
docker container run --name web-1 -p 8081:80 -d nginx
docker container run --name web-2 -p 8082:80 -d nginx
docker container run --name web-3 -p 8083:80 -d nginx
docker container ls
# Thấy 3 container, đều từ image nginx, ports khác nhauLiệt kê image trên máy:
docker image ls
# REPOSITORY TAG IMAGE ID CREATED SIZE
# nginx latest abc123... 2 weeks ago 142MB
# hello-world latest def456... 6 months ago 13.3kBVào shell container — debug bên trong
Có 2 trường hợp:
TH1: Container đang chạy — vào shell hiện tại
Dùng docker container exec:
docker container exec -it web-1 bash
# root@abc123:/# ← bạn đang ở trong container
# Test:
ls /
ps aux # chỉ thấy nginx process, không thấy host
exit # thoát shell, container vẫn chạyexec— execute command trong container đang chạy.-it— interactive + tty (cần thiết để có shell tương tác).bash— shell muốn vào.
⚠️ Một số image tối giản (alpine) không có bash, chỉ có sh:
docker container exec -it alpine-container shTH2: Tạo container mới và vào shell luôn
Dùng docker container run -it ... bash:
docker container run -it ubuntu bash
# root@abc123:/#
# Container này tạo riêng để debug, exit sẽ tắt luôn- Image OS (
ubuntu,centos,debian) → bash là CMD mặc định, có thể vào shell ngay. - Image app (
nginx,mariadb) → CMD mặc định là chạy app, phải thêmbashđể override.
COMMAND — lệnh khởi tạo container
COMMAND là lệnh khởi tạo đầu tiên khi container chạy. Mỗi image có CMD default:
nginx→ CMD =nginx -g 'daemon off;'ubuntu→ CMD =bash
Khi run, có thể override:
docker container run nginx # CMD mặc định: chạy nginx
docker container run nginx bash # Override: chạy bash thay vì nginx
# → container exit ngay vì bash thoát
docker container run -it nginx bash # Override + tty → vào shellThoát terminal container
2 cách thoát khi -it:
exit— thoát terminal VÀ tắt luôn container (vì shell là process chính, shell tắt → container tắt).Ctrl+P, Ctrl+Q— detach, không tắt container. Container vẫn chạy background, bạn có thểdocker attachlại sau.
docker container run -it ubuntu bash
# root@abc:/# echo "đang trong container"
# root@abc:/# [Ctrl+P, Ctrl+Q] ← detach
docker container ls # ubuntu vẫn chạy
docker container attach abc # attach lại vào containerLưu ý: chỉ image OS như ubuntu mới chạy được -it bash. Image như nginx đã chạy nginx rồi, override bash sẽ không có ý nghĩa nhiều (nginx không chạy).
Monitor container
Logs
docker container logs web-1 # Xem log
docker container logs -f web-1 # Follow (như tail -f)
docker container logs --tail 100 web-1 # 100 dòng cuối
docker container logs --since 5m web-1 # 5 phút gần đâydocker logs đọc stdout/stderr của process chính trong container. Đây là lý do app trong container nên log ra stdout, không ghi file (xem section "12-factor app").
Process list trong container
docker container top web-1
# UID PID PPID ... CMD
# root 1234 1200 ... nginx: master
# nginx 1245 1234 ... nginx: worker
# nginx 1246 1234 ... nginx: workerHiển thị process đang chạy trong container (PID trong container namespace).
Lưu ý: PID này khác với PID host. Trên host, ps aux | grep nginx sẽ thấy PID khác:
ps aux | grep nginx
# root 25543 ... nginx: master process→ Container top thấy PID 1234, host ps thấy PID 25543 — cùng process, khác namespace nên PID khác nhau.
Performance — CPU, RAM, IO
docker container stats # Live tất cả container
docker container stats web-1 # Live 1 container cụ thể
docker container stats --no-stream # Snapshot, không live updateOutput:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
abc123 web-1 0.01% 3.7MiB / 1.93GiB 0.19% 1.2kB/0B 0B/0B→ Container web-1 đang dùng 0.01% CPU, 3.7MB RAM. Rất nhẹ — đặc trưng container.
Inspect — xem cấu hình chi tiết
docker container inspect web-1Output JSON dài, chứa toàn bộ config: IP, mount, network, env vars, command, restart policy... Dùng --format để filter:
docker container inspect --format '{{.NetworkSettings.IPAddress}}' web-1
# 172.17.0.2
docker container inspect --format '{{.State.Status}}' web-1
# runningHữu ích trong script automation.
Câu chuyện thực tế
Lần đầu mình thuyết phục team chuyển sang Docker, sếp hỏi:
"Container có gì hay hơn VM? Tao thấy giống nhau mà?"
Mình demo:
Test 1: Khởi động.
# VM:
virsh start ubuntu-vm
# ... khoảng 25 giây sau ...
# VM sẵn sàng
# Container:
docker run -d --name test ubuntu sleep 3600
# < 1 giâySếp: "OK, container nhanh hơn."
Test 2: RAM idle.
# 1 VM Ubuntu chạy không làm gì
free -h trong VM
# Used: ~ 400MB
# 1 container Ubuntu chạy sleep
docker stats --no-stream test
# MEM USAGE: 1.5MiBSếp: "...container ngốn ít hơn 250 lần?"
Test 3: Scale.
# Tạo 50 container Ubuntu:
for i in $(seq 1 50); do
docker run -d --name ub-$i ubuntu sleep 3600
done
# < 30 giây xong
free -h # Host vẫn còn 80% RAM freeSếp: "50 con? Trên VMware tao mở 5 con đã đầy RAM rồi."
Đó là moment quyết định chuyển stack sang Docker. 1 năm sau, team có 200+ container chạy trên 3 server, thay vì 30 VM trên 10 server. Tiết kiệm 70% hardware cost.
Bài học:
Container khác VM bản chất, không chỉ "nhẹ hơn". Hiểu kernel sharing là gì → giải thích được hết các trade-off.
Demo bằng số liệu, không phải lý thuyết. Sếp không quan tâm "namespace là gì", quan tâm "tiết kiệm bao nhiêu tiền".
Container không thay thế VM hoàn toàn. Lúc đó vẫn giữ 5 VM cho legacy app + Windows services. Container không phải silver bullet.
Lab — thực hành đầy đủ
Lab 1: Run nginx + browser test
# Pull và run
docker container run --publish 8080:80 --name web --detach nginx
# Test
curl http://localhost:8080
# <html>...Welcome to nginx!...
# Xem log
docker container logs web
# Xem stats
docker container stats --no-stream web
# Stop và cleanup
docker container stop web
docker container rm webLab 2: Vào shell container
# Chạy ubuntu, vào shell
docker container run -it --name lab-ubuntu ubuntu bash
# Trong container:
apt update && apt install -y curl
curl https://google.com -I
# HTTP/2 301
# Thoát mà giữ container chạy: Ctrl+P, Ctrl+Q
# Bên ngoài:
docker container ls
# Thấy lab-ubuntu vẫn UP
# Attach lại:
docker container attach lab-ubuntu
# Exit hẳn:
exit
docker container ls -a
# Status: ExitedLab 3: Nhiều container từ 1 image
# 3 nginx khác port
docker container run -d -p 8081:80 --name web-1 nginx
docker container run -d -p 8082:80 --name web-2 nginx
docker container run -d -p 8083:80 --name web-3 nginx
docker container ls
# 3 container, đều IMAGE nginx
# Customize từng cái:
docker container exec web-1 sh -c "echo 'Web 1' > /usr/share/nginx/html/index.html"
docker container exec web-2 sh -c "echo 'Web 2' > /usr/share/nginx/html/index.html"
docker container exec web-3 sh -c "echo 'Web 3' > /usr/share/nginx/html/index.html"
# Test
curl http://localhost:8081 # Web 1
curl http://localhost:8082 # Web 2
curl http://localhost:8083 # Web 3
# Cleanup
docker container rm -f web-1 web-2 web-3Lab 4: Resource limit
# Giới hạn 100MB RAM, 0.5 CPU
docker container run -d \
--name limited \
--memory=100m \
--cpus=0.5 \
nginx
docker container stats --no-stream limited
# MEM LIMIT: 100MiB
# CPU %: max 50%
docker container rm -f limitedLab 5: Monitor — quan sát PID khác giữa host và container
docker container run -d --name observed nginx
# PID trong container
docker container top observed
# PID 1: nginx master
# PID trên host
docker container inspect --format '{{.State.Pid}}' observed
# 12345 (ví dụ)
# Trên host, process này tồn tại
ps aux | grep 12345
# Cleanup
docker container rm -f observed→ Cùng nginx, container thấy PID 1, host thấy PID 12345. PID namespace là đây.
Pitfalls
Tưởng container là VM nhỏ — dẫn tới nhiều hiểu lầm. Container = process bị cô lập, không có kernel riêng.
Quên
-d(detach) → container chạy foreground, đóng terminal là container tắt. Production luôn-d.Quên
-pmap port → service trong container không tiếp cận được từ ngoài.docker run nginx(không-p) → port 80 trong container, nhưng host không expose.Quên
--name→ Docker tự sinh tên random kiểuvibrant_einstein→ khó tham chiếu, khó script.docker runthay vìdocker startcho container đã tồn tại → tạo container mới (trùng tên fail). Đã có container thìdocker start <name>, đừngdocker runlại.exittrong shell container → tắt luôn container. Muốn giữ chạy thìCtrl+P, Ctrl+Q.Mount sai path → app trong container không thấy data. Sẽ học chi tiết ở Bài 03.
Không set resource limit → 1 container ngốn hết RAM, mọi container khác chết. Production phải
--memoryvà--cpus.Image to GB không hiểu sao → dùng base image lớn (
ubuntu70MB) thay vì5MB). Bài 02 sẽ tối ưu.alpine(User trong group
dockermà không biết = root → security risk nghiêm trọng. Production limit group này.docker logskhông thấy gì → app log vào file thay vì stdout. Container app phải log stdout/stderr (12-factor app).Container Linux trên Windows/Mac thực ra dùng VM ẩn — Docker Desktop tạo VM Linux nhỏ. Performance kém hơn Linux native.
Tóm tắt
- Container ≠ VM. Container là process bị cô lập (qua namespace + cgroups), chia sẻ kernel với host.
- Docker = tool quản lý container, không phải bản thân container technology.
- Kiến trúc Docker: Client (CLI) ↔ Daemon ↔ Registry. CLI gửi lệnh, Daemon làm việc thật.
- 4 Docker objects: Image (template), Container (instance), Volume (data), Network (kết nối).
docker container run -d -p HOST:CONTAINER --name X image— chạy container thông dụng nhất.docker container exec -it X bash— vào shell container đang chạy.exittắt container,Ctrl+P,Qdetach. Nhớ kỹ.docker logs -f,docker stats,docker top,docker inspect— bộ tool monitor cơ bản.- PID 1 trong container = app chính, không phải systemd. Container không có init system.
- Production luôn
--memory,--cpusđể giới hạn tài nguyên.
Bài tiếp: 02 — Image & Dockerfile: từ Docker Hub đến image của riêng mình.