Обзор



При сборке образов Docker часто происходит неожиданное увеличение их объема. Большинство причин связаны с необоснованно созданными уровнями. В этой статье я поделюсь своим опытом оптимизации образов с помощью команды docker history, чтобы проверить "родословную" изображений и сделать их более легкими.

Используя эту команду, вы сможете точно определить, что делает изображение тяжелым, и создать более эффективные образы.


docker history: Проверка родословной изображения

docker history — это команда, которая показывает, как был создан конкретный образ и его состав. Вы можете видеть, какие уровни (layer) были созданы для каждой команды Dockerfile, а также время создания и размер каждого уровня.

Основное использование очень простое.

docker history [опции] <имя образа:тег>

Например, команда для проверки истории локального образа my-app:latest выглядит следующим образом.

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
  • CREATED BY: Команда Dockerfile, которая создала этот уровень

  • SIZE: Объем, занимаемый этим уровнем

Полезный совет: Просмотр всей команды с флагом --no-trunc

По умолчанию вывод команды docker history может быть обрезан, если команда в столбце CREATED BY слишком длинная. В этом случае использование --no-trunc позволяет увидеть полную команду без обрезки, что необходимо для точного анализа.

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

Почему важен анализ уровней: реальный опыт оптимизации



Образы Docker по сути создают один уровень для каждой строки команды в Dockerfile. А каждый уровень представляет собой "изменение" относительно предыдущего уровня.

Проблема в том, что ненужное разделение уровней может привести к резкому увеличению объема изображения.

Пример: Ловушки COPY и RUN chown

При написании Dockerfile часто копируют файлы проекта в контейнер (COPY), а затем меняют права на эти файлы для определенного пользователя (например, appuser) с помощью (RUN chown).

Неэффективный метод: 2 уровня

Сначала я написал 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

Здесь возникает серьезная проблема.

  1. Команда COPY . . добавила файлы размером 150MB в уровень 1.

  2. Команда RUN chown просто скопировала файлы из уровня 1 размером 150MB, изменив только информацию о правах, и сохранила в уровень 2.

Содержимое файлов одно и то же, но один из них хранится с правами root, а другой с правами appuser, в разных уровнях. В результате общий объем изображения составляет 300MB (150MB + 150MB).

Эффективный метод: 1 уровень

Эту проблему можно легко решить, используя флаг --chown для команды COPY.

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

После внесения таких изменений в Dockerfile снова проверьте с помощью docker history.

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

Создан только один уровень, а общий объем изображения уменьшился до 150MB. Просто объединив две команды в одну, я сократил объем изображения почти вдвое.


Заключение

docker history — это не просто способ просмотра истории создания изображения, а основной инструмент анализа для оптимизации изображений.

После изменения Dockerfile полезно привычно запускать docker history --no-trunc, чтобы убедиться, что уровни созданы так, как задумано, и нет ли ненужных уровней, занимающих место. Эта маленькая привычка поможет создавать легкие и эффективные изображения.