DOCKER
#docker#dockerfile#optimization#devops

Как уменьшить размер Docker-образа: alpine, кеш и порядок инструкций

Практические приёмы уменьшения Docker-образов: alpine-образы, кеширование слоёв и правильный порядок инструкций Dockerfile.

Как использовать

  1. Скопируйте нужный фрагмент кода.
  2. Вставьте в свой проект и при необходимости измените под задачу.
  3. Проверьте зависимости и окружение (версии, переменные).

Практические приёмы уменьшения размера Docker-образа и ускорения сборки: выбор базового образа (Alpine vs Debian), работа кеша слоёв и порядок инструкций в Dockerfile. Ниже — строго по официальной документации Docker и оптимизации кеша.


Почему размер Docker-образа важен

Большой образ:

  • дольше качается при docker pull и деплое;
  • занимает больше места в registry и на нодах;
  • увеличивает поверхность атаки (лишние пакеты и зависимости).

В Best practices явно сказано: при выборе базового образа нужно стремиться к минимальному образу, который достаточен для приложения. Меньший образ — быстрее распространение, меньше уязвимостей.


Разница между Debian и Alpine

Debian-based (например ubuntu:22.04, debian:bookworm-slim) — полноценный дистрибутив, образы обычно сотни мегабайт.

Alpine Linux — минималистичный дистрибутив на musl libc и BusyBox. В документации Docker явно рекомендуется: Alpine image — «tightly controlled and small in size (currently under 6 MB), while still being a full Linux distribution».

Практический пример — один и тот же сервис на Node.js:

# Вариант на Debian — образ заметно больше
FROM node:22-bookworm-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]
# Вариант на Alpine — меньше размер
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
CMD ["node", "server.js"]

Проверка размера — в конце статьи команда docker image ls. Для многих стеков (Node, Nginx, Postgres) есть официальные образы с суффиксом -alpine; их стоит предпочитать, если совместимость (в т.ч. с glibc) не требуется.


Как работает кеш Docker-слоёв

При сборке Docker проходит инструкции Dockerfile по порядку. Для каждой инструкции проверяется: можно ли взять результат из кеша. Если инструкция или её зависимости изменились — кеш для этого слоя и всех следующих инвалидируется, слои пересобираются.

Из документации по кешу:

  • слой берётся из кеша, если инструкция и файлы, от которых она зависит, не менялись;
  • при изменении слоя пересобираются все последующие слои.

Отсюда правило: редко меняющиеся и тяжёлые шаги — в начало Dockerfile, часто меняющиеся (например код приложения) — в конец.


Неправильный порядок COPY (пример)

Если сначала скопировать весь контекст (COPY . .), то любое изменение любого файла в проекте инвалидирует кеш для этого слоя и для всех следующих. Зависимости будут ставиться заново при каждом изменении кода.

# syntax=docker/dockerfile:1
FROM node:22-alpine
WORKDIR /app

# Плохо: любой правка в проекте ломает кеш ниже
COPY . .

RUN npm ci --omit=dev
RUN npm run build

CMD ["node", "dist/server.js"]

Итог: правка в src/ или в любом файле приводит к повторному npm ci и npm run build, хотя зависимости не менялись. Сборка долгая и образ неоптимально использует кеш.


Правильный порядок COPY (пример)

Сначала копируем только файлы, от которых зависят установка зависимостей и сборка (например package.json, package-lock.json), затем ставим зависимости, и только потом копируем остальной код. Так слой с npm ci кешируется, пока не меняются lock-файлы.

Рекомендация из Optimize cache usage: «First, copy over the package management files. Then, install the dependencies. Finally, copy over the project source code.»

# syntax=docker/dockerfile:1
FROM node:22-alpine
WORKDIR /app

# Сначала только файлы зависимостей
COPY package.json package-lock.json ./

RUN npm ci --omit=dev

# Потом остальной код — меняется часто, в конце
COPY . .

RUN npm run build

CMD ["node", "dist/server.js"]

Теперь при изменении только исходников пересобираются слои начиная с COPY . ., а слой с npm ci берётся из кеша. Сборка быстрее.

Тот же принцип для Python (сначала requirements.txt, потом pip install, потом код) или для любого менеджера пакетов.


Проверка размера образа командой docker image ls

После сборки посмотреть размер образа:

docker image ls

Вывод показывает образы и их размер (колонка SIZE). Для одного и того же тега можно сравнить размер до и после оптимизации.

# Собрать образ с тегом
docker build -t myapp:latest .

# Посмотреть размер
docker image ls myapp:latest

Дополнительно можно посмотреть историю слоёв:

docker history myapp:latest

Так видно, какие слои сколько занимают и где есть смысл уменьшать образ (меньше RUN, объединение команд, выбор более лёгкого базового образа).


Кратко

  1. Размер образа — влияет на скорость pull, место на диске и безопасность.
  2. Alpine — при возможности используйте -alpine варианты официальных образов для уменьшения размера (по документации).
  3. Кеш слоёв — при изменении инструкции или её зависимостей пересобираются этот слой и все следующие.
  4. Порядок в Dockerfile — сначала копировать только файлы зависимостей и ставить зависимости, затем копировать код; так кеш для установки сохраняется при правках кода.
  5. Проверка размераdocker image ls и при необходимости docker history.

Ссылки на официальную документацию: