🐳 Начало «диеты» образов Docker: Разбираем родословную с помощью docker history

Бывает, что вы запускаете всего одно легкое приложение, но при проверке обнаруживаете, что размер образа превышает 1 ГБ. Возникает вопрос: «Что я такого добавил?» В такие моменты необходимо разобраться в «родословной» образа.

Диета слоев Docker

«Что же ты съел, чтобы так вырасти?»

Образ Docker подобен многослойной луковице. Команда docker history — это рентген, который позволяет снять каждый слой и найти «виновника» его разрастания.

Её базовое использование очень просто:

# Базовое использование
docker history [опции] <имя_образа:тег>

# Пример: проверка истории моего приложения
docker history my-app:latest

При выполнении команды отображается полная история образа, начиная с самого нового слоя и до базового образа, в обратном хронологическом порядке.

IMAGE          CREATED          CREATED BY                                      SIZE
a6215f271958   5 minutes ago    /bin/sh -c #(nop)  CMD ["/bin/sh"]               0B
<missing>      7 weeks ago      /bin/sh -c #(nop) ADD file:f28242cf608f6...     7.81MB

Внимательно посмотрите на столбец SIZE. Если там, где должно быть 0B, указаны сотни мегабайт, значит, это ваш кандидат на «диету». С опытом вы быстро научитесь чувствовать, где «пахнет неладным».

**💡 Полезный совет: --no-trunc** `

По умолчанию вывод команды обрезается, если она слишком длинная. Добавление опции --no-trunc позволяет увидеть команду целиком без обрезки, что крайне важно для точного анализа.

docker history --no-trunc my-app:latest


Практический пример: «Виновником оказался chown»

Позвольте рассказать реальную историю из моего опыта. Я собирал проект размером 150 МБ, но образ в итоге весил более 300 МБ. Виновником оказались всего две, казалось бы, безобидные строки в Dockerfile.

[Неэффективный метод: создание двух слоев]

# 1. Сначала копируем файлы (по умолчанию копируются с правами root)
COPY . .

# 2. Изменяем владельца для безопасности (выполняется отдельной командой)
RUN chown -R appuser:appgroup /app

После сборки и проверки с помощью docker history я получил следующий результат:

IMAGE          CREATED BY                                        SIZE
<layer_id_2>   /bin/sh -c chown -R appuser:appgroup /app         150MB  <-- (Проблемное место!)
<layer_id_1>   /bin/sh -c #(nop) COPY dir:abc in /app            150MB

Здесь проявляется коварство слоев Docker. На первом шаге, когда копируется файл размером 150 МБ, создается один слой. Но когда на втором шаге выполняется chown, Docker «говорит»: «Ой, информация о файле (владелец) изменилась? Тогда нужно создать еще один слой с новым состоянием!» — и копирует те же 150 МБ, создавая новый слой поверх существующего.

В итоге, одно и то же содержимое, но с разными правами владения, было сохранено дважды, что удвоило размер образа.


Решение: «диета» в одну строку!

Решение простое: достаточно указать права владения сразу при копировании, объединив таким образом создание слоев в один.

[Эффективный метод: создание одного слоя]

# Копирование и одновременное указание прав владения!
COPY --chown=appuser:appgroup . .

Если изменить Dockerfile таким образом и снова выполнить docker history, результат будет впечатляющим.

IMAGE          CREATED BY                                               SIZE
<layer_id_1>   /bin/sh -c #(nop) COPY --chown=appuser... dir:abc       150MB

Видите? Создан всего один слой, и размер образа снова стал «стройным» — 150 МБ. Просто объединив две команды в одну, мы сократили размер вдвое.


Вывод: Всегда проверяйте «историю» после сборки!

Не забывайте: образ Docker — это один слой на каждую команду.

Как вы тестируете код после написания, так и после сборки образа возьмите за привычку запускать docker history --no-trunc. Вопросы вроде «Почему этот apt-get так много весит?» или «Зачем этот слой был разделен?» будут нанизываться один за другим, и незаметно для себя вы построите очень легкую и эффективную систему.

Ведь ненужные слои — враги вашей системы!


Было полезно? Если да, поставьте лайк!

Читайте также связанные статьи.

Связанные статьи

Полное руководство по общей памяти Docker: shm_size и ipc

Docker: Связь между контейнерами через порт хоста без общей сети

Копирование Docker Volume: ‘Почему’ это необходимо?