어떤 패키지가 Docker 이미지의 용량을 늘리고 있나요?

발행: (2026년 5월 25일 PM 08:24 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

위 링크에 포함된 글의 본문을 제공해 주시면, 해당 내용을 한국어로 번역해 드리겠습니다.

layer‑blame: 이미지 레이어에 대한 git blame

layer‑blame은 Docker 이미지의 모든 바이트를 해당 바이트를 도입한 패키지에 할당합니다.

예시: Alpine

docker save alpine:3.20 -o alpine.tar
layer-blame alpine.tar
Image total: 8.4 MB across 1 layers  ·  package attribution: 100%

Layer 0  8.4 MB  ADD alpine-minirootfs-3.20.10-aarch64.tar.gz /
      4.8 MB  libcrypto3                      pkg   ← largest line highlighted
    911.7 KB  libssl3                         pkg
    906.0 KB  busybox                         pkg
    706.5 KB  musl                            pkg
    327.1 KB  apk-tools                       pkg

이제 레이어의 모든 바이트에 소유자가 지정됩니다; libcrypto3가 8.4 MB 중 4.8 MB를 차지합니다.

다른 도구와의 비교

도구표시 내용
docker history레이어 크기만 표시하고 내용은 표시하지 않음
dive레이어를 파일 단위로 인터랙티브하게 탐색
layer‑blame모든 바이트에 대한 패키지 수준 할당을 제공 (비인터랙티브 보고서)

dive는 탐색에 좋고, layer‑blame은 할당에 좋습니다. 두 도구를 함께 사용하세요.

예시: python:3.12‑slim

docker save python:3.12-slim -o py.tar
layer-blame --top 5 py.tar
Image total: 137.7 MB across 4 layers  ·  package attribution: 69%

Layer 0  95.8 MB  # debian.sh --arch 'arm64' out/ 'trixie' ...
     22.5 MB  libc6                           pkg
      9.1 MB  coreutils                       pkg
      7.4 MB  perl-base                       pkg
      7.3 MB  libssl3t64                      pkg
      6.6 MB  util-linux                      pkg

Layer 2  38.2 MB  RUN /bin/sh -c set -eux; savedAptMark="$(apt-mark showmanual)"; …
      6.3 MB  /usr/local/lib/libpython3.12.so.1.0                          file
      1.8 MB  /usr/local/lib/python3.12/ensurepip/_bundled/pip-...whl       file
      1.1 MB  /usr/local/lib/python3.12/lib-dynload/unicodedata...so        file

Layer 2에서는 가장 큰 기여도가 파일로 보고됩니다. 이는 이미지가 소스에서 Python을 빌드했기 때문에 해당 바이트가 어떤 dpkg 패키지에도 속하지 않기 때문입니다. “package attribution: 69%” 헤더는 이미지의 69 %가 패키지에 깔끔하게 매핑된다는 것을 알려 주며, 나머지는 더 자세히 살펴볼 필요가 있음을 의미합니다.

사용법

docker save -o image.tar
layer-blame [flags] image.tar

Docker 데몬이 필요 없습니다layer‑blame은 저장된 tarball(또는 일반 OCI 레이아웃)을 직접 파싱하므로 CI에 친화적입니다.

플래그

플래그기본값설명
--top N5각 레이어별 상위 N명의 기여자를 표시합니다
--no-colorfalseANSI 색상을 비활성화합니다 (NO_COLOR와 비‑TTY 출력도 존중)
--versionfalse버전, 커밋, 빌드 날짜를 출력합니다

작동 방식 (다섯 단계의 결정적 절차)

  1. Load the tarball / OCI layout via go-containerregistry, normalizing Docker‑save, BuildKit, and containerd/OCI formats.
    go-containerregistry를 통해 tarball / OCI 레이아웃을 로드하고, Docker‑save, BuildKit, containerd/OCI 포맷을 정규화합니다.

  2. Walk each layer’s filesystem diff, recording every added file and its size. Whiteout (deletion) markers are ignored.
    → 각 레이어의 파일시스템 차이를 순회하면서 추가된 모든 파일과 그 크기를 기록합니다. Whiteout(삭제) 마커는 무시됩니다.

  3. Build a file→package index from the image’s own package databases:
    → 이미지 자체의 패키지 데이터베이스에서 파일→패키지 인덱스를 구축합니다:

    • Alpine – /lib/apk/db/installed
    • Debian/Ubuntu – /var/lib/dpkg/info/*.list
  4. Group added bytes by owning package. Files with no owner are reported individually, ensuring large unattributed artifacts surface by name.
    → 소유 패키지별로 추가된 바이트를 그룹화합니다. 소유자가 없는 파일은 개별적으로 보고되어, 이름으로 큰 무소속 아티팩트가 드러나도록 합니다.

  5. Map each layer back to the Dockerfile instruction (created_by from the image config) and print the table.
    → 이미지 설정의 created_by 정보를 사용해 각 레이어를 Dockerfile 명령으로 매핑하고 표를 출력합니다.

The novel part is step 4 – the JOIN between added bytes and the package database. Everything else is plumbing.
→ 새로운 부분은 4단계, 즉 추가된 바이트와 패키지 데이터베이스 간의 JOIN입니다. 나머지는 모두 파이프라인에 해당합니다.

제한 사항 및 주의점

  • Scratch / distroless 이미지는 패키지 데이터베이스가 없으므로, 귀속이 파일 수준으로 되돌아갑니다.
  • 멀티‑스테이지 COPY --from 파일은 원본 패키지 정보를 잃어버리며, 대상 레이어에서 귀속되지 않은 것으로 표시됩니다.
  • 현재는 apk(Alpine)와 dpkg(Debian/Ubuntu)만 지원합니다. RPM 및 언어‑별 관리자(npm, pip 등)는 아직 구현되지 않았습니다.
  • 단일 Go 바이너리이며, MIT‑licensed.

설치

사전 빌드 바이너리 (Linux/macOS/Windows, amd64 + arm64)

curl -sSfL https://github.com/mk668a/layer-blame/releases/latest/download/layer-blame__linux_amd64.tar.gz \
  | tar -xz layer-blame

Go로 빌드

go install github.com/mk668a/layer-blame@latest

저장소:

언제 사용할까

다음에 PR이 이미지 크기를 부풀리고 리뷰어가 “왜 이게 800 MB인가요?”라고 물으면, 한 번의 명령만 실행해 문제를 일으키는 바이트에 연결된 패키지 이름을 확인하세요—dive로 수동으로 파고들며 보낸 오후를 절약할 수 있습니다. 특이한 이미지에서 예상치 못한 귀속을 발견하면 이슈를 열어 주세요; 가장 흥미로운 경우는 (RPM, 단계 간 COPY 등) 가장자리 케이스입니다.

0 조회
Back to Blog

관련 글

더 보기 »

Kubernetes 도구

kubectl: 모든 작업을 kubectl으로 실행합니다: get pods, describe, logs, exec, delete, apply—여러 네임스페이스에서 하루에 수십 번씩 실행합니다. 작동은 하지만…