
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 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.
Đố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.
Image lưu các layer filesystem và metadata để tạo container. Storage backend quyết định cách host lưu và trình bày layer; không đồng nhất mọi image với overlay2. Engine 29 cài mới mặc định dùng containerd image store, trong khi host nâng cấp có thể giữ backend cũ.
alpine, debian-slim hoặc distroless.RUN, COPY thường tạo layer; các chỉ thị metadata có vai trò khác lưu lại sự thay đổi (Delta) so với tầng trước đó.Nhờ cấu trúc này, nếu bạn tải 10 container cùng sử dụng base image node:24-alpine, máy chủ chỉ cần lưu trữ duy nhất một bản copy của base image đó trên đĩa cứng.

Để đảm bảo tính tái lập và an toàn trong quá trình triển khai sản xuất, bạn không nên phụ thuộc vào tag :latest vì nội dung của nó có thể thay đổi bất kỳ lúc nào.
# Gan nhieu tag cho cung mot image ID
docker tag my-app:build-105 my-app:1.2.0
docker tag my-app:build-105 my-app:latest
docker tag my-app:build-105 registry.thanhnh.id.vn/corp/my-app:1.2.0
# Kiem tra ma hash sha256 duy nhat (Digest) cua image
docker images --digests my-app
Trong môi trường Kubernetes hoặc Production khắt khe, người ta thường kéo image chính xác theo chuỗi Digest (ví dụ: my-app@sha256:7131e31cd4f1...) để đảm bảo không một ai có thể tráo đổi mã nguồn độc hại vào tag.
Trong các môi trường biệt lập (Air-gapped) như máy chủ ngân hàng, quân đội hoặc datacenter không có kết nối ra Internet, bạn không thể dùng lệnh docker pull từ Docker Hub. Giải pháp là đóng gói image thành file .tar:
# 1. Tren may co Internet: Dong goi image va nen tar.gz
docker save my-app:1.2.0 | gzip > my-app-1.2.0.tar.gz
# Dong goi nhieu image dong thoi vao mot file duy nhat
docker save -o full-stack.tar redis:7-alpine postgres:16-alpine nginx:alpine
# 2. Copy file sang may chu dich va nap lai vao Docker daemon
docker load -i full-stack.tar
gunzip < my-app-1.2.0.tar.gz | docker load
Có hai cách trích xuất trạng thái của một container đang chạy:
docker commit: Lưu toàn bộ container thành một image mới, ghi thay đổi filesystem cùng cấu hình image phù hợp; không lưu trạng thái RAM hoặc mounted volume.docker export: Xuất toàn bộ hệ thống file phẳng (flatten filesystem) của container thành file tar, xóa sạch toàn bộ lịch sử commit trước đó.# Tao image tu container dang chay de luu lai trang thai debug
docker commit -m "Fix config hotfix" web_debug my-app:1.2.1-hotfix
Để lưu trữ và phân phối các image nội bộ doanh nghiệp mà không cần trả phí cho Docker Hub, bạn có thể tự triển khai dịch vụ Registry v2 chính thức của Docker chỉ bằng một câu lệnh container:
docker run -d \
--name local-registry \
--restart always \
-p 127.0.0.1:5000:5000 \
-v /data/registry:/var/lib/registry \
registry:3
Đẩy và kéo image từ kho lưu trữ nội bộ này:
# Gan tag tro toi dia chi registry
docker tag my-app:1.2.0 localhost:5000/my-app:1.2.0
# Day image len local registry
docker push localhost:5000/my-app:1.2.0
# Keo image ve
docker pull localhost:5000/my-app:1.2.0
Registry từ xa cần HTTPS và chuỗi chứng chỉ tin cậy. Không gửi Basic Auth qua HTTP. Tách lab localhost khỏi cấu hình production, kiểm tra /v2/, login, push và pull từ client trước chuyển tải.
Phạm vi lab: mẫu sau chỉ bind loopback và dùng htpasswd, chưa cấu hình TLS nên không phải mẫu mở ra LAN/Internet. Truy cập từ xa cần endpoint TLS tin cậy, xác thực và phân quyền; không dùng insecure-registries để né lỗi chứng chỉ.
# Tao thu muc auth va sinh file htpasswd
mkdir -p /opt/registry/auth
sudo apt install -y apache2-utils
htpasswd -B -c /opt/registry/auth/htpasswd admin_user
# Chay Registry voi co auth bat buoc
docker run -d \
--name secure-registry \
--restart always \
-p 127.0.0.1:5000:5000 \
-v /opt/registry/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
-v /data/registry:/var/lib/registry \
registry:3
Từ client, người dùng phải đăng nhập trước khi có thể push hoặc pull image:
docker login registry.example.com --username admin_user
# Nhap mat khau qua prompt; automation dung --password-stdin.
Khi bạn xóa tag trên Private Registry, các blob dữ liệu vật lý trên ổ đĩa vẫn không tự động bị xóa cho đến khi bạn chạy tiến trình thu gom rác (Garbage Collection):
docker exec secure-registry registry garbage-collect --dry-run /etc/distribution/config.yml
# Xac minh executable va duong dan config tren image dang dung.
Ở bài học tiếp theo, chúng ta sẽ bước vào phần quan trọng nhất trong việc đóng gói ứng dụng: Xây dựng Dockerfile chuẩn Production kết hợp công nghệ BuildKit hiện đại.
Trước GC: sao lưu storage, dừng ghi hoặc chuyển registry sang read-only theo phiên bản đang cài, chạy dry-run và rà manifest còn dùng. GC khi vẫn push có thể xóa blob cần thiết. Image registry v3 không có nghĩa giao thức HTTP API v3; kiểm tra migration khi nâng major, không chỉ đổi tag.
Commit và export không backup dữ liệu mounted volume. Digest xác định nội dung, không chứng minh artifact đáng tin; cần xác minh nguồn và scan. Với image đa nền tảng, kiểm tra cả manifest và platform.

Đọ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.

Chọn nơi lưu dữ liệu, kiểm tra quyền UID/GID và phục hồi thử backup; phân biệt dữ liệu tạm với database cần sao lưu nhất quán theo ứng dụng.

Chọn driver theo phạm vi kết nối, dùng DNS mạng tự tạo và kiểm tra published port; phân biệt mạng Docker Swarm với mô hình CNI của Kubernetes.