
Bài 5: Tối ưu Docker image bằng multi-stage và distroless
Tách builder khỏi runtime, kiểm tra tương thích libc và native module; giảm thành phần không cần thiết mà không cam kết tỷ lệ dung lượng chưa đo.

Phân biệt metrics daemon, tài nguyên container và trạng thái ứng dụng; thiết kế scrape Prometheus, theo dõi OOM và bảo vệ công cụ đọc Docker socket.
Đối chiếu ngày 24/10/2026. Các nguồn ở cuối bài dùng để kiểm tra phiên bản và giới hạn. Ví dụ phải điều chỉnh theo hệ điều hành và ứng dụng; không phải báo cáo thử nghiệm trên mọi nền tảng.
Giám sát phải trả lời được dịch vụ có phục vụ người dùng hay không và có bằng chứng tìm nguyên nhân. Metrics daemon mô tả Docker; cAdvisor quan sát tài nguyên container; metrics ứng dụng và kiểm tra endpoint phản ánh lỗi, latency, tỷ lệ thành công. Các lớp bổ sung nhau: daemon online không chứng minh ứng dụng đáp ứng tốt.
Bắt đầu bằng lệnh chỉ đọc. Ghi trạng thái, thời điểm restart, OOM và log trước khi tác động. CPU cao có thể do tải hợp lệ; CPU thấp không chứng minh ứng dụng khỏe. Network I/O và block I/O là lượng tích lũy nên cần xem tốc độ thay đổi khi phân tích một khoảng thời gian.
docker stats --no-stream
docker ps -a
docker inspect --format '{{json .State}}' app
docker inspect --format '{{.RestartCount}}' app
docker logs --timestamps --tail 100 app

Hợp nhất metrics-addr vào daemon.json hiện có, không ghi đè các tùy chọn khác. Không cần sao chép experimental=true theo bài cũ chỉ để mở endpoint. Lưu cấu hình, validate rồi áp dụng trong cửa sổ bảo trì theo tài liệu daemon; restart có thể ảnh hưởng workload.
{
"metrics-addr": "127.0.0.1:9323"
}
sudo dockerd --validate --config-file=/etc/docker/daemon.json
# Sau khi ap dung cau hinh theo quy trinh:
curl --fail http://127.0.0.1:9323/metrics
Loopback 127.0.0.1 chỉ truy cập trong network namespace host. Prometheus chạy trong bridge container không thể scrape nó qua 172.17.0.1 chỉ vì đó là IP host. Có thể dùng host networking cho Prometheus trên Linux, hoặc thiết kế endpoint quản trị riêng có kiểm soát. Không bind 0.0.0.0 ra Internet để chữa lỗi scrape. Metrics daemon không cung cấp toàn bộ metrics ứng dụng và tên metric có thể thay theo phiên bản.
Upstream dùng image ghcr.io/google/cadvisor cho bản mới, thay gcr.io trong ví dụ cũ. Chọn release/digest và đọc hướng dẫn đúng với kernel, cgroup, rootless cùng vị trí dữ liệu host. Không giả định mọi máy đều dùng cùng đường /var/lib/docker hoặc một bộ mount.
Collector có thể cần host mounts, thiết bị và quyền cao để thấy đủ dữ liệu. Chỉ cấp sau đánh giá; không sao chép nguyên lệnh privileged vào mọi máy. Bind UI/metrics ở loopback hoặc mạng quản trị, xác minh xác thực và firewall. Kiểm tra các metric thực sự xuất hiện trước xây dashboard. Khi nâng exporter, so sánh label và số series để tránh tăng cardinality ngoài dự kiến.
Mẫu cAdvisor giả định Prometheus và cAdvisor cùng mạng có service DNS cadvisor. Job daemon loopback chỉ đúng khi Prometheus truy cập namespace mạng host; đây là hai tình huống kết nối riêng, không mặc định cùng một topology.
scrape_configs:
- job_name: cadvisor
static_configs:
- targets: ['cadvisor:8080']
# Chi bat khi Prometheus truy cap loopback cua HOST:
# - job_name: docker-daemon
# static_configs:
# - targets: ['127.0.0.1:9323']
Kiểm tra Targets và truy vấn up theo job. Dashboard Grafana phải khớp metric, label và phiên bản exporter, không chỉ import một ID rồi coi là hoàn tất. Giới hạn retention, dung lượng và quyền xem; kiểm tra mất scrape khác với giá trị tài nguyên bằng 0. Đưa cả tính sẵn sàng của chính hệ giám sát vào kế hoạch cảnh báo.
Chọn logging driver theo nhu cầu; Docker khuyến nghị cân nhắc local cho nhiều trường hợp thông thường. Đổi mặc định daemon không đổi container đã tồn tại, cần kế hoạch recreate và xác nhận driver từng container. Đừng xóa trực tiếp file log do daemon quản lý trong lúc đang ghi. Nếu cần lưu dài hạn, thiết kế pipeline thu log cùng retention và xử lý thông tin nhạy cảm.
Dozzle là giao diện xem log, không tự thay kho log bền vững. Cấu hình xác thực theo tài liệu, giới hạn truy cập và thử bằng tài khoản có quyền thấp trước publish. Mount docker.sock với :ro chỉ hạn chế ghi filesystem, không biến API qua socket thành API read-only. Quyền tới daemon rất mạnh; đánh giá cơ chế hạn quyền, không mở dashboard ra Internet mặc định.
Không dùng container_restart_count như counter cAdvisor mặc định khi chưa thấy trong exporter. Có thể thu RestartCount bằng collector riêng có tài liệu hoặc dùng sự kiện runtime. Nếu exporter có counter OOM, xét increase trong cửa sổ thời gian thay vì so tổng tích lũy lớn hơn 0 mãi mãi.
# Chi dung neu exporter thuc su cung cap metric va labels nay:
increase(container_oom_events_total[5m]) > 0
# Target khong scrape duoc; can khoang for phu hop:
up{job="cadvisor"} == 0
Đặt ngưỡng và thời gian chờ theo tải nền, mức ảnh hưởng và thời gian phục hồi chấp nhận được. Thử mất target, OOM trong lab và kênh thông báo; ghi runbook để người trực biết kiểm tra giới hạn, log hay chuyển cấp. Không tăng RAM tự động chỉ vì có OOM: cần xét memory limit, peak tải và lỗi ứng dụng. Đây là kế hoạch kiểm chứng, không phải báo cáo đã thử trên server của bạn.

Đọc đúng tài liệu theo phiên bản. Địa chỉ, tên và đường dẫn trong ví dụ cần thay theo môi trường. Sơ đồ là khái niệm, không phải screenshot hay benchmark.
Lưu cấu hình và digest cũ, kiểm tra backup phục hồi được, xác nhận endpoint và log sau thay đổi. Rollback phải tính đến dữ liệu đã ghi mới; đổi image không tự phục hồi database.

Tách builder khỏi runtime, kiểm tra tương thích libc và native module; giảm thành phần không cần thiết mà không cam kết tỷ lệ dung lượng chưa đo.

Cài Docker Engine từ kho phù hợp, kiểm tra phiên bản và quyền truy cập; phân biệt lưu trữ containerd mới với overlay2 trên máy nâng cấp.

Tra trạng thái, đọc log, kiểm tra OOM và giới hạn tài nguyên; hiểu tác động của stop, restart và prune trước khi thao tác trên máy production.

Phân biệt tag và digest, save/load với export; chuẩn bị TLS, xác thực và quy trình garbage collection trước khi vận hành private registry.