Giao diện
Container Lifecycle CLI — run, stop, rm, ps, logs, exec
Thứ Sáu, 2 giờ sáng. Cảnh báo PagerDuty vang lên: "Payment Service — 502 Bad Gateway". Đội fintech của bạn đang xử lý 40,000 giao dịch/phút. Mỗi giây downtime là tiền thật mất đi. Bạn SSH vào production server, chạy docker ps — container payment-api biến mất khỏi danh sách. docker ps -a hiện nó ở trạng thái Exited (137). Bạn cần xem log, restart container, exec vào kiểm tra config — và bạn cần làm tất cả trong vòng 3 phút trước khi khách hàng bắt đầu gọi hotline.
Đây không phải kịch bản giả tưởng. Đây là thứ xảy ra hàng đêm ở các hệ thống production thực tế. Bài này trang bị cho bạn 6 lệnh Docker CLI cốt lõi để quản lý toàn bộ vòng đời container — từ khởi tạo đến dọn dẹp. Không chỉ cú pháp, mà hiểu tại sao mỗi lệnh hoạt động như vậy, để khi production cháy lúc 3 giờ sáng, bạn phản xạ được ngay.
🎯 Mục tiêu
- Nắm vững state machine của container: Created → Running → Paused → Stopped → Removed — và lệnh nào chuyển giữa các trạng thái
- Thành thạo
docker runvới các flag quan trọng:-d,--name,-p,-e,-v,--rm,-it - Dùng
docker ps,docker logs,docker execđể giám sát và debug container đang chạy - Hiểu quy trình cleanup:
docker stop→docker rm→docker system prune - Nắm cơ chế signal propagation bên dưới
docker stop— tại sao SIGTERM trước, SIGKILL sau
Concept — Container States: Máy Trạng Thái
Trước khi gõ bất kỳ lệnh nào, bạn cần hình dung container như một finite state machine — mỗi container luôn ở đúng một trạng thái tại mỗi thời điểm, và chỉ có thể chuyển trạng thái thông qua các lệnh cụ thể.
docker unpause
┌──────────────────┐
│ │
▼ │
┌─────────┐ docker start ┌─────────┐ docker pause ┌──────────┐
│ Created │───────────────▶│ Running │────────────────▶│ Paused │
└─────────┘ └─────────┘ └──────────┘
▲ │
│ │ docker stop / docker kill
│ ▼
│ ┌─────────┐ docker rm ┌─────────┐
│ │ Stopped │───────────────▶│ Removed │
│ │(Exited) │ │ (Gone) │
│ └─────────┘ └─────────┘
│ │
│ docker restart │
└──────────────────────────┘
docker create → Created
docker run → Created + Started (2 bước gộp 1)
docker rm -f → Bất kỳ state nào → Removed (force)Giải thích từng trạng thái
| Trạng thái | Ý nghĩa | Lệnh vào | Lệnh ra |
|---|---|---|---|
| Created | Container được tạo nhưng chưa chạy. Filesystem đã mount, config đã set. | docker create | docker start, docker rm |
| Running | Process chính (PID 1) đang chạy bên trong container. | docker start, docker run | docker stop, docker pause, docker kill |
| Paused | Process bị đóng băng (SIGSTOP). Không tiêu thụ CPU nhưng vẫn giữ memory. | docker pause | docker unpause |
| Stopped | Process chính đã exit. Container vẫn tồn tại trên disk (filesystem, logs). | docker stop, crash | docker start, docker rm |
| Removed | Container bị xóa hoàn toàn. Không thể recover. | docker rm | — |
⚠️ State "Stopped" ≠ "Removed"
Đây là nguồn gốc của 90% container zombie. Khi container stop, nó vẫn chiếm disk space — filesystem layer, logs, metadata đều còn nguyên. Chỉ docker rm mới giải phóng thực sự. Rất nhiều engineer mới nghĩ docker stop là xong — sai.
docker run — Con Dao Thụy Sĩ
docker run là lệnh bạn dùng nhiều nhất. Nó gộp docker create + docker start thành một bước, kèm theo hàng chục flag điều khiển hành vi container.
Chạy container ở chế độ detached (background)
bash
# Chạy nginx ở background, đặt tên "myapp", map port 8080 host → 80 container
docker run -d --name myapp -p 8080:80 nginx
# Phân tích từng flag:
# -d → detached mode — container chạy nền, trả lại terminal cho bạn
# --name myapp → đặt tên dễ nhớ thay vì ID ngẫu nhiên (ví dụ: a3f8c2b1d9e0)
# -p 8080:80 → host port 8080 → container port 80
# nginx → image name (tự pull từ Docker Hub nếu chưa có local)Sau khi chạy, bạn có thể truy cập http://localhost:8080 trên máy host để thấy trang chào Nginx.
Truyền biến môi trường và mount volume
bash
# Chạy PostgreSQL với env vars và volume
docker run -d \
--name mydb \
-e POSTGRES_USER=admin \
-e POSTGRES_PASSWORD=supersecret \
-e POSTGRES_DB=payments \
-v pgdata:/var/lib/postgresql/data \
-p 5432:5432 \
postgres:16-alpine
# -e KEY=VALUE → set biến môi trường bên trong container
# -v name:path → mount named volume — dữ liệu persist ngay cả khi container bị rm⚠️ Cạm bẫy
Lệnh docker run -e POSTGRES_PASSWORD=supersecret ở trên chỉ dùng cho dev/learning. Trong production, KHÔNG BAO GIỜ đặt password trực tiếp trong CLI — nó hiện rõ trong docker inspect, process list (ps aux), và shell history.
bash
# ❌ Sai — secret lộ trong process list và shell history
docker run -e DB_PASSWORD=MyS3cretP@ss myapp
# ✅ Đúng — dùng file hoặc Docker secrets
docker run --env-file .env myapp
# Hoặc với Docker Swarm:
echo "MyS3cretP@ss" | docker secret create db_password -Auto-cleanup với --rm
bash
# Container tự xóa khi exit — hoàn hảo cho task ngắn
docker run --rm alpine echo "Hello from Alpine"
# Output: Hello from Alpine
# Container đã biến mất — docker ps -a sẽ không thấy nó
# Dùng --rm cho script chạy-một-lần
docker run --rm -v $(pwd):/app -w /app node:20-alpine npm test
# Chạy test rồi tự dọn — không tạo rácChế độ interactive — khi bạn cần shell
bash
# Mở shell Alpine interactive
docker run --rm -it alpine sh
# -i → interactive — giữ STDIN mở (để bạn gõ lệnh)
# -t → tty — cấp pseudo-terminal (để output có format, màu sắc)
# sh → override CMD mặc định, chạy shell thay vì process chính của image
# Bên trong container:
/ # whoami
root
/ # cat /etc/os-release
NAME="Alpine Linux"
/ # exit
# → Container exit + tự xóa (nhờ --rm)💡 Khi nào dùng -it vs -d?
-d(detached): Cho service chạy nền lâu dài — web server, database, message queue-it(interactive): Cho debug, explore filesystem, chạy one-off command--rm: Kết hợp với cả hai khi bạn không cần giữ container sau khi exit
Quy tắc nhớ: Service → -d. Debug → -it --rm.
Tổng hợp các flag quan trọng của docker run
| Flag | Viết tắt | Ý nghĩa | Ví dụ |
|---|---|---|---|
--detach | -d | Chạy nền | -d |
--name | — | Đặt tên container | --name myapp |
--publish | -p | Map port host:container | -p 8080:80 |
--env | -e | Set biến môi trường | -e NODE_ENV=production |
--volume | -v | Mount volume/bind mount | -v data:/app/data |
--rm | — | Tự xóa khi exit | --rm |
--interactive | -i | Giữ STDIN mở | -i |
--tty | -t | Cấp pseudo-terminal | -t |
--memory | -m | Giới hạn RAM | -m 512m |
--cpus | — | Giới hạn CPU | --cpus 1.5 |
--restart | — | Chính sách restart | --restart unless-stopped |
docker ps — Giám Sát Container
docker ps là "đôi mắt" của bạn. Nó cho biết container nào đang chạy, port nào đang mở, và container đã chạy bao lâu.
Xem container đang chạy
bash
docker ps
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# a3f8c2b1d9e0 nginx "/docker-entrypoint.…" 5 minutes ago Up 5 minutes 0.0.0.0:8080->80/tcp myapp
# b7d2e4f6a8c1 postgres:16-alpine "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 0.0.0.0:5432->5432/tcp mydbXem TẤT CẢ container (kể cả đã stop)
bash
docker ps -a
# CONTAINER ID IMAGE STATUS NAMES
# a3f8c2b1d9e0 nginx Up 5 minutes myapp
# b7d2e4f6a8c1 postgres Up 3 minutes mydb
# c9e1f3a5b7d2 alpine Exited (0) 10 minutes ago focused_newton ← container cũ chưa rm
# d1f3a5b7c9e2 redis Exited (137) 2 hours ago old_cache ← bị kill (137 = SIGKILL)Exit code giải mã:
- 0 → exit bình thường (success)
- 1 → lỗi application
- 137 → bị SIGKILL (OOM killer hoặc
docker kill) - 143 → nhận SIGTERM (từ
docker stop) và exit gracefully
Format tùy chỉnh — output gọn gàng
bash
# Chỉ hiện tên, trạng thái, port
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# NAMES STATUS PORTS
# myapp Up 5 minutes 0.0.0.0:8080->80/tcp
# mydb Up 3 minutes 0.0.0.0:5432->5432/tcp
# Xuất JSON — hữu ích cho scripting
docker ps --format json | jq '.Names'
# "myapp"
# "mydb"
# Lọc container theo trạng thái
docker ps -a --filter "status=exited"
# Chỉ hiện container đã stop — tiện cho cleanup
# Chỉ lấy container ID — pipe vào lệnh khác
docker ps -aq --filter "status=exited"
# c9e1f3a5b7d2
# d1f3a5b7c9e2docker logs — Debug Bằng Mắt
Khi container gặp lỗi, docker logs là nơi đầu tiên bạn kiểm tra. Nó hiển thị mọi thứ mà process bên trong container ghi ra stdout và stderr.
Xem log cơ bản
bash
# Xem toàn bộ log của container
docker logs myapp
# Xem 100 dòng cuối — tránh bị ngập bởi hàng triệu dòng
docker logs --tail 100 myappFollow log real-time
bash
# Theo dõi log liên tục — giống tail -f
docker logs -f myapp
# 172.17.0.1 - - [15/Jan/2025:02:15:33 +0000] "GET / HTTP/1.1" 200 615
# 172.17.0.1 - - [15/Jan/2025:02:15:34 +0000] "GET /api/health HTTP/1.1" 200 2
# 172.17.0.1 - - [15/Jan/2025:02:15:35 +0000] "POST /api/payment HTTP/1.1" 500 89 ← BUG
# ^C để dừngLọc log theo thời gian
bash
# Xem log 5 phút gần nhất — critical khi debug incident
docker logs --since 5m myapp
# Xem log trong khoảng thời gian cụ thể
docker logs --since 2025-01-15T02:00:00 --until 2025-01-15T02:30:00 myapp
# Kết hợp: 50 dòng cuối + follow
docker logs --tail 50 -f myappThêm timestamp vào log
bash
docker logs -t myapp
# 2025-01-15T02:15:33.000000000Z 172.17.0.1 - ... "GET / HTTP/1.1" 200
# 2025-01-15T02:15:35.000000000Z 172.17.0.1 - ... "POST /api/payment HTTP/1.1" 500💡 Chiến thuật debug log trong production incident
Khi nhận alert lúc 2 giờ sáng, đây là quy trình 3 bước:
bash
# Bước 1: Xem container có đang chạy không
docker ps -a --filter name=payment-api
# Bước 2: Xem log 10 phút gần nhất với timestamp
docker logs --since 10m -t payment-api
# Bước 3: Tìm dòng lỗi cụ thể
docker logs --since 10m payment-api 2>&1 | grep -i "error\|fatal\|panic"Mẹo: redirect 2>&1 vì nhiều app ghi lỗi ra stderr, nhưng grep mặc định chỉ đọc stdout.
docker exec — Đột Nhập Container Đang Chạy
docker exec cho phép bạn chạy lệnh bên trong một container đang Running. Đây là công cụ debug mạnh nhất khi bạn cần kiểm tra filesystem, network, hoặc config bên trong container.
Mở shell interactive
bash
# Mở shell bash/sh bên trong container đang chạy
docker exec -it myapp sh
# Bên trong container:
# ls /usr/share/nginx/html/
# index.html 50x.html
# cat /etc/nginx/nginx.conf | head -20
# exitChạy lệnh one-off (không cần shell)
bash
# Xem config nginx mà không cần mở shell
docker exec myapp cat /etc/nginx/nginx.conf
# Kiểm tra DNS resolution bên trong container
docker exec myapp nslookup google.com
# Kiểm tra biến môi trường
docker exec myapp env | grep -i postgres
# POSTGRES_USER=admin
# POSTGRES_DB=payments
# Kiểm tra process đang chạy bên trong container
docker exec myapp ps aux
# PID USER TIME COMMAND
# 1 root 0:00 nginx: master process
# 29 nginx 0:00 nginx: worker process
# 30 nginx 0:00 nginx: worker processChạy lệnh với user khác
bash
# Exec với quyền root (khi container chạy với user khác)
docker exec -u root myapp apt-get update
# Exec với user cụ thể
docker exec -u www-data myapp whoami
# www-data🔥 Anti-pattern: Dùng docker exec thay SSH trong production
Tuyệt đối không dùng docker exec như SSH replacement cho production workload.
bash
# ❌ Anti-pattern — thay đổi file trong container đang chạy
docker exec -it payment-api vi /app/config.json
# Hậu quả:
# 1. Thay đổi mất khi container restart (container là ephemeral)
# 2. Không có audit trail — ai sửa gì, lúc nào?
# 3. Tạo config drift — container A khác container B
# ✅ Đúng cách — build image mới với config đúng, deploy lại
# Sửa config trong source code → rebuild image → redeploy
docker build -t payment-api:v1.2.1 .
docker stop payment-api && docker rm payment-api
docker run -d --name payment-api payment-api:v1.2.1Nguyên tắc vàng: Container là immutable. Mọi thay đổi phải đi qua image build pipeline. docker exec chỉ dùng để đọc và debug, không bao giờ để sửa.
docker stop / docker rm — Dọn Dẹp Có Trách Nhiệm
docker stop — Tắt container lịch sự
bash
# Gửi SIGTERM → chờ 10 giây → SIGKILL nếu chưa exit
docker stop myapp
# Tùy chỉnh thời gian chờ (ví dụ: 30 giây cho app cần flush data)
docker stop -t 30 myapp
# Stop nhiều container cùng lúc
docker stop myapp mydb redis_cachedocker rm — Xóa container
bash
# Xóa container đã stop
docker rm myapp
# ❌ Không xóa được container đang chạy
docker rm mydb
# Error: cannot remove running container — stop first
# Force remove — stop + rm trong 1 lệnh
docker rm -f mydb
# Container bị SIGKILL ngay lập tức — KHÔNG có grace period
# Xóa tất cả container đã stop
docker rm $(docker ps -aq --filter "status=exited")⚠️ Cạm bẫy
Mỗi lần bạn docker run mà không có --rm, container sẽ ở lại sau khi exit. Sau vài tuần develop/test, bạn có hàng trăm container zombie:
bash
# Kiểm tra "thiệt hại"
docker ps -a | wc -l
# 347 ← 346 container zombie đang ngốn disk
# Mỗi container giữ lại: writable layer, logs, metadata
docker system df
# TYPE TOTAL ACTIVE SIZE RECLAIMABLE
# Containers 346 2 12.4GB 12.1GB (97%) ← 12GB rác!
# Images 45 8 8.7GB 6.2GB (71%)
# Giải pháp nhanh: xóa tất cả container stopped
docker container prune
# WARNING! This will remove all stopped containers.
# Are you sure you want to continue? [y/N] y
# Deleted Containers:
# c9e1f3a5b7d2... d1f3a5b7c9e2... (344 containers deleted)
# Total reclaimed space: 12.1GBThói quen tốt: Luôn dùng --rm cho container tạm thời. Với service lâu dài, schedule cleanup hàng tuần.
docker system prune — Nút bấm hạt nhân
bash
# Xóa TẤT CẢ: stopped containers, unused networks, dangling images, build cache
docker system prune
# Thêm -a để xóa cả images không được container nào dùng
docker system prune -a
# Thêm --volumes để xóa cả volumes không dùng (CẨN THẬN — mất dữ liệu!)
docker system prune -a --volumes🔥 docker system prune --volumes xóa cả dữ liệu!
Nếu bạn có named volume chứa database data mà không container nào đang mount, --volumes sẽ xóa sạch nó. Luôn kiểm tra docker volume ls trước khi prune.
🔥 Thực Chiến: Debug Payment Service Lúc 2 Giờ Sáng
🏦 Business Scenario — Fintech Production Incident
Bối cảnh
Đội fintech vận hành payment-api container xử lý thanh toán qua API gateway. Lúc 2:13 AM, Grafana alert: error rate tăng từ 0.1% lên 15%. Khách hàng chuyển tiền bị lỗi. SLA yêu cầu recovery trong 5 phút.
Quy trình debug thực tế
bash
# === PHÚT 1: Xác nhận tình trạng ===
# Container có đang chạy không?
docker ps --filter name=payment-api --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# NAMES STATUS PORTS
# payment-api Up 3 days 0.0.0.0:8443->8443/tcp
# → Container vẫn chạy. Vấn đề ở bên trong.
# Resource usage — OOM?
docker stats payment-api --no-stream
# CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O
# payment-api 89% 1.87GiB / 2GiB 93% 1.2GB / 890MB
# → Memory gần limit! Có thể memory leak.
# === PHÚT 2: Xem log tìm root cause ===
# Log 5 phút gần nhất, lọc error
docker logs --since 5m payment-api 2>&1 | grep -i "error\|exception\|fatal"
# 2025-01-15T02:10:12Z ERROR: Connection pool exhausted — max 20 connections reached
# 2025-01-15T02:10:15Z ERROR: Failed to acquire DB connection within 5s timeout
# 2025-01-15T02:11:33Z FATAL: java.lang.OutOfMemoryError: Java heap space
# → Root cause: DB connection pool exhausted → memory leak từ connection chưa release
# === PHÚT 3: Exec vào kiểm tra chi tiết ===
# Kiểm tra connection pool metrics
docker exec payment-api curl -s localhost:8080/actuator/health | jq .
# {
# "status": "DOWN",
# "components": {
# "db": { "status": "DOWN", "details": { "error": "Connection pool exhausted" } }
# }
# }
# Kiểm tra số connection thực tế
docker exec payment-api sh -c "ss -tn | grep 5432 | wc -l"
# 20 ← Max pool size reached, tất cả bị giữ
# Xem heap memory
docker exec payment-api jcmd 1 VM.native_memory summary
# === PHÚT 4: Khắc phục tạm thời ===
# Restart container để giải phóng connection pool
docker restart payment-api
# Verify recovery
sleep 10
docker logs --tail 5 payment-api
# 2025-01-15T02:17:45Z INFO: Application started on port 8443
# 2025-01-15T02:17:46Z INFO: DB connection pool initialized: 5/20
docker exec payment-api curl -s localhost:8080/actuator/health | jq .status
# "UP"
# === SAU INCIDENT: Tạo ticket fix connection leak ===
# Root cause: missing connection.close() trong payment transaction handler
# Long-term fix: code review + add connection timeout + monitoring alertBài học rút ra
docker statslà thứ đầu tiên kiểm tra — memory/CPU anomaly cho manh mối ngaydocker logs --since+greplọc nhanh root cause từ hàng triệu dòng logdocker exec+ health endpoint xác nhận component nào faildocker restartlà bandage tạm thời — luôn tìm và fix root cause sau incident
🧩 Bài Tập Parsons: Sắp Xếp Lệnh Debug
Bạn nhận alert: container order-service trả HTTP 500. Sắp xếp các lệnh sau theo đúng thứ tự để debug và khắc phục:
A. docker exec order-service curl -s localhost:8080/health
B. docker logs --since 5m order-service 2>&1 | grep -i error
C. docker ps -a --filter name=order-service
D. docker restart order-service
E. docker stats order-service --no-stream
F. docker logs --tail 5 order-service💡 Đáp án đúng
C → E → B → A → D → F
- C —
docker ps -a— Xác nhận container có tồn tại và đang ở trạng thái nào - E —
docker stats— Kiểm tra resource usage (CPU, memory) để phát hiện anomaly - B —
docker logs --since 5m | grep error— Tìm root cause trong log gần đây - A —
docker exec curl health— Kiểm tra health endpoint từ bên trong container - D —
docker restart— Khắc phục tạm thời sau khi đã hiểu nguyên nhân - F —
docker logs --tail 5— Xác nhận container đã restart thành công
Nguyên tắc: Quan sát trước → Chẩn đoán → Hành động → Xác nhận.
Under the Hood — Signal Propagation: Tại Sao docker stop Gửi SIGTERM Trước?
Khi bạn chạy docker stop myapp, Docker không kill container ngay lập tức. Quy trình bên dưới phức tạp hơn bạn tưởng — và hiểu nó là sự khác biệt giữa graceful shutdown và data corruption.
Quy trình chi tiết
Bạn gõ: docker stop myapp
│
▼
┌─────────────────────────────────┐
│ Docker Daemon nhận request │
│ Gửi SIGTERM (signal 15) │──────────▶ PID 1 trong container
│ đến PID 1 của container │ (process chính)
└─────────────────────────────────┘
│ │
│ Bắt đầu đếm ngược │
│ (mặc định: 10 giây) ▼
│ ┌──────────────────────────┐
│ │ App nhận SIGTERM: │
│ │ - Flush buffer/cache │
│ │ - Close DB connections │
│ │ - Finish pending request│
│ │ - Write final log │
│ │ - Exit với code 0 │
│ └──────────────────────────┘
│
│ Nếu hết 10 giây mà PID 1 chưa exit...
▼
┌─────────────────────────────────┐
│ Docker Daemon gửi SIGKILL │──────────▶ Process bị kernel hủy
│ (signal 9) — KHÔNG THỂ BẮT │ ngay lập tức. Không cleanup.
│ KHÔNG THỂ BỎ QUA │ Exit code: 137
└─────────────────────────────────┘Tại sao điều này quan trọng?
Nếu app không handle SIGTERM, nó sẽ bị SIGKILL sau 10 giây — nghĩa là:
- Database transaction đang dở → có thể corrupt data
- File đang ghi → file bị truncate
- HTTP request đang xử lý → client nhận connection reset
- Message queue → message bị mất, không ack
Ví dụ: App Node.js handle SIGTERM đúng cách
javascript
const http = require('http');
const server = http.createServer(handler);
const db = require('./database');
// Graceful shutdown handler
process.on('SIGTERM', async () => {
console.log('SIGTERM received. Starting graceful shutdown...');
// 1. Ngừng nhận connection mới
server.close(() => {
console.log('HTTP server closed. No new connections.');
});
// 2. Chờ request đang xử lý hoàn thành (tối đa 8 giây)
await Promise.race([
waitForPendingRequests(),
new Promise(resolve => setTimeout(resolve, 8000))
]);
// 3. Đóng database connection pool
await db.pool.end();
console.log('Database connections closed.');
// 4. Exit sạch
process.exit(0);
});
server.listen(8080);💡 Liên kết Linux Phase 1
Cơ chế signal (SIGTERM, SIGKILL, SIGHUP) được giải thích chi tiết trong bài Process Management & Signals. Docker container thực chất là Linux process — nên mọi kiến thức về signal trong Linux đều áp dụng trực tiếp cho container.
Điểm khác biệt duy nhất: PID 1 trong container không có signal handler mặc định của init system. Nếu app chạy với PID 1 mà không handle SIGTERM → signal bị bỏ qua (không phải default behavior "terminate"). Đây là lý do nhiều production container dùng tini hoặc dumb-init làm PID 1 wrapper.
dockerfile
# ✅ Dùng tini làm init process — forward signal đúng cách
FROM node:20-alpine
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "server.js"]So sánh docker stop vs docker kill
| Hành vi | docker stop | docker kill |
|---|---|---|
| Signal gửi đầu tiên | SIGTERM (15) | SIGKILL (9) mặc định |
| Grace period | 10s (tuỳ chỉnh -t) | Không có |
| App có cơ hội cleanup? | ✅ Có | ❌ Không |
| Exit code | 143 (SIGTERM) hoặc 0 | 137 (SIGKILL) |
| Khi nào dùng? | Mặc định — luôn dùng | Chỉ khi container bị treo, không phản hồi SIGTERM |
🏎️ Performance: docker stats và Resource Limits
💡 Giám sát resource real-time với docker stats
bash
# Real-time resource monitoring — giống top nhưng cho container
docker stats
# CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
# payment-api 2.3% 256MiB / 2GiB 12.5% 1.2GB / 890MB 45MB / 12MB
# mydb 1.1% 512MiB / 1GiB 50.0% 230MB / 180MB 1.2GB / 890MB
# redis_cache 0.1% 28MiB / 256MiB 10.9% 45MB / 38MB 0B / 0B
# Snapshot một lần (không stream)
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"Luôn set resource limits khi chạy container trong production — ngăn một container "ăn hết" tài nguyên server:
bash
# Giới hạn 512MB RAM và 1.5 CPU cores
docker run -d --name myapp \
--memory 512m \
--cpus 1.5 \
--restart unless-stopped \
myapp:latest
# Nếu container vượt memory limit → OOM killer → exit code 137
# Nếu container vượt CPU limit → bị throttle (chậm lại, không bị kill)📋 Tổng Hợp: Cheatsheet Docker Container Lifecycle
bash
# ─── KHỞI TẠO ───
docker run -d --name app -p 8080:80 nginx # Chạy nền
docker run --rm -it alpine sh # Interactive + tự dọn
docker run -d -e KEY=val -v data:/app app:v1 # Với env + volume
# ─── GIÁM SÁT ───
docker ps # Container đang chạy
docker ps -a # Tất cả container
docker stats # Resource real-time
docker logs --tail 100 -f app # Log gần + follow
# ─── DEBUG ───
docker exec -it app sh # Shell vào container
docker exec app cat /etc/config.yml # Đọc file config
docker logs --since 5m app 2>&1 | grep error # Lọc lỗi
# ─── DỌN DẸP ───
docker stop app # SIGTERM → chờ → SIGKILL
docker rm app # Xóa container stopped
docker rm -f app # Force kill + xóa
docker container prune # Xóa mọi container stopped
docker system prune -a # Xóa TOÀN BỘ rác✅ Checklist Hoàn Thành
::: success ✅ Bạn đã nắm vững bài này khi:
- [ ] Vẽ được state machine của container (5 trạng thái) mà không nhìn tài liệu
- [ ] Chạy được container detached với port mapping, env vars, và volume
- [ ] Dùng
docker ps -a+docker logs --since+docker execđể debug container có vấn đề - [ ] Giải thích được tại sao
docker stopgửi SIGTERM trước SIGKILL - [ ] Biết khi nào dùng
--rmvà tại sao quên nó gây tích tụ container zombie - [ ] Đã thực hành ít nhất một lần: chạy nginx → xem logs → exec vào → stop → rm
:::
Quiz: Kiểm Tra Kiến Thức
❓ Câu 1: Exit code 137 nghĩa là gì?
A. Container exit bình thường B. Container bị SIGTERM và exit gracefully C. Container bị SIGKILL (128 + 9 = 137) D. Container gặp lỗi permission denied
Đáp án: C — 137 = 128 + signal number 9 (SIGKILL). Container bị kernel hủy cưỡng bức, thường do OOM killer hoặc docker kill.
❓ Câu 2: docker stop gửi signal nào trước?
A. SIGKILL rồi SIGTERM B. SIGTERM rồi chờ 10 giây rồi SIGKILL C. SIGHUP rồi SIGTERM D. SIGINT rồi SIGKILL
Đáp án: B — docker stop luôn gửi SIGTERM trước, chờ grace period (mặc định 10s), rồi mới SIGKILL nếu process chưa exit.
❓ Câu 3: Lệnh nào giúp bạn xem log 5 phút gần nhất?
A. docker logs --last 5m myappB. docker logs --since 5m myappC. docker logs --time 5m myappD. docker logs --recent 5m myapp
Đáp án: B — --since nhận relative time (5m, 1h, 30s) hoặc absolute timestamp.
❓ Câu 4: Tại sao KHÔNG nên dùng docker exec để sửa file trong production container?
A. Vì docker exec chạy chậm B. Vì thay đổi sẽ mất khi container restart, không có audit trail, gây config drift C. Vì docker exec không có quyền root D. Vì docker exec chỉ hoạt động trên Linux
Đáp án: B — Container là ephemeral (tạm thời). Mọi thay đổi bên trong phải đi qua image build pipeline để đảm bảo reproducibility và traceability.