Sổ tay kỹ thuật

Giảm dung lượng Docker image

how-to#docker#dockerfile· cập nhật 2026-11-06

Dùng khi image quá lớn, pull chậm hoặc nhiều CVE, cần chọn base slim/alpine/distroless, tách multi-stage và soi layer bằng dive, docker history.

Sửa trang này
Mục lục

Tóm tắt: Đo trước (docker image ls, docker history, dive), tìm layer to nhất, rồi xử lý theo thứ tự hiệu quả: (1) multi-stage để bỏ toolchain build, (2) đổi base sang -slim hoặc distroless, (3) dọn cache trong cùng RUN, (4) .dockerignore. Quy tắc viết Dockerfile chung xem Dockerfile – best practices.

Luồng hoạt động

flowchart LR A["Đo: image ls, history"] --> B["dive: layer nào to, file thừa"] B --> C{"Có toolchain build?"} C -->|Có| D["Multi-stage"] C -->|Không| E["Đổi base slim/distroless"] D --> E E --> F["Dọn cache trong cùng RUN"] F --> G["Đo lại, CI=true dive"]

Lệnh

Đo và soi layer

docker image ls app                                   # cột SIZE là dung lượng giải nén
docker history app:latest                             # kích thước từng layer, lệnh tạo ra nó
docker history --no-trunc --format '{{.Size}}\t{{.CreatedBy}}' app:latest | sort -h | tail
docker buildx imagetools inspect python:3.12-slim     # manifest, các platform có sẵn
 
# dive: duyệt từng layer, xem file thêm/sửa/xoá và "wasted space"
dive app:latest
CI=true dive app:latest                               # chế độ không tương tác, trả exit code

Trong dive: Tab chuyển giữa panel layer và cây file, Ctrl+U ẩn file không đổi để chỉ thấy file do layer đó thêm. Chỉ số Image efficiency thấp nghĩa là có file bị ghi đè hoặc xoá ở layer sau (vẫn chiếm chỗ ở layer trước).

.dive-ci
rules:
  lowestEfficiency: 0.95
  highestWastedBytes: 20MB
  highestUserWastedPercent: 0.10

So sánh base image

Base Đặc điểm Lưu ý
debian:bookworm, python:3.12 Đầy đủ tool build, vài trăm MB tới ~1 GB Chỉ dùng cho stage build
*-slim (Debian) glibc, apt, bỏ phần lớn tool build Lựa chọn an toàn nhất cho Python/Node
alpine Rất nhỏ (~vài MB), musl libc, apk Wheel Python glibc không dùng được, phải build lại; khác biệt DNS/locale của musl
gcr.io/distroless/static-debian12 Không shell, không package manager, có CA cert, tzdata, user nonroot Cho binary Go/Rust build tĩnh
gcr.io/distroless/base-debian12, cc-debian12 Thêm glibc (và libstdc++ cho cc) Binary link động
scratch Rỗng hoàn toàn Tự copy CA cert, /etc/passwd nếu cần

Distroless có tag :nonroot (chạy UID 65532) và :debug (có busybox shell) để debug tạm.

Multi-stage: Go → distroless

Dockerfile
# syntax=docker/dockerfile:1
FROM golang:1.23 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
    CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/app ./cmd/app
 
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]

Kết quả thường từ ~800 MB (golang) xuống còn cỡ dung lượng binary + ~2 MB.

Dọn trong cùng layer

# Sai: rm ở layer sau không làm image nhỏ đi
RUN apt-get update && apt-get install -y build-essential
RUN rm -rf /var/lib/apt/lists/*
 
# Đúng
RUN apt-get update \
 && apt-get install -y --no-install-recommends libpq5 \
 && rm -rf /var/lib/apt/lists/*
 
# pip / npm: không giữ cache trong image
RUN pip install --no-cache-dir -r requirements.txt
RUN npm ci --omit=dev && npm cache clean --force

Với Python cần compile extension: cài build-essential ở stage build, tạo venv, rồi chỉ copy /opt/venv sang stage -slim (ví dụ ở trang Dockerfile – best practices).

Lỗi thường gặp

Triệu chứng Nguyên nhân Cách xử lý
Đã rm file mà image không nhỏ đi File vẫn nằm ở layer trước Tạo và xoá trong cùng RUN, hoặc dùng multi-stage
exec /app: no such file or directory trên distroless/scratch Binary link động (CGO) nhưng image không có libc CGO_ENABLED=0, hoặc dùng distroless/base/cc
x509: certificate signed by unknown authority trên scratch Thiếu CA bundle Dùng distroless/static, hoặc COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
pip install trên alpine build rất lâu, lỗi gcc Wheel manylinux (glibc) không dùng được với musl Dùng python:*-slim
Không kubectl exec ... sh được vào distroless Image không có shell kubectl debug -it <pod> --image=busybox --target=<container> hoặc tag :debug
Build context gửi hàng trăm MB Thiếu .dockerignore (.git, node_modules) Thêm .dockerignore
Giờ trong log sai múi giờ trên scratch Không có tzdata Dùng distroless (có tzdata) hoặc Go import _ "time/tzdata"

Tham khảo